OpenAI Audiences
Sync audience changes from Singular or another supported source to OpenAI Ads. Extract uploads CSV files containing additions and removals, resolving the audience by name on each run.
Setup
- In OpenAI Ads Manager, open Settings → API Keys and generate a key for the intended ad account.
- Enter it in OpenAI Ads API Key (
OPENAI_ADS_API_KEY). Each destination credential belongs to one ad account. - Enter an Audience Name with at least three characters. Extract looks up an active audience with that exact name on each run and reuses it. If none exists, Extract creates it. The name stays configured across runs. If multiple active audiences share the name, rename them in OpenAI Ads Manager so the match is unique.
- Map at least one identifier field using the Custom audience contract, and use Append loading.
- Supply the required diff_result action using either:
- Source-provided actions: map a string column containing
Added,Changed, orRemoved. S3 Partition extraction is supported for files containing these actions. A staticAddedmapping supports add-only files; removals require explicitRemovedrecords. - Extract-generated actions: use Full Refresh extraction, configure a primary key, and enable Perform Diffing. Extract supplies
diff_resultautomatically.
- Source-provided actions: map a string column containing
- Configure the desired run schedule.
Use one source stream to manage each OpenAI audience. An audience ID is bound to its ad account; changing to another account requires a new destination stream.
How diffing works
With Perform Diffing enabled, Extract reads the full source to detect deletions. With source-provided actions, Extract follows each record’s mapped action:
AddedandChangedrecords are sent to/add, matching Google Ads and Liftoff audiences.Removedrecords are sent to/remove.- Unchanged records are not sent.
With Extract-generated diffing, the first run adds all current source records. Source-provided action files must include the initial membership and explicit removals; missing rows in a later file are not interpreted as deletions. Existing remote members outside the source are not cleared. Later manual OpenAI changes are not automatically reconciled unless the corresponding source record changes.
The differ supplies only the new values for a Changed record. Include membership identifiers in your source primary key if identifier changes must remove the old identity and add the new one. Use one row per membership identity; duplicate identities across source keys can cause a deletion to remove a member still represented by another row.
Changing the audience name or identifier mappings does not reset the source diff baseline. When Extract generates the diff, use a new connection or reset the source baseline before populating a new audience; unchanged records otherwise will not be resent. With source-provided actions, replay the needed action files when changing audiences. Wait for active runs to finish before making these changes. Remapping identifiers does not remove identifiers previously uploaded under the old mapping; select a fresh audience when those old memberships must be cleared. Identifier source fields must be strings; format numeric phone data upstream with an explicit country code.
Hashing and normalization
| Field | What Extract does |
|---|---|
email | Trims whitespace and lowercases the address. Keeps dots and plus tags. |
phone_number | Converts international phone formatting to E.164, including the leading + and country code. Accepts + or 00 international prefixes. |
email_sha256 | Validates an existing SHA-256 digest or normalizes and hashes a raw email when Apply hashing is enabled. |
phone_number_sha256 | Validates an existing SHA-256 digest or normalizes and hashes a raw international phone when Apply hashing is enabled. |
gaid | Converts a raw, nonzero UUID to lowercase hyphenated format. OpenAI hashes it internally. |
For example, First.Last+sale@Example.COM becomes first.last+sale@example.com, and +1 (202) 555-0123 becomes +12025550123. Extract does not infer missing country codes or accept phone extensions.
When hashing upstream, normalize first and hash the UTF-8 value without a trailing newline. Select Source values are already hashed for digest inputs. Extract lowercases hexadecimal digests and converts Base64 encodings of exactly 32 bytes to the required 64-character hexadecimal form. It cannot determine whether the original value was normalized correctly before hashing. GAIDs must remain unhashed.
Data contract
All fields are optional individually and must be strings. Every row must have at least one populated identifier. Missing, null, or whitespace-only values produce empty CSV cells. Mixed identifier types are supported in the same row and file.
Every populated identifier must be valid. Extract validates each batch before upload. If a value cannot be corrected, it stops and reports the batch row and field without including the identifier; earlier batches may already have completed. It also rejects files exceeding 500,000,000 bytes. A completed diff with no changes succeeds without sending membership requests. If all source records are deleted, their removal records are sent to OpenAI.
The connector reads one source record at a time and writes CSV files to disk, flushing each buffer at 1 MiB. It retains only the current batch’s files. Uploads stream from disk. Mapped identifier inputs are limited to 4,096 bytes before normalization; API responses are capped at 1 MiB, and audience/operation lists at 4 MiB of JSON or 10,000 entries/pages. Oversized inputs fail explicitly instead of growing buffers indefinitely. These limits cover connector-owned buffers; upstream source and message-pipe allocations are separate.
Only identifiers listed above are sent. Each field can be mapped once. A row is accepted only after all of its populated fields pass validation.
Sync completion and recovery
Extract waits for each add/remove operation to succeed before marking the sync successful. Each batch contains at most 100,000 source records, split into remove/add CSVs. Removals finish before additions within the batch, and each batch completes before the next starts. After active operations have completed successfully, a failed local run can replay its diff, so a previously completed removal or addition may be sent again. Adding an existing member and removing an absent member are safe. Audience readiness alone does not indicate that an operation has finished. Delivered row counts are local input counts, not matched-user counts or targeting eligibility.
Temporary failures and rate limits are retried. Request retries reuse the same idempotency key and body. Before sending a delta, Extract waits for active remote operations from previous attempts. After 30 minutes, the run fails; retry the connection to replay the diff. Interrupted operations reporting custom_audience_operation_recovery_required are resumed by operation ID. An audience with a failed operation blocks new changes: reconcile with OpenAI support, or use a fresh audience with a new connection or a reset source baseline. There is no separate destination recovery journal. Contact support with the operation ID, without including customer identifiers.
If audience creation has an ambiguous outcome, check OpenAI Ads Manager and select the existing audience explicitly. Do not repeatedly create audiences with the same name.
This integration manages membership only. Configure campaign targeting separately in OpenAI Ads Manager.
See the OpenAI Custom Audiences API documentation.
Rate limits
OpenAI documents 600 requests per minute per endpoint and 1,200 overall, enforced by both ad account and IP address. The connector sends sequential file-based changes, not one API call per record, and backs off on 429 while retaining the original mutation key. Polling uses increasing delays. Other streams and services using the same account or IP share those limits.