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), or connect an MCP client that signs in with OAuth to https://mcp.seobandwagon.com/mcp instead.

  • 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 recognized: 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.

not_enrolledHTTP 403

This account has not joined the AI Overview study

Meaning:
The account is not an active member of the AI Overview study, so there is nothing to give it or take from it.
Fix:
Join on the study page of the dashboard, /dashboard/aio-study, signed in with the email address the invitation went to. A member who left has to join again.
Retry:
Do not retry unchanged: the request itself has to change.

not_invitedHTTP 403

This account was not invited to the AI Overview study

Meaning:
The AI Overview study is by invitation, and the email address on this account is not on the invitation list.
Fix:
If you were invited, sign in with the email address the invitation went to, then join on the study page of the dashboard, /dashboard/aio-study. Otherwise email info@seobandwagon.com.
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.

cloudflare_reauth_requiredHTTP 409

The client's Cloudflare connection cannot do this step

Meaning:
The client's Cloudflare grant is missing, expired, or lacks a scope this step needs. missingScopes lists the scopes, when that is why.
Fix:
The client connects Cloudflare again at /dashboard/connect, granting what the step needs; the step waits for them.
Retry:
Do not retry unchanged: the request itself has to change.

cloudflare_account_requiredHTTP 409

No Cloudflare account chosen for the site

Meaning:
The client has not chosen the Cloudflare account the site goes into.
Fix:
The client chooses the account in the migration wizard; the step waits for them.
Retry:
Do not retry unchanged: the request itself has to change.

cloudflare_account_missingHTTP 409

The site's Cloudflare account is not in the client's connection

Meaning:
The Cloudflare account this site is registered for is not among the accounts the client's connection can act on, so nothing can be deployed into it.
Fix:
The client connects Cloudflare again at /dashboard/connect and picks the firm's account on Cloudflare's consent screen.
Retry:
Do not retry unchanged: the request itself has to change.

workers_subdomain_missingHTTP 409

The account has no workers.dev subdomain

Meaning:
The client's Cloudflare account has not chosen a workers.dev subdomain, so a preview address cannot be built for the site.
Fix:
Choose one in the account (Workers & Pages, then Account details, then the workers.dev subdomain), then run the workflow again.
Retry:
Do not retry unchanged: the request itself has to change.

site_disabledHTTP 403

The site's deploys are switched off

Meaning:
Deploys are switched off for this site.
Fix:
Email info@seobandwagon.com to have them switched on.
Retry:
Do not retry unchanged: the request itself has to change.

ref_not_allowedHTTP 403

This branch or tag may not deploy this environment

Meaning:
The workflow run's ref is not one the site's environment allows to deploy.
Fix:
Run the workflow from an allowed ref. Production deploys only from main.
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.

payment_requiredHTTP 402

Pay for this request with x402

Meaning:
This endpoint is paid per call with x402, and no payment came with the request.
Fix:
Read the PAYMENT-REQUIRED header (or the body): pay what accepts[0] states in USDC, and send the signed payment as PAYMENT-SIGNATURE. An x402 client library does both.
Retry:
Do not retry unchanged: the request itself has to change.

THE REQUEST

bundle_refusedHTTP 422

The build was refused

Meaning:
The platform refused the build. problems lists each reason.
Fix:
Fix what problems names in the site and run the workflow again. Nothing was deployed by a refusal that comes before the upload.
Retry:
Do not retry unchanged: the request itself has to change.

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.

not_assignedHTTP 422

The study did not give this member that search today

Meaning:
The term_id is not one of the searches the study gave this member for today, or it is not in the study at all, so a capture for it is not accepted.
Fix:
Send only a search that GET /api/v1/aio-study/today lists for the same UTC day, with the term_id it gives. Sending the same request again will fail the same way.
Retry:
Do not retry unchanged: the request itself has to change.

query_mismatchHTTP 422

The CSV is for a different search

Meaning:
The search term in the CSV is not the search term the term_id stands for.
Fix:
Run the search the study gave you, export that results page, and send its CSV with that search's term_id. Sending the same request again will fail the same way.
Retry:
Do not retry unchanged: the request itself has to change.

invalid_exportHTTP 422

The CSV is not a usable export

Meaning:
The CSV is not a results-page export the study can read: it has no rows, is not in the extension export format, or has a row from a page other than page 1.
Fix:
Send the unedited CSV the extension exports for page 1 of the search. Sending the same request again will fail the same way.
Retry:
Do not retry unchanged: the request itself has to change.

export_too_largeHTTP 413

The CSV is larger than the study accepts

Meaning:
The CSV is more than 262,144 bytes, the most the study takes for one search.
Fix:
Send the unedited export of one results page; a file this large is not one. Sending the same request again will fail the same way.
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.

payment_in_useHTTP 409

This payment is already being used

Meaning:
Another request is using the same signed payment.
Fix:
Sign a new payment for each request; a payment pays for one call.
Retry:
Do not retry unchanged: the request itself has to change.

ADDRESSES AND RECORDS

site_not_registeredHTTP 404

No site is registered for this repository

Meaning:
No client site is registered for the GitHub repository that sent this request.
Fix:
Email info@seobandwagon.com to have the repository registered. A repository is matched by its numeric id, so a recreated repository must be registered again.
Retry:
Do not retry unchanged: the request itself has to change.

deploy_in_progressHTTP 409

Another deploy of the site is running

Meaning:
Another deploy of this site and environment is running now.
Fix:
Wait for it to finish, then run the workflow again.
Retry:
Retry later. No time is promised, so no Retry-After is sent.

not_approvedHTTP 409

The deploy request is not approved

Meaning:
The deploy request is not approved, so the build cannot be sent. status says where it stands: awaiting_approval, rejected, expired, or already deploying or finished.
Fix:
Wait until the request is approved, or run the workflow again to ask for a new one. An approval allows one deploy.
Retry:
Do not retry unchanged: the request itself has to change.

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

not_yoursHTTP 422

That site or Analytics property is not one this account can reach

Meaning:
The Search Console site or Analytics property in the request is not in the list Google gives for the signed-in account, or that side of Google was not granted.
Fix:
Choose from the sites and properties the page lists. If the one you want is missing, check that this Google account can open it, and connect Google again at /dashboard/search-console.
Retry:
Do not retry unchanged: the request itself has to change.

already_sentHTTP 409

This search was already sent

Meaning:
A capture for this search was already sent for this member this week, and the study keeps the first.
Fix:
There is nothing more to send for it. GET /api/v1/aio-study/today lists the searches still open.
Retry:
Do not retry unchanged: the request itself has to change.

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.

study_unavailableHTTP 503

The AI Overview study is not available right now

Meaning:
The study cannot give or take anything at the moment: its database or storage is not set up on the server that answered.
Fix:
Try again later. If it keeps happening, email info@seobandwagon.com with the request_id.
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.

payment_unavailableHTTP 503

Payments cannot be checked right now

Meaning:
The service that verifies x402 payments did not answer. Nothing was charged.
Fix:
Retry in a little while; the same signed payment works until it expires.
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.

coming_soonHTTP 501

This step is not built yet

Meaning:
This step of the job is not built yet, so nothing was done.
Fix:
Nothing to do now. The dashboard shows the step as coming soon until it is ready; action names it.
Retry:
Do not retry unchanged: the request itself has to change.

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; honor 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: