REST API
Use Pendium's REST API directly with curl, fetch, or any HTTP client — no MCP required.
When to use the REST API
The MCP server is the recommended way to connect AI agents to Pendium — it handles tool discovery, parameter validation, and structured responses automatically.
But if you're building a custom integration, scripting with curl, or working in an environment that doesn't support MCP, the REST API gives you direct HTTP access to the same data.
Base URLs:
Authentication
Pass your API key in the x-api-key header:
Or, for HTTP clients that only support bearer auth (Databricks Unity Catalog, Bruno, Insomnia, n8n), use Authorization: Bearer:
No key? Most endpoints return a 401 with instructions on how to sign up. The score lookup endpoint works without authentication.
The REST API needs a Starter or Pro plan on the account that owns the key. A key from a free account gets a 403 with code: "PLAN_REQUIRED" and an upgradeUrl on every endpoint; see Authentication.
An account in read-only mode can read its data through the API but can't trigger writes or spend — write/scan endpoints return a 403 (code: "READ_ONLY"); read endpoints (polling, reports, lookups) work normally. A free-plan API key can't trigger scans at all: POST /api/visibility/scan returns a 403 with code: "PLAN_UPGRADE_REQUIRED". A paid key rescanning a group-shared brand whose owner is on the free plan gets a 403 with code: "OWNER_PLAN_UPGRADE_REQUIRED" until the owner upgrades; polling that brand's existing scans still works. If your plan caps how many brands an account can own, a scan of a new URL at that cap returns a 403 with code: "BRAND_LIMIT_REACHED" and creates nothing (details). On a plan with a monthly allowance of on-demand scans, a rescan past it returns a 429 with code: "SCAN_LIMIT_REACHED" and starts nothing. Every limit refusal carries the same fields (errorType, limitKind, message, resetsAt, allowanceOwner, limitSource, ctaLabel, ctaUrl) next to its existing ones (details). Separately, during a platform upgrade we may briefly pause all new scans: POST /api/visibility/scan then returns 503 (code: "SCANS_PAUSED") while polling and reports keep working.
See Authentication for full details.
Endpoints at a glance
Scanning and reports
| Method | Endpoint | What it does |
|---|---|---|
POST | /api/visibility/scan | Trigger a new visibility scan for a brand |
GET | /api/visibility/scan | Check if a scan is done and get progress |
GET | /api/visibility/report | Get the full visibility report with raw LLM responses |
POST | /api/visibility/scan-preview | Trigger a fast, shareable Visibility Scan Preview (slug-canonical, perfect for outreach links) |
GET | /api/visibility/scan-preview | Read the structured Visibility Scan Preview — both scores, analysis, personas, prompts |
A scan only creates a public Brand Index page when you trigger it with publishToBrandIndex: true. Once a brand has a page, it shows the newest scan.
A brand agent a scan creates is added to your account's own team, if it has one, so teammates' API keys can poll it and read its reports.
Scans report transient failures honestly: if a run can't reach any AI engine (a temporary hiccup), it comes back with scanFailed: true (when polling the latest completed scan) or success: false (in a webhook callback, which is scoped to the run that just finished) and a 0 score — so you never mistake a transient zero for a real result. The same honesty applies per platform: any platformScores entry the scan didn't query comes back with queried: false and a null score, not a 0. Which platforms a scan queries, and its query cap, follow the brand owner's plan rather than the calling key's; see Platforms by plan.
Site content
| Method | Endpoint | What it does |
|---|---|---|
GET | /api/v1/site/export | Read a brand's published pages, posts, profile, and business details in one call |
The site export is a versioned contract for your own renderer, so its responses carry an ETag and no _pendium block. Use your own front end walks through a site built on it.
Brand data
| Method | Endpoint | What it does |
|---|---|---|
GET | /api/visibility/account | See your plan and all your brand agents |
GET | /api/visibility/score | Look up any brand's public visibility score (no auth needed) |
Topics and personas
| Method | Endpoint | What it does |
|---|---|---|
GET | /api/visibility/topics | List all topics and search queries for a brand |
POST | /api/visibility/topics | Add new topics with search queries |
GET | /api/visibility/personas | List buyer personas used in scans |
POST | /api/visibility/personas | Create a new persona (AI-generated or manual) |
Response format
Every Visibility API response includes a _pendium object with context about what you're looking at and what to do next. This is designed for both humans reading JSON and AI agents parsing responses.
A paid key reading a brand whose owner is on the free plan (a teammate's brand, for example) gets a partial report from the scan poll and report endpoints, marked freeTierLimited: true (the score and the top 3 findings), in place of the full response. See Get the report.
Rate limits
- Authenticated: 60 requests/minute, plus a shared 5/minute for
POSTtoscan,scan-preview,topics, andpersonas - Unauthenticated: 15 requests/minute (keyless
POSTs toscan,topics, andpersonascount against the same 15)
Limits are counted per server instance, so they're approximate.
See Rate Limiting for details.