Skip to main content

Overview

The Searchable REST API (/api/mcp/* and /api/v1/projects/{projectId}/audits) exposes the same AI-visibility, sources, sentiment, traffic, and content data available in the Searchable dashboard and MCP server, over plain HTTP with a bearer API key. It’s a read-mostly surface — every endpoint below is a GET except the three mutating POSTs (/reports, /audits, /sitemap/refresh). Every endpoint in the left sidebar is interactive — send a real request from the browser using your own API key.

Advanced API Usage

Narrative walkthroughs + curl/JavaScript examples per data area.

Error Reference

Every error code this API returns, and how to fix it.

Base URL

Every path in this reference (e.g. /api/mcp/projects) is relative to that base URL.

Authentication

Create an API key in Settings → Workspace → Integrations in your Searchable dashboard. Keys start with sea_ and are shown only once — store it securely. API access requires a paid Searchable plan; keys can’t be created on the Free plan. Send it as a bearer token on every request:
This REST surface authenticates with a static sea_ API key only. The MCP server additionally supports an OAuth 2.1 login flow for interactive AI clients — see MCP integration if you’re connecting Claude, Cursor, or another MCP client instead of calling REST directly.

Scopes

Each key carries one or more scopes. Scopes are independent grants, not tiers — write does not implicitly include read: A key that only calls GET endpoints needs read. A key that also needs the three action endpoints needs write requested alongside read (or admin, which covers both). A key missing the required scope gets 403 { code: "missing_scope" } — see missing_scope.

Project binding

A key can optionally be bound to a single project at creation time. A project-bound key can only ever act on that one project — even a request for a project its owning user can otherwise access in the dashboard returns 404 { code: "not_found" }, indistinguishable from a project that doesn’t exist. Leave a key unbound to use it across every project you can access. Bind a key when handing it to a single-tenant integration; leave it unbound for an integration that manages multiple projects.

Pagination

Two conventions appear across this API, depending on the endpoint: Offset pagination (limit + offset) — the standard shape for most list endpoints:
Pass nextOffset as the next request’s offset until hasMore is false. totalCount is occasionally null on endpoints where an exact cross-partition total isn’t cheap to compute (e.g. GET /traffic/bots) — hasMore/nextOffset still work correctly in that case. Not every paginated endpoint echoes the full block, though — check each operation’s response schema in the sidebar for the authoritative shape:
  • GET /issues and GET /opportunities return limit/offset/returnedCount/totalCount but omit hasMore/nextOffset — derive it yourself as offset + returnedCount < totalCount.
  • GET /competitors returns only totalCount next to its array, with no limit/offset echo at all.
Cursor pagination (cursor + nextCursor) — used by the Sources domain’s URL-listing endpoints (GET /sources/domains/{domain}/urls, GET /sources/urls), which read from a keyset-ordered store:
Pass the previous response’s nextCursor as the next request’s cursor. Omit cursor for the first page. Treat the cursor as opaque — don’t parse or construct it. limit defaults and maximums vary by endpoint (typically default 20-30, max 100-250) — see each operation’s description in the sidebar.

Rate limiting

Every API key is limited to 600 requests/minute across this REST surface (/api/mcp/*, /api/v1/*, and the Looker Studio connector — MCP tool calls have their own separate concurrency limits, not this budget). Every response carries rate-limit headers, whether it succeeds or errors:
RateLimit/RateLimit-Policy follow the IETF rate-limit header draft (r = remaining, t = seconds until reset, q = quota, w = window in seconds). X-RateLimit-* is kept alongside for clients that read the older convention. The limiter fails open: if its backing store is briefly unavailable, requests are allowed and the rate-limit headers are simply omitted from that response — don’t hard-require the headers in client code. Exceeding the limit returns 429 with a Retry-After header (seconds) and a rate_limited problem+json body.

Idempotency

The three mutating POST endpoints — POST /reports, POST /audits, and POST /sitemap/refresh — accept an Idempotency-Key header. Send the same key on a retry (after a timeout or a dropped connection) and the API replays the original response instead of repeating the side effect:
Notes:
  • Keys are scoped per API key and held for 24 hours.
  • Only a successful (2xx) response is stored — a failed attempt (4xx/5xx) is never replayed; retrying after a real failure with the same key simply runs the request again.
  • Two concurrent requests with the same key never both execute: the second gets 409 { code: "idempotency_in_flight" } with Retry-After: 5 while the first is still running.
  • A stored response body over 100KB isn’t cached — the request still runs normally, and the response carries X-Idempotent-Skipped: body-too-large instead of X-Idempotent-Replay.
  • No Idempotency-Key header — the request behaves exactly as before. Fully opt-in.

Errors

Every non-2xx response is application/problem+json — a machine-readable envelope that additively extends the API’s original { error: "..." } shape, so existing integrations that only read error keep working unmodified:
code is the field to branch on — it’s stable across releases. Full catalog, per-code remediation, and additive fields (requiresUpgrade, retryable, current/limit, …): API Error Reference. Beyond the statuses listed per operation, any endpoint may also return a generic 500 or 502 problem+json with code: "internal_error" on an unexpected internal failure (the 502 variant carries retryable: true) — treat both as transient and retry with exponential backoff. Every response — success or error — also carries X-Request-Id. Include it when contacting support@searchable.com about a specific request.

Typed clients

There’s no official SDK — the OpenAPI spec is the contract. Call the API directly with fetch/curl/your HTTP client of choice, or generate TypeScript types straight from the spec with the MIT-licensed openapi-typescript:
That produces a paths / components type tree covering every endpoint and schema on this page — pair it with a typed fetch wrapper (e.g. openapi-fetch) for end-to-end type safety with no generated SDK to maintain. An official SDK isn’t planned unless customer demand shows up.

Next steps

Agent API

Programmatic access to the Searchable agent, for approved customers.

Changelog

What’s new on this API, and the deprecation policy.