GET /sdk/v1/campaigns/:campaignId/media/sync
Use this endpoint when you keep a local copy of campaign media. The first run downloads the current records in pages. Later runs return records affected by changes and IDs to remove. Eachdata item is a complete replacement record
with the same fields as the campaign media list.
Requests require X-Application-Id, X-Api-Key, and the campaigns:read scope.
The existing /media endpoint continues to return its complete list.
One cursor for pages and future polls
- Start without a cursor and with an empty local collection for this campaign.
- Upsert every
dataitem bycampaignCreatorMediaId; delete everyremovedIdsentry. - Save
nextCursorin the same local transaction as those changes. - If
hasMoreistrue, request the next page usingcursor=nextCursor. - Otherwise, keep the cursor and use it the next time you poll.
hasMore is
true even if data is empty: a page can contain only removals.
Cursors have no time-based expiry. You can pause polling, restart your service,
rotate an API key, or change the page size and continue with the saved cursor.
There is no routine full reload after a retention window.
Query parameters
Filters are saved in the cursor, so you can omit them on subsequent calls. Use a
separate local collection and a new cursor when starting a different filtered
view. A record that leaves a filtered view appears in
removedIds.
Pages also target 1 MiB of record data. A single larger record is returned intact.
The number of returned records can therefore be lower than limit.
Example request
cursor argument on the first request.
Response
removedIds covers deletion, tracking retirement, movement to another campaign,
creator removal, and records that no longer match your filters. Deleting a local
ID you do not have is harmless. Restored records return as complete upserts.
The sync also captures metric changes, creator names and email addresses, agency
display names, metadata, and account totals affected by child posts. It sends the
latest available state, so multiple changes to one record can become one upsert.
It is not an audit history of every intermediate edit.
Changes during paging and retries
Pages read current records. Changes during a run can appear in a later page or on the next poll. The cursor preserves changes committed after the run began, including transactions that were already in progress. A run finishes after its bounded set of IDs; ongoing writes do not require a frozen database transaction. Delivery is at least once. A record may repeat during retries, concurrent edits, or catch-up. Apply complete replacements and removals idempotently. Serialize requests and page application for each local copy so an older response cannot overwrite a newer one. Independent integrations may maintain independent cursors. If a request or local transaction fails, retain your data and retry the last saved cursor. Never clear your local collection on an API error. Only start without a cursor when intentionally creating a new local copy. Campaign-widemetrics and asOfTimestamp are not included. For totals from your
local copy, avoid counting both an account’s aggregate and its child posts.
Client example
The repository demo exportsmediaSync from sdk-demo/backend/src/sdkClient.js.
It follows pages sequentially and stops on an API or storage error. Supply your
own transaction implementation:
store methods above represent your database adapter. The API client and
page loop are provided; your adapter must make the page and cursor atomic.
Errors
400: malformed parameters, a damaged cursor, a cursor for another campaign or agency, or filters different from the cursor’s saved filters.401or403: invalid credentials or insufficient scope. Fix authorization and retain your saved cursor.404: the campaign is unavailable to the authenticated agency.429or5xx: followRetry-Afterwhen present; otherwise retry with backoff using the same cursor.