PendiumDocs

Poll Scan Status

Check whether a visibility scan has finished and get progress updates.

GET /api/visibility/scan?syntheticId={id}

Authentication: Required. Polling is read-only, so it works even for accounts in read-only mode (only triggering a scan is refused).

After triggering a scan, use this endpoint to check whether it's done. Poll every 30–60 seconds until scanAvailable is true, then grab the full report.

Query parameters

ParameterTypeRequiredDescription
syntheticIdintegerYesThe brand agent ID returned when you triggered the scan.

Example

curl "https://pendium.ai/api/visibility/scan?syntheticId=1234" \
  -H "x-api-key: pk_live_xxxxxxxxxxxx"

Response — scan complete

When the scan is done, you'll get the top-level scores and a pointer to the full report:

{
  "syntheticId": 1234,
  "scanAvailable": true,
  "scanFailed": false,
  "overallScore": 62,
  "scanDate": "2026-03-17T14:30:00Z",
  "platformScores": {
    "chatgpt": 71,
    "claude": 58,
    "gemini": 55,
    "aio": 64
  },
  "reportUrl": "https://pendium.ai/1234/visibility",
  "_pendium": {
    "what_you_are_seeing": "Latest scan results for syntheticId=1234...",
    "next_steps": ["Get the full report: GET /api/visibility/report?syntheticId=1234"]
  }
}

This returns the latest completed scan and its scanFailed flag — it is not scoped to a specific run id. If the agent already had a completed scan and you just triggered a rescan, polling can return that previous scan with scanAvailable: true until the new run finishes. Don't treat the first scanAvailable: true after a rescan as your result — poll until scanDate is newer than when you triggered (see Polling strategy).

Response — scan reached no engines (transient failure)

Occasionally a run can't get a usable response from any AI engine (a temporary provider/network hiccup). The scan still completes and is returned with scanAvailable: true, but scanFailed is true and overallScore is 0. Treat the score as not meaningful — trigger a new scan rather than ingesting the zero:

{
  "syntheticId": 1234,
  "scanAvailable": true,
  "scanFailed": true,
  "overallScore": 0,
  "scanDate": "2026-03-17T14:30:00Z",
  "summary": "Could not be analyzed — no AI engine responses were received. This is usually temporary; try again."
}

Response — scan still running

{
  "syntheticId": 1234,
  "scanAvailable": false,
  "scanInProgress": true,
  "progress": {
    "completedQueries": 18,
    "totalQueries": 30
  },
  "message": "Scan in progress: 18/30 queries complete. Poll again in 30-60 seconds."
}

Response — no scan found

{
  "syntheticId": 1234,
  "scanAvailable": false,
  "scanInProgress": false,
  "message": "No scan found. Trigger one with POST /api/visibility/scan."
}

Polling strategy

A typical scan takes 2–5 minutes depending on the number of queries and platforms. Here's a reasonable polling loop:

  1. Trigger the scan with POST /api/visibility/scan and note the time
  2. Wait 30 seconds
  3. GET /api/visibility/scan?syntheticId=...
  4. If scanAvailable is false, wait 30 seconds and try again
  5. Re-scanning an agent that already had a scan? A scanAvailable: true whose scanDate is older than your trigger time is the previous scan — keep polling until scanDate advances. (First-ever scans don't hit this: there's no prior scan to return.)
  6. Once you have the fresh scan, fetch the full report with GET /api/visibility/report. If scanFailed is true, the run reached no engine — trigger another scan instead of using the 0.

On this page