Error Codes

The error format and every error code the Spysay API returns, grouped by type, with the HTTP status and what each code means.

Failed requests return a non-2xx status and a JSON body with one error object:

json
{
  "error": {
    "type": "billing_error",
    "code": "insufficient_credits",
    "message": "This needs 40 credits, and the balance is 12. Top up to continue.",
    "credits_required": 40,
    "credits_balance": 12,
    "doc_url": "/docs/api-reference/error-codes#insufficient_credits"
  }
}
Field Description
type Broad category. Branch on this first.
code Specific reason. Stable, safe to match in code.
message Human-readable. May change, so don't parse it.
param The request field at fault, as a dotted path for nested fields, e.g. redirect_rules.0.source.
doc_url Link to the code's entry on this page.
extra fields Some codes add context, e.g. credits_required and credits_balance, limit and current, details, audit_id.

Error types

Type Status Meaning
invalid_request_error 400, 404, 409, 422 The request was malformed, conflicted or pointed at nothing
authentication_error 401 No API key, or a bad one
billing_error 402 The credit balance can't cover the request
permission_error 403 Valid key, not allowed to do this
rate_limit_error 429 Too many requests. Back off and retry.
api_error 5xx Something failed on our side. Safe to retry.

invalid_request_error

parameter_missing

A required parameter is missing, and param names it. Status 400.

parameter_invalid

A parameter has the wrong type or format, or is out of range. It's also returned for an ID with the wrong prefix. param names the parameter. Status 400.

parameter_unknown

The body contains a field that doesn't exist, often the camelCase spelling of a snake_case field. param names it, and snake_case explains the convention. Status 400.

invalid_hostname

The hostname is empty or isn't a valid hostname. Status 400.

invalid_origin_host

The origin host doesn't normalize to a valid hostname. Status 400.

origin_host_same_as_domain

The origin host can't be the same as the domain. Status 400.

origin_host_is_editor_url

The origin host is an editor URL. Use the published project URL instead. Status 400.

redirect_limit_reached

The domain already has the maximum number of redirect rules. The error includes limit and current. Status 400.

invalid_custom_headers

One or more response header rules failed validation. The error's details lists the problems. Status 400.

sitemap_fetch_failed

The sitemap URL couldn't be fetched. Status 400.

sitemap_parse_failed

The sitemap URL returned something other than a valid XML sitemap. Status 400.

invalid_urls

At least one audit seed URL is invalid or isn't on the target domain. Status 400.

no_sitemap

The operation needs a sitemap, and the domain doesn't have one. Status 400.

resource_missing

The domain, audit, prompt or other object doesn't exist on your account, or no endpoint matches the URL and method. Status 404.

audit_in_progress

An audit is already running for this domain. The error includes its audit_id. Status 409.

domain_already_connected

The domain is already connected to an account. Status 409.

business_already_claimed

Another account has already verified this business's website. Contact support to move it. Status 409.

action_not_waiting

The action was already approved, dismissed or published. Only a waiting action can be approved or dismissed. Status 409.

verification_failed

The business couldn't be verified: the signed-in email isn't at its website's domain, or nobody is signed in. On Growth, its prompts aren't checked until it is. Status 422.

no_new_pages

new_pages_only was set, and nothing has changed since the last audit. Status 422.

authentication_error

missing_api_key

The request doesn't include an API key. Status 401.

invalid_api_key

The API key is invalid or has been revoked. See Authentication. Status 401.

billing_error

insufficient_credits

Your credit balance can't cover the request. The error includes credits_required and credits_balance, so you know how much to top up before retrying. Status 402.

plan_limit_reached

Adding this would pass your plan's limit: active questions, or markets. The error includes limit and current. Upgrade, or pause or remove one first; a paused question doesn't count. Status 403.

permission_error

requires_unscoped_key

This endpoint needs an account-wide key rather than a domain-scoped one. Status 403.

domain_not_owned

The domain isn't connected to your account. Status 403.

api_key_domain_scope_mismatch

The key is valid, but it's scoped to a different domain. Status 403.

domain_suspended

The domain is suspended and can't be added again. Status 403.

permission_denied

You're signed in as a team member whose role doesn't allow this change. Viewers can only read; editors can't manage billing, the team, API keys or account settings, and approve fixes only when the owner allows it. Status 403.

rate_limit_error

rate_limited

This key sent too many requests. Wait about a minute, then retry. Status 429.

api_error

internal_error

Something failed unexpectedly on our side. Retry, and contact support if it keeps happening. Status 500.

insert_failed

The record couldn't be saved. Retry the request. Status 500.

not_implemented

The endpoint exists, but its logic hasn't shipped yet. Status 501.

archive_unavailable

The audit's archived pages couldn't be read. Retry later. Status 503.

service_unavailable

The configuration couldn't be applied. Retry the request. Status 503.

Best practices

  • Branch on type first and then on code. Don't parse message, since its wording can change.
  • Send Accept: text/html on render requests made for crawlers.
  • Rotate keys regularly, and revoke any key that leaks from the dashboard.
  • Keep prewarm on for important pages so they're cache hits right after a purge.
  • When the Render API returns 304, follow Location to the origin.