Skip to main content
Every error has the same JSON body:
  • error is a sentence that says what to change.
  • code is stable. Branch on it, not on error. New codes may be added; none is renamed.
  • requestId matches the X-Request-Id response header. Quote it to support. You can send your own X-Request-Id.
  • details appears on validation errors, one entry per field, with what is accepted (allowed, minimum, maximum, expected).
Every 402 (and a 403 plan_required) also carries resolveUrl, the dashboard page where a person fixes it: resume the plan, buy credits or manage billing. GET /v1/billing lists all of these links. Over MCP, an error comes back as a tool result with isError: true and this body as its text. A tool called without a required path argument (for example get_action with no actionId) answers invalid_request with “actionId is required” before any request is made.

Codes

Rate limits

Limits are per API key. Every response with a key carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (seconds until the bucket is full). Over the limit you get 429 rate_limited with Retry-After in seconds. At most 4 of these slow reads run at once per key: GET /v1/analytics/overview, /v1/analytics/prompts, /v1/analytics/metrics, /v1/keywords and /v1/opportunities/{id}/brief. The fifth gets 429 too_many_concurrent with Retry-After: 2. Across all keys, the API runs at most 2 of these slow reads at once. A slow read over that cap waits up to 5 seconds for a slot, then gets 429 too_many_concurrent with Retry-After: 5. Wait that long and retry; the cap protects every other request from slowing down. Agent connect has no key, so it is limited per IP address: 10 new sessions per 10 minutes, and 120 polls per minute. Its 429 uses the same error body (rate_limited) and Retry-After.

Idempotency

Send Idempotency-Key: <any string, 1 to 255 characters> on a write you might retry. It is honoured on POST /v1/prompts, /v1/prompts/bulk, /v1/visibility-runs, /v1/plan-batches, /v1/opportunities/{id}/plan-requests, /v1/content, /v1/actions/{id}/executions, /v1/site-changes/{id}/pull-requests, /v1/research-requests, /v1/brand/competitors and /v1/webhooks.
  • The same key with the same request within 24 hours returns the first response again, with Idempotent-Replayed: true and the usual X-Request-Id and X-RateLimit-* headers.
  • Only successful responses are kept, so a failed attempt can be retried with the same key.
  • The same key with a different method, path or body answers 422 idempotency_key_reused.
  • The same key while the first request is still running answers 409 conflict. Retry shortly.
Treat it as protection against double submits, not a permanent record: a replay is not guaranteed across a deploy.

Support

Email jia@spreadjam.com or mal@spreadjam.com with the requestId.