Syncing your audiences
Send an existing ContactLevel audience to a connected destination, then check its progress. You can use the same connection ID to start the sync and check its status.
Before you start
You need:
- Python 3.10+, Node.js 22+, or cURL, depending on your chosen example.
- A ContactLevel API key with public API and audience sync access.
- An audience containing the contacts you want to sync.
- A destination connected and set up in ContactLevel, such as Facebook or Google Ads.
The API key, audience, and connection must belong to the same organization. The API gets your organization from the key; you do not send an organization ID.
1. Copy and paste the setup
Choose Python, JavaScript, or cURL using the tabs. Switching languages updates all examples on this page.
Check your tools. Python needs requests; JavaScript uses Node.js's built-in fetch.
For Python, paste the setup into sync_audiences.py. For JavaScript, use sync_audiences.mjs. For cURL, paste it directly into a Bash or zsh terminal.
Replace YOUR_API_KEY and YOUR_AUDIENCE_ID. Use your API key starting with api_ and the audience's UUID. Keep your filled-in setup private.
For Python and JavaScript, leave only the current step's call uncommented at the bottom of the file, then run the file again for each step. For cURL, paste each step's command into the same terminal.
Each example prints the HTTP status and response body.
2. Confirm your audience
If you need to find the audience ID, run:
This calls POST /api/audiences/search and lists the first 25 audiences. Copy the relevant audiences[].id into AUDIENCE_ID. You can change page in the helper to see more results.
Then run:
This calls GET /api/audiences/{audience_id}. Check the returned audience.name to confirm you selected the right audience.
If you have not created and populated an audience yet, do that in ContactLevel first. You can also use the API reference to create an audience and add existing contacts. Creating an empty audience does not start a sync.
3. Choose a destination connection
This calls POST /api/connections/search. A connection looks like this:
Code
Copy the connection_id for the destination you want. For cURL, replace YOUR_CONNECTION_ID in export CONNECTION_ID="YOUR_CONNECTION_ID" and run that assignment again. Choose a connection with ready: true. If it is false, blocking_reason explains what needs fixing in ContactLevel before you can sync.
These are example IDs; use the IDs from your own responses.
4. Start the sync
Replace YOUR_CONNECTION_ID with the ID from step 3:
This sends:
Code
Code
This single request creates or reuses the destination sync, enables it if necessary, and starts it. No configuration step or sync ID is required. The /api/audience-syncs/configure endpoint is deprecated and remains available only for backward compatibility.
The request waits up to 25 seconds for startup, then returns HTTP 202. Example:
Code
HTTP 202 does not mean the sync completed. Check each result's disposition:
| Disposition | What to do |
|---|---|
started | The run started or already finished. Check status next. |
already_running | A run already exists and is active. Check its status. |
pending | Startup is not confirmed yet. Check status before trying again. |
failed | Read error_message and resolve the problem before retrying. |
Explicitly selected destinations are automatically enabled, including previously disabled ones. No separate configure call is needed. The connection must already be set up and ready; unresolved destination deletions still block a run.
Omit connection_ids or send [] to run existing enabled destinations only. This does not enable disabled destinations or add new ones.
5. Check progress
Use the same connection ID:
This calls POST /api/audience-syncs/status with audience_id and connection_ids, and returns the latest sync for that connection in this audience.
Example response:
Code
Run the status check again after a few seconds. You do not need to call run_sync() again to refresh progress.
| Status | Meaning |
|---|---|
NOT_STARTED or not_ready | A sync exists but has not started or is not ready. |
PENDING, SCHEDULED, or DELAYED | The run is waiting. Check again later. |
IN_PROGRESS | The run is still working. |
COMPLETED | The run finished successfully. |
COMPLETED_WITH_ERRORS | The run finished with some errors. Review the counts and any error message. |
FAILED | The run failed. Read error_message before retrying. |
Counts describe the ContactLevel run, not the destination's matched audience size. Skipped records are reported separately from failed records. Before a run exists, status reports the saved startup state with zero counters.
Other ways to check status
Check a particular sync using sync_id from the run response. For cURL, first set SYNC_ID to that value:
Check every destination configured for this audience:
Use either connection_id or sync_id in the helper, not both. The API accepts their plural forms as lists: connection_ids or sync_ids. Omitting both lists, or sending both empty, returns all destinations.
Do not pass sync_run_id to a status filter. It identifies an individual execution; sync_id identifies the audience's sync configuration.
If a request fails
The examples print the response body when an HTTP request fails. Read its detail field. For HTTP 202, inspect results[].error_message instead.
| Result | Next step |
|---|---|
401 | Check your API key. |
402 | Check the access or billing requirement in the response. |
404 on a connection status check | Confirm the audience and connection IDs. A connection has no sync to check until one has been created for this audience. |
409 | Read detail; the connection may be unavailable, incomplete, or the destination may be awaiting deletion. |
422 | Use valid UUIDs without duplicates. For status, populate only one ID list. |
A timeout or pending startup | Check status before resubmitting; the run may still start. |
failed with an internal service authentication or scope error | Contact support with the sync ID and error message. |
See the API reference for the full request and response schemas.