Before you start
- A key with the
writescope. A partner key also needs aread_writelink to the organisation. See Authentication. - An own brand that is not archived, with at least one active prompt. Competitors cannot be scanned on their own.
- Scan allowance. An accepted request uses one manual scan from the organisation’s monthly allowance. When the allowance is used up, it uses one purchased scan credit instead. Plans with unlimited manual scans skip the check. Manual scans & allowances lists the allowance for each plan. This includes a brand’s first scan: the free baseline scan described there is a dashboard feature and does not apply to scans requested through the API.
SURFAIS_ORG_ID and SURFAIS_BRAND_ID. The longer scripts take them as arguments.
Request the scan
CallPOST /orgs/{orgId}/brands/{brandId}/scan-requests. It takes no request body.
202 means the scan is queued. It has not run yet.
Response
One scan in flight per brand
A brand can have one scan in flight per UTC day. While a scan that covers the brand is queued or running, another request that day answers409 scan_already_queued. It does not matter who started the first scan: you, someone in the dashboard, or the schedule.
Response
409 as success. The scan you wanted is already on its way, and the refused request used no allowance. details.job_id names the scan that covers the brand. It can be null, so do not depend on it.
One exception: a scan reads the brand’s active prompts once, when it starts. If you changed the prompts after the scan in flight began, it does not include the change. Wait for it to finish, as described below, and request again.
The rule covers scans that are still queued or running. Once the scan has finished, a new request on the same day is accepted: it queues another scan, uses another manual scan, and its results replace the earlier ones for that date.
There are two different keys here.
idempotency_key in these responses belongs to the scan queue. The Idempotency-Key request header is yours: like every write, this endpoint accepts it, and a retry with the same value replays your first response. See Idempotent requests.Refusals
A refused request queues nothing and uses no allowance.
The
402 carries the organisation’s balance:
Response
401 invalid_api_key, 429 rate_limited and the 503 family.
Know when the scan has finished
A scan takes minutes, not seconds. What happens in a scan gives the typical duration. There are two ways to learn that it is done.Poll the brand
GET /orgs/{orgId}/brands/{brandId} carries two fields that move while a scan runs. Data model defines both.
last_scan_atmoves when the scan’s first result is finalised, and again with each later one. When it differs from the value you read before your request, results are arriving. It does not tell you that the scan is complete.last_scan_changed_atmoves with every one of those results, and again when the scan’s scores are published. Scores are published once every result is in.
last_scan_at has moved since your request and last_scan_changed_at has then stayed unchanged for several minutes. This is a signal, not a status flag: after the last result there is a quiet stage, which can last minutes, before the scores are published, and a short quiet period mistakes it for the end. The scripts below wait for ten quiet minutes, then stop. They also give up after a timeout, because a scan can wait in the queue before it starts.
For the first scan of a date there is a definite check as well: once the scores are published, latest.date on GET /orgs/{orgId}/brands/summary is the scan date. For a second scan of a date that already has scores, polling has no definite signal, so allow a longer quiet period before you read the numbers.
scan.completed.
Subscribe to scan.completed
Platform partners can receive a signed scan.completed webhook instead of polling. It is sent after the scan’s scores are published, and it carries the brand’s new AIS Score. A scan you request through the API triggers it like any other scan. See Webhooks and the event reference.
Read the numbers
Once the scan has finished, these three reads give you the new state.Response
score is the headline AIS Score. An empty data list for the scan’s date means the scores are not published yet, so wait and read again. Before you compare this point with an earlier one, check that score_version and panel_version match. See Data model.
Share of voice returns the own brand first, then its competitors:
Response