PendiumDocs

Trigger a Scan

Start an AI visibility scan for any brand across ChatGPT, Claude, Gemini, and AI Overviews.

POST /api/visibility/scan

Authentication: Required. An API key whose account is in read-only mode gets a 403 with code: "READ_ONLY" — scans are a write + spend, so they're refused.

Start a visibility scan that checks how AI platforms perceive and recommend a brand. You can scan a new URL (which creates a brand agent if one doesn't exist) or re-scan an existing agent by ID.

The scan runs in the background — this endpoint returns immediately. Use Poll Scan Status to check when results are ready.

Request body

{
  "url": "https://example.com",
  "mode": "batch",
  "maxQueries": 30
}
FieldTypeRequiredDefaultDescription
urlstringOne of url or syntheticIdWebsite URL to scan. If no agent exists for this domain yet, one is created automatically.
syntheticIdintegerOne of url or syntheticIdID of an existing brand agent to re-scan. Use Account to find your agent IDs.
modestringNo"batch"Both "batch" and "full" scan all 4 AI platforms with fast, cost-effective models. (Premium-model runs are an advanced opt-in, not selected by "full" on its own.)
maxQueriesintegerNo30How many queries to run across all platforms (1–100). 20–40 is the sweet spot for coverage vs. cost.
platformConfigobjectNoPer-platform overrides, e.g. { "Gemini": { "enabled": false } }. The four standard platforms (ChatGPT, Claude, Gemini, AI Overviews) are on by default. Enterprise-only platforms (Perplexity, Grok, DeepSeek, Qwen, MiniMax, Nemotron, Llama) require the enterprise plan — see below.
webhookUrlstringNoHTTPS URL we POST the results to when the scan finishes, so you don't have to poll. Max 2048 chars; must be HTTPS and public (private/internal addresses are rejected). The response echoes webhookRegistered: true when accepted. See Webhook callback.

Enterprise platforms

The four standard platforms run on every scan. Enterprise plans can additionally enable Perplexity, Grok, DeepSeek, Qwen, MiniMax, Nemotron, and Llama by passing them in platformConfig (e.g. { "Perplexity": { "enabled": true } }).

If your account isn't on the enterprise plan, any enterprise platform you request is dropped — the scan still runs on the standard platforms — and the scanScope.droppedEnterprisePlatforms field in the response lists what was skipped. Contact sales to enable them.

How it works

Scanning an existing agent (syntheticId):

  1. Verifies you own the agent
  2. Checks your credit balance
  3. If a scan is already running, returns its progress instead of starting a new one
  4. Otherwise, kicks off the scan and returns immediately

Scanning a new URL (url):

  1. Extracts the domain from your URL
  2. Checks if you already have an agent for that domain — if so, re-scans it
  3. If not, creates a new brand agent (scrapes the site, builds a knowledge base) and starts the scan

Example

curl -X POST https://pendium.ai/api/visibility/scan \
  -H "Content-Type: application/json" \
  -H "x-api-key: pk_live_xxxxxxxxxxxx" \
  -d '{"url": "https://example.com", "mode": "batch", "maxQueries": 30}'

Response

{
  "success": true,
  "syntheticId": 1234,
  "agentName": "Example",
  "scanTriggered": true,
  "isNewAgent": false,
  "mode": "batch",
  "maxQueries": 30,
  "message": "Scan triggered. Poll GET /api/visibility/scan?syntheticId=1234 for results."
}

If a scan is already running:

{
  "success": true,
  "scanAlreadyRunning": true,
  "syntheticId": 1234,
  "progress": {
    "completedQueries": 12,
    "totalQueries": 30
  }
}

If you're out of credits:

{
  "success": false,
  "error": "Insufficient credits",
  "creditBalance": 0,
  "upgradeUrl": "https://pendium.ai/pricing"
}

Webhook callback

If you passed webhookUrl, we POST the finished results there when the scan completes, so you don't have to poll. The payload describes the scan that just ran:

{
  "success": true,
  "syntheticId": 1234,
  "scanId": "8f3c…",
  "overallScore": 62,
  "visibilityLevel": "moderate",
  "summary": "…",
  "platformScores": [],
  "topCompetitors": [],
  "recommendations": [],
  "reportUrl": "https://pendium.ai/r/abc12345",
  "claimUrl": null
}

success reflects the real outcome of that run. If the scan couldn't reach any AI engine (a transient provider/network failure), success is false and overallScore is 0 — don't ingest the zero as a real result; trigger another scan. The same condition surfaces as scanFailed: true when you poll.

On this page