The list envelope
A list response has two top-level keys:data, the items on this page, and pagination.
array
required
The items on this page. An empty array is a valid page.
string | null
required
The cursor for the next page.
null on the last page.boolean
required
true while another page exists. Stop when it is false.{ "data": { … } } with no pagination key.
limit and cursor
integer
default:50
Page size, from 1 to 200. The default is 50.
GET /orgs/{orgId}/brands/{brandId}/mentions is the bulk-export endpoint and accepts up to 1000.A value outside the range answers 400 validation_error.string
The
pagination.next_cursor value from the previous page. Leave it out to get the first page.- A cursor is opaque. Copy
next_cursorinto the next request exactly as you received it. Do not decode it, edit it or build one yourself. Pass it like any other query value, URL-encoded by your HTTP client. - Stop on
has_more: false.next_cursorisnullon that page. - Send
limiton every request. The cursor does not remember it. Withoutlimit, the next page falls back to the default size. You can changelimitbetween pages.
A complete loop
Each sample yields every prompt in an organisation fromGET /orgs/{orgId}/prompts. It waits out 429 and 503 responses using Retry-After, and gives up instead of sleeping when the wait is long. A spent monthly quota counts down to the end of the month, for example — see Rate limits and quotas.
What a cursor is bound to
A cursor is a position in one series. It remembers which series, so a walk cannot drift into a different one half-way through. A cursor that does not fit the request answers400 invalid_cursor.
The endpoint and the collection. A cursor belongs to the list that issued it and to the organisation, brand or prompt in that request’s path. A cursor from one organisation’s prompts does not work on another organisation’s prompts, or on any other list. On GET /orgs it belongs to the owner of your key. Two endpoints are stricter still:
- A cursor from
GET /orgs/{orgId}/prompts/{promptId}/resultsalso belongs to the own brand the prompt was under. It stops working if the prompt is moved to another own brand. GET /orgs/{orgId}/brands/{brandId}/scoresserves two series, one withcountryand one without. They issue distinct cursors, so addingcountryto a walk that started without it is refused.
details.
The filters of the first page. The filters an endpoint takes — platform, country, brand_id, active, tag, is_own or status — travel inside the cursor. Continue with the cursor alone and they are restored, so page two is still filtered the way page one was. Repeating the same filter value is fine, so you can send your whole query string again with the cursor added. Naming a different value, or adding a filter the walk did not start with, answers 400 invalid_cursor with details.reason set to filter_changed. details.parameter names the filter.
The date window of the first page. On an endpoint that takes from and to, the first page fixes the window and the cursor carries it. A walk that starts without from and to does not shift forward when it crosses midnight UTC. Naming a different from or to on a later page answers 400 invalid_cursor with details.reason set to window_changed.
To change a filter or the window, start again from the first page.
Reading results while a scan is running
Three endpoints read scan results:GET /orgs/{orgId}/prompts/{promptId}/resultsGET /orgs/{orgId}/brands/{brandId}/mentionsGET /orgs/{orgId}/brands/{brandId}/sources
400 invalid_cursorwithdetails.reasonset tostale. The brand’s data changed after the cursor was issued. A running scan is the usual cause, because every result it finalises counts as a change. Start again from the first page.409 scan_in_progresswithRetry-After. Results were being finalised while a first page was read, so it could not be served from a single scan generation. Expect it while a scan of the brand runs: from the sources endpoint at any page size, and from the results and mentions endpoints on a first page that takes more than one read: a large page, or one that reaches the end of the data. WaitRetry-Afterseconds, then send the same request again.
- Wait for the scan to settle. Poll the brand’s change token:
last_scan_changed_atonGET /orgs/{orgId}/brands/{brandId}, orchanged_aton each row ofGET /orgs/{orgId}/brands/summary. Start a bulk walk once the value has stopped moving. - Use smaller pages while a scan runs. A smaller first page is less likely to answer
409 scan_in_progress, though any first page can. - Make the walk restartable. On
stale, discard what you collected in this walk and begin again from the first page. Do not try to resume from the old cursor.
Lists that do not read scan results never answer
stale or scan_in_progress. That covers organisations, brands, prompts, scores and the webhook lists.Date windows
Five endpoints report on a period and take afrom and to window:
GET /orgs/{orgId}/prompts/{promptId}/resultsGET /orgs/{orgId}/brands/{brandId}/mentionsGET /orgs/{orgId}/brands/{brandId}/scoresGET /orgs/{orgId}/brands/{brandId}/share-of-voiceGET /orgs/{orgId}/brands/{brandId}/sources
string
First day of the window, inclusive. The default is 30 days before
to.string
Last day of the window, inclusive. The default is today, in UTC.
YYYY-MM-DD. The window includes both days.
to can be at most 366 days after from. On GET /orgs/{orgId}/brands/{brandId}/sources it can be at most 90 days after from, because that endpoint adds up every result in the window on each request.
400 validation_error. The reason is in details[].code:
A value that is not written as
YYYY-MM-DD also answers 400 validation_error.
On a paginated endpoint the first page fixes the window for the whole walk, as described in What a cursor is bound to. See Errors for the full error format.