SEO BANDWAGON

FOR DEVELOPERS & AI AGENTS

API ERRORS

Every error code the SEO Bandwagon API and MCP server send: what it means, how to fix it, and when a retry helps. Each error response links its entry here in docs_url.

THE ERROR SHAPE

HTTP/1.1 403 Forbidden
Content-Type: application/json
Cache-Control: no-store
X-Request-Id: 3f2c9a1e-5b7d-4e61-9c0a-2d8f4b6e1a77

{
  "error": "insufficient_scope",
  "required": "captures",
  "message": "The API key is valid but lacks the scope this endpoint needs. The `required` field names it.",
  "fix": "A key's scopes are fixed when it is made. Make a new key that includes the required scope at https://seobandwagon.com/dashboard/api-keys, then revoke the old one if nothing else uses it.",
  "docs_url": "https://seobandwagon.com/docs/errors#insufficient_scope",
  "request_id": "3f2c9a1e-5b7d-4e61-9c0a-2d8f4b6e1a77"
}
  • error is the code, listed below. It is the value each endpoint has always sent, so code that matches on it keeps working, and any other field an endpoint sends (details, required, limit) stays as it was.
  • message says what went wrong and fix how to recover.
  • docs_url links the code's entry on this page.
  • request_id, also sent as the X-Request-Id header, identifies the request. Quote it when you email info@seobandwagon.com.

Every error carries Cache-Control: no-store. Every 429, and a 503 when waiting helps, carries Retry-After: the seconds to wait before sending the request again. Authentication is described in auth.md and the endpoints on the API & MCP server page.

THE MCP ENDPOINT

https://seobandwagon.com/api/mcp checks the API key before any tool runs and answers each way a key can fail differently. The bodies carry the same fields as every other error, plus error_description. Protocol errors after that come back as JSON-RPC messages, and a tool that fails returns isError: truewith its reason.

WHENSTATUSERRORERROR_DESCRIPTIONHEADERS
No API key sent401invalid_tokenNo authorization providedWWW-Authenticate: Bearer realm="seobandwagon", resource_metadata="https://seobandwagon.com/.well-known/oauth-protected-resource" (no error code in the challenge); Link: </auth.md>; rel="help"
Malformed key (not sbw_ and 48 hex characters)401invalid_tokenThe bearer token is not an SEO Bandwagon API keyWWW-Authenticate: Bearer realm="seobandwagon", error="invalid_token", error_description, resource_metadata="https://seobandwagon.com/.well-known/oauth-protected-resource"; Link
Unknown or revoked key401invalid_tokenThe API key is unknown or has been revokedWWW-Authenticate: Bearer realm="seobandwagon", error="invalid_token", error_description, resource_metadata="https://seobandwagon.com/.well-known/oauth-protected-resource"; Link
Key without the mcp scope403insufficient_scopeThe API key does not have the mcp scopeWWW-Authenticate: Bearer realm="seobandwagon", error="insufficient_scope", error_description, scope="mcp", resource_metadata="https://seobandwagon.com/.well-known/oauth-protected-resource"; Link
The key cannot be checked (database error)503Auth backend unavailableRetry-After: 30
More than 120 requests in a minute for one account, across all its keys429Too many requestsRetry-After: the seconds left in the minute
Any other single-segment /api/<x> path404Not foundnone beyond the usual

KEYS, SESSIONS AND ACCESS

UnauthorizedHTTP 401

No accepted API key or signed-in session

Meaning:
The request carried no API key and no signed-in session.
Fix:
Send an API key as Authorization: Bearer sbw_…, or call from a browser signed in at /auth/signin. How to get a key: /auth.md
Retry:
Do not retry unchanged: the request itself has to change.
  • The request sent no API key.

    The request carried no API key and no signed-in session.

    Fix: Send an API key as Authorization: Bearer sbw_…, or call from a browser signed in at /auth/signin. How to get a key: /auth.md

  • The request sent an API key.

    The API key was not accepted: it is malformed, unknown or revoked, or this endpoint takes only a signed-in session.

    Fix: Check the key with GET /api/v1/me. If that also answers 401, make a new key at /dashboard/api-keys. If it answers 200, this endpoint needs a browser signed in at /auth/signin; /docs/api lists what each endpoint accepts.

signin_requiredHTTP 401

Sign-in required

Meaning:
This endpoint runs only for a signed-in account, and the request had no session.
Fix:
Sign in at /auth/signin and send the request from that browser session.
Retry:
Do not retry unchanged: the request itself has to change.
Also sent as:
Sign in required

API key requiredHTTP 401

This endpoint checks an API key

Meaning:
This endpoint exists to check an API key, and the request did not carry a valid one. A signed-in session does not count here.
Fix:
Send Authorization: Bearer sbw_… with a live key. Keys are made at /dashboard/api-keys; see /auth.md.
Retry:
Do not retry unchanged: the request itself has to change.

invalid_tokenHTTP 401

The MCP endpoint did not get a usable API key

Meaning:
The MCP endpoint needs an API key on every request, and this one did not have a usable key.
Fix:
Send Authorization: Bearer sbw_… with a key that has the mcp scope. How to get one: /auth.md
Retry:
Do not retry unchanged: the request itself has to change.
  • No key was sent (error_description "No authorization provided").

    No API key was sent. The MCP endpoint needs one on every request.

    Fix: Send Authorization: Bearer sbw_… with a key that has the mcp scope. How to get one: /auth.md

  • The bearer token is not shaped like a key.

    The bearer token is not an SEO Bandwagon API key. Keys are sbw_ followed by 48 hexadecimal characters.

    Fix: Send the key exactly as it was shown when it was made. A lost key cannot be shown again: make a new one, with the mcp scope, at /dashboard/api-keys.

  • The key is well formed but unknown or revoked.

    The API key is not recognised: it was revoked, or it never existed.

    Fix: Make a new key with the mcp scope at /dashboard/api-keys and send it as Authorization: Bearer sbw_….

insufficient_scopeHTTP 403

The API key lacks the scope this endpoint needs

Meaning:
The API key is valid but lacks the scope this endpoint needs. The required field names it.
Fix:
A key's scopes are fixed when it is made. Make a new key that includes the required scope at /dashboard/api-keys, then revoke the old one if nothing else uses it.
Retry:
Do not retry unchanged: the request itself has to change.

ForbiddenHTTP 403

Signed in, but not allowed

Meaning:
The account is signed in but may not do this, usually because the action is for administrators or touches another account's data.
Fix:
Use an account that has access. If yours should, email info@seobandwagon.com with the request_id.
Retry:
Do not retry unchanged: the request itself has to change.

Cross-origin request rejectedHTTP 403

Key management from another site

Meaning:
The request came from another site. API keys can be managed only from seobandwagon.com.
Fix:
Manage keys at /dashboard/api-keys.
Retry:
Do not retry unchanged: the request itself has to change.

upgrade_requiredHTTP 403

The tool is on a higher plan

Meaning:
This tool is on a higher plan than the account's. plan is the account's plan and requiredPlan the one the tool needs.
Fix:
To upgrade the account, email info@seobandwagon.com. Tools on the account's own plan keep working.
Retry:
Do not retry unchanged: the request itself has to change.

Auth backend unavailableHTTP 503

The API key could not be checked

Meaning:
The API key could not be checked because the key store did not answer. The key itself may be fine.
Fix:
Retry after the number of seconds in the Retry-After header. If it keeps happening, /api/health shows whether the site is up.
Retry:
Retry after the seconds in the Retry-After header (30 when the endpoint sends none).

scope_not_allowedHTTP 403

A key tried to grant a scope it does not hold

Meaning:
An API key can make keys only with scopes it holds itself, and never with keys. The request asked for one it cannot pass on.
Fix:
Ask for a subset of this key's own scopes, without keys, or make the key at /dashboard/api-keys while signed in.
Retry:
Do not retry unchanged: the request itself has to change.

not_revocableHTTP 403

This key cannot revoke that key

Meaning:
An API key can revoke only itself or keys it created. The key named in the request is neither.
Fix:
Revoke it at /dashboard/api-keys while signed in, or with the key that created it.
Retry:
Do not retry unchanged: the request itself has to change.

THE REQUEST

Invalid inputHTTP 400

The request failed validation

Meaning:
The request body or parameters failed validation. details lists each problem.
Fix:
Correct the fields named in details and send the request again. Sending the same request again will fail the same way.
Retry:
Do not retry unchanged: the request itself has to change.

Invalid queryHTTP 400

A query parameter is missing or invalid

Meaning:
A query-string parameter is missing or invalid. details lists each problem.
Fix:
Correct the parameters named in details and send the request again. Sending the same request again will fail the same way.
Retry:
Do not retry unchanged: the request itself has to change.

Invalid bodyHTTP 400

The JSON body failed validation

Meaning:
The JSON body failed validation. details lists each problem.
Fix:
Correct the fields named in details and send the request again. Sending the same request again will fail the same way.
Retry:
Do not retry unchanged: the request itself has to change.

Validation errorHTTP 400

The request failed validation

Meaning:
The request failed validation. details lists each problem.
Fix:
Correct the fields named in details and send the request again. Sending the same request again will fail the same way.
Retry:
Do not retry unchanged: the request itself has to change.

Invalid JSONHTTP 400

The body is not JSON

Meaning:
The request body is not valid JSON.
Fix:
Send a JSON body with the header Content-Type: application/json.
Retry:
Do not retry unchanged: the request itself has to change.

Invalid domainHTTP 400

The domain is not a hostname

Meaning:
The domain in the request is not a valid hostname.
Fix:
Send a bare hostname such as example.com, with no scheme, path or port.
Retry:
Do not retry unchanged: the request itself has to change.

bad_requestHTTP 400

Any other 400

Meaning:
The request is missing something or has a value the endpoint cannot use.
Fix:
The error field says what is wrong. Correct the request, checking the endpoint at /docs/api, and send it again. Sending the same request again will fail the same way.
Retry:
Do not retry unchanged: the request itself has to change.

idempotency_conflictHTTP 409

That Idempotency-Key was used for a different request

Meaning:
The Idempotency-Key header matches an earlier request with a different body.
Fix:
Send a new Idempotency-Key, or repeat the earlier request exactly.
Retry:
Do not retry unchanged: the request itself has to change.

not_self_serveHTTP 422

That plan is not sold by checkout

Meaning:
Only Pro is bought by checkout. Agentic Local, Enterprise and the services are bought by talking to us.
Fix:
Get in touch at /contact; every price is at /pricing.
Retry:
Do not retry unchanged: the request itself has to change.

invalid_requestHTTP 400

The request is not in the documented shape

Meaning:
A field is missing, extra or of the wrong type.
Fix:
Send the fields the endpoint documents at /docs/api.
Retry:
Do not retry unchanged: the request itself has to change.

ADDRESSES AND RECORDS

Not foundHTTP 404

Nothing at this address, or not yours

Meaning:
Nothing exists at this address, or the record belongs to another account.
Fix:
Check the path and any id in it. A record that belongs to another account also answers 404, so that ids cannot be probed.
Retry:
Do not retry unchanged: the request itself has to change.
Also sent as:
not found

conflictHTTP 409

Any other 409

Meaning:
The request conflicts with the current state of the record.
Fix:
The error field names the conflict. Change what it names, or read the current state, before sending again.
Retry:
Do not retry unchanged: the request itself has to change.

already_subscribedHTTP 409

The account already pays for Pro

Meaning:
This account already has an active Pro subscription, so there is nothing to buy.
Fix:
Open the billing portal (POST /api/v1/billing/portal, or the billing_portal MCP tool) to change the card, see invoices or cancel.
Retry:
Do not retry unchanged: the request itself has to change.

payment_past_dueHTTP 409

A Pro payment is past due

Meaning:
The last Pro payment failed and Stripe is retrying it. The plan stays on until its grace date while it does.
Fix:
Update the card in the billing portal (POST /api/v1/billing/portal, or the billing_portal MCP tool).
Retry:
Do not retry unchanged: the request itself has to change.

no_billing_accountHTTP 409

No billing account yet

Meaning:
This account has never started a checkout, so there is no billing portal to open.
Fix:
Start a checkout first: POST /api/v1/billing/checkout, or the billing_checkout MCP tool.
Retry:
Do not retry unchanged: the request itself has to change.

not_foundHTTP 404

Nothing with that id belongs to this account

Meaning:
No record with that id belongs to the account behind this request.
Fix:
Check the id. For a checkout, use the session_id that POST /api/v1/billing/checkout returned.
Retry:
Do not retry unchanged: the request itself has to change.

LIMITS

Too many requestsHTTP 429

Rate limit

Meaning:
Too many requests from this address or account in the current window.
Fix:
Wait the number of seconds in the Retry-After header, then retry. Spreading requests out keeps them under the limit.
Retry:
Retry after the seconds in the Retry-After header (60 when the endpoint sends none).

daily_limitHTTP 429

Daily allowance used

Meaning:
The account has used today's allowance for this endpoint. limit is the daily number.
Fix:
Retry after the number of seconds in the Retry-After header, when the daily count resets. A higher plan has a higher allowance.
Retry:
Retry after the seconds in the Retry-After header, which counts to the daily reset.

limit_reachedHTTP 429

Daily runs used, or a plan cap reached

Meaning:
The account has used today's runs of this tool. used and limit give the counts.
Fix:
Retry after the number of seconds in the Retry-After header, when the daily count resets. A higher plan has more runs.
Retry:
Retry after the seconds in the Retry-After header, which counts to the daily reset.
  • Sent with 429: the daily runs of a tool are used up.

    The account has used today's runs of this tool. used and limit give the counts.

    Fix: Retry after the number of seconds in the Retry-After header, when the daily count resets. A higher plan has more runs.

  • Sent with 403: the account holds as many of something as its plan allows.

    The account already holds as many of these as its plan allows.

    Fix: Remove one first. To raise the cap, email info@seobandwagon.com about upgrading.

capacity_reachedHTTP 429

The tool's shared daily capacity is used

Meaning:
This tool has used its shared daily capacity, across all accounts.
Fix:
Retry after the number of seconds in the Retry-After header, when the daily capacity resets.
Retry:
Retry after the seconds in the Retry-After header, which counts to the daily reset.

key_limitHTTP 409

Too many live API keys

Meaning:
The account already has the most live API keys it can hold. limit gives the number.
Fix:
Revoke a key you no longer use at /dashboard/api-keys, then make the new one.
Retry:
Do not retry unchanged: the request itself has to change.

OUR SIDE

stripe_errorHTTP 502

Stripe refused or failed the request

Meaning:
The payment provider did not complete the request.
Fix:
Retry in a minute. If it keeps failing, email info@seobandwagon.com.
Retry:
Retry later. No time is promised, so no Retry-After is sent.

billing_unavailableHTTP 503

Checkout is not available right now

Meaning:
Checkout is not configured on the site at the moment.
Fix:
Try again later, or get in touch at /contact to buy.
Retry:
Retry later. No time is promised, so no Retry-After is sent.

internal_errorHTTP 500

Something failed on our side

Meaning:
Something failed on our side while handling the request.
Fix:
Retry once. If it fails again, email info@seobandwagon.com with the request_id.
Retry:
Retry later. No time is promised, so no Retry-After is sent.
Also sent as:
Internal error, Internal server error, Something went wrong

upstream_errorHTTP 502

A data provider failed

Meaning:
A data provider we depend on, such as Google or DataForSEO, failed to answer.
Fix:
Retry in a little while. Nothing in the request needs to change.
Retry:
Retry later. No time is promised, so no Retry-After is sent.

Database not configuredHTTP 503

No database on the server that answered

Meaning:
The server that answered has no database connection configured.
Fix:
The fault is ours, not the request's. Try again later; if it persists, email info@seobandwagon.com with the request_id.
Retry:
Retry later. No time is promised, so no Retry-After is sent.
Also sent as:
DB not configured

Temporarily unavailableHTTP 503

A service behind the endpoint is down

Meaning:
A service this endpoint needs is not available right now.
Fix:
Try again later. Nothing in the request needs to change; if it persists, email info@seobandwagon.com.
Retry:
Retry later. No time is promised, so no Retry-After is sent.

feature_disabledHTTP 503

The feature is switched off

Meaning:
This feature is switched off on the server right now.
Fix:
Try again later. Nothing in the request needs to change.
Retry:
Retry later. No time is promised, so no Retry-After is sent.

service_unavailableHTTP 503

Any other 503

Meaning:
The endpoint cannot serve requests right now.
Fix:
The error field says what is missing. Try again later; honour Retry-After when it is sent.
Retry:
Retry later. No time is promised, so no Retry-After is sent.

ERRORS NOT LISTED HERE

Some endpoints send an error that describes their own failure, such as Failed to fetch campaigns or Pitch not found. That text is the message, and fix and docs_url come from the entry for its HTTP status: