Error and status codes
Every machine-readable error code and HTTP status the platform returns, listed alphabetically, with cause, fix and whether it is safe to retry.
Last updated 2026-08-06
Summary#
Two lookups in one page. The first is alphabetical by code, for anyone reading a response body or a log line. The second runs the other way, from the message a customer can see on their screen back to the code behind it, for anyone on a support call holding a screenshot. Both are complete for the codes the platform emits.
Overview#
A failed request returns a human message and, when the failure came from a gate rather than from a handler's own validation, a stable machine code.
{
"error": "This tool needs a paid plan. Free includes the 10 technical SEO tools; upgrade to Pro to unlock the rest.",
"code": "upgrade_required",
"current_plan": "free",
"required_plan": "pro",
"upgrade_url": "https://metricvaultai.com/pricing"
}Three rules for reading one:
- Branch on
code, never on the message. Messages are translated in the interface and their wording changes; codes are stable. codeis often absent. Plain validation failures returnerroronly. That is normal, not a malformed response.- A
200is not always a success. A few endpoints report failure inside the body. They are listed near the end of this page.
Quota and plan failures carry extra structured fields. Quota responses add plan, used, quota, cost, reset_at, reset_date, days_until_reset and upgrade_url. Rate-limit responses add limit, used and reset_in_minutes. Plan gates add current_plan, required_plan and upgrade_url.
HTTP statuses#
| Status | Meaning here | Safe to retry? |
|---|---|---|
200 | Success, or a soft failure carrying ok: false. Check the body | Not applicable |
201 | Created. Blog sites and taxonomy items only | No |
301 | Permanent redirect. Legacy public-tool paths only | Follow it |
400 | The request was wrong: a missing field, a bad value, an unsupported type | No. Fix the request |
401 | Not authenticated: no credential, or an invalid, expired or revoked one | No. Fix the credential |
403 | Authenticated but not allowed: wrong plan, wrong role, suspended account, wrong secret | No. Change the entitlement |
404 | The resource does not exist, or does not belong to the caller | No |
405 | Method not allowed. Blog site console only | No |
409 | Conflict: already invited, or a newsletter already sent | No, unless you resend with force |
410 | Gone. A share link past its expiry | No |
413 | Payload too large. Blog media import only | No. Send something smaller |
429 | Rate limited, or out of credits | Yes, after the stated window |
500 | An unhandled error inside a handler, or an upstream failure | Once |
502 | An upstream fetch failed. Responsiveness analysis and Growth Actions | Once |
503 | A dependency is unavailable or unconfigured and the gate failed closed | Yes, with backoff |
504 | An upstream fetch timed out. The responsiveness preview only | Once |
Every administration route answers a failed authorisation with 403 and {"error":"Unauthorized"}, not 401.
Codes, A to Z#
code | HTTP | What it means | Cause | Fix | Retry |
|---|---|---|---|---|---|
account_suspended | 403 | The account is suspended | An administrator suspended it. Billing is not affected | Contact support | No |
ai_not_configured | 503 | No AI provider key is set for this feature | Operator configuration | Operator action | No |
ai_unavailable | 503 | Every AI provider failed for this call | Upstream outage or timeout during claim extraction | Wait and run again | Yes |
already_sent | 409 | The newsletter for that post has already been delivered | A repeat send | Send again with force only if intended | No |
auth_required | 401 | The call needs an identified account | A metered run arrived with no account, or a recommendations request did | Sign in, or send the account address | No |
bad_form | 400 | The upload was not multipart | Wrong content type on a media upload | Use multipart/form-data | No |
bad_locale | 400 | The language code was not recognized | A malformed translation target | Send a two-letter code, optionally with a region | No |
bad_method | 405 | That method is not supported on this route | Blog site console only | Use GET, POST or PATCH | No |
bad_request | 400 | A required field was missing or a value was not allowed | Blog writes without a site, role changes without both addresses, an attempt to assign the owner role | Send the missing field, or pick a valid value | No |
bad_type | 400 | Unsupported image type | Blog media upload | Use PNG, JPG, WEBP or GIF | No |
blocked_host | 400 | The target host is not allowed | The address resolved to a private, loopback, link-local or reserved network | Point at a public host | No |
blog_not_configured | 503 | Blog publishing is not configured in this environment | Operator configuration | Operator action | No |
create_failed | 400 | The item could not be created | A taxonomy item or author created without a name | Send a name | No |
email_not_configured | 503 | Email delivery is not configured | The email provider key is missing | Operator action | No |
forbidden | 403 | Allowed to sign in, not allowed to do this | A blog role lacks the capability, an owner-only blog action, or a diagnostic route called without the internal key | Ask the owner to raise your role, or supply the correct key | No |
hourly_rate_limit | 429 | The hourly fair-use ceiling was reached | More than 100 light-tool calls in one clock hour | Wait for reset_in_minutes | Yes |
media_not_configured | 503 | Image storage is not connected | The object store is not bound | Operator action | No |
no_customer | 400 | No subscription exists for this address | The address has never subscribed | Subscribe first | No |
no_file | 400 | The upload contained no file | The multipart body had no file part | Attach the file | No |
no_recipients | 400 | None of the addresses given are confirmed subscribers | A newsletter aimed at unknown addresses | Check the list | No |
no_sentences | 400 | No checkable sentences were found | The text was fragments rather than prose | Send prose | No |
no_session | 401 | The blog API could not identify the caller | No account address on the request | Send the account address | No |
no_subscribers | 400 | The site has no confirmed subscribers | Nothing to send to | Collect subscribers first | No |
not_configured | 503 | A provider this feature needs is not configured | Plagiarism checking needs search-data credentials; Fact Check needs the research provider key | Operator action | No |
not_found | 404 | The target does not exist | An unknown id, a non-member, or an unknown sub-route | Check the identifier | No |
not_published | 400 | The post is still a draft | A newsletter was requested for an unpublished post | Publish it first | No |
plan_check_failed | 503 | The plan lookup failed, and the gate failed closed | A dependency was briefly unavailable. Nothing ran and nothing was charged | Retry with backoff | Yes |
portal_not_configured | 503 | The billing portal has no configuration and could not be created | Operator configuration | Operator action | No |
post_not_found | 404 | No such public post | The slug is wrong, or the post is not live | Publish it, or check the slug | No |
quota_check_failed | 503 | The usage lookup failed, and the gate failed closed | A dependency was briefly unavailable. Nothing ran and nothing was charged | Retry with backoff | Yes |
quota_exceeded | 429 | The month's credit allowance is spent, or the plan has none | A premium run on an exhausted or zero allowance | Wait for reset_date, or upgrade | Not until reset |
reco_quota_exceeded | 429 | The month's AI recommendations allowance is spent | Too many Get recommendations requests this month | Wait for the reset, or upgrade | Not until reset |
reco_upgrade_required | 403 | Recommendations need a higher plan | Pro and above by default | Upgrade | No |
same_locale | 400 | The translation target equals the source | The post is already in that language | Pick another language | No |
seat_check_failed | 503 | The seat count could not be read, and the gate failed closed | A dependency was briefly unavailable | Retry | Yes |
send_failed | 503 | The email provider rejected the batch | Upstream refusal | Retry | Yes |
site_not_found | 404 | No such blog site | Wrong site id or slug | Check the identifier | No |
stripe_not_configured | 503 | Billing is not configured in this environment | The billing key is not set | Operator action | No |
stripe_error | 503 or 400 | The billing provider returned an error | Passed through from the provider | Read the message; retry only for a transient error | Sometimes |
subscribe_failed | 400 | The subscription address failed validation | A malformed email address | Correct the address | No |
too_large | 400 or 413 | The payload exceeded its ceiling | A media upload over 5 MB, or an import over 15 MB | Send something smaller. See All limits and quotas | No |
too_short | 400 | There was not enough text to check | Fewer than 25 words sent to the plagiarism or fact checks | Send more text | No |
upgrade_required | 403 | The plan is below the feature's minimum | A Free account ran a tool that costs credits, a feature needs a specific tier, or the plan has no team seats | Upgrade. required_plan names the tier | No |
webhook_not_configured | 503 | The billing webhook signing secret is missing | Operator configuration | Operator action | No |
From the message on screen back to the code#
The messages a customer is most likely to read out or screenshot, and what they mean. Quoted exactly.
| Message | Code | HTTP | What actually happened |
|---|---|---|---|
Please sign in to run this. | auth_required | 401 | A run that costs credits arrived without an account |
Please sign in to use recommendations. | auth_required | 401 | Same, from the recommendations panel |
This account is suspended. Please contact support. | account_suspended | 403 | The account was suspended by an administrator |
This tool needs a paid plan. Free includes the 10 technical SEO tools; upgrade to Pro to unlock the rest. | upgrade_required | 403 | A Free account ran a tool that costs credits |
This feature requires the <Tier> plan. Your account is on <Plan>. Upgrade to unlock it. | upgrade_required | 403 | A tier-gated feature: scheduled reports, white-label branding or API keys |
Team invites require the Pro plan or higher. Your account is on Starter. | upgrade_required | 403 | The plan carries no invite allowance. It names Starter even on Free |
Team seat limit reached (<cap> members on the <Plan> plan). Upgrade for more seats. | upgrade_required | 403 | Every seat is taken, pending invites included |
AI recommendations are available on the <Plan> plan and above. Your account is on <Plan>. Upgrade to turn any result into a step-by-step action plan. | reco_upgrade_required | 403 | Recommendations need Pro by default |
You've used all <N> premium reports on the <PLAN> plan this month. Light tools (keyword research, SERP, content, technical audits) keep working. Quota resets on <date> (<in N days>). Upgrade to continue using premium reports now. | quota_exceeded | 429 | The month's allowance is spent |
Premium reports aren't included on the <PLAN> plan. Light tools (keyword research, SERP, content, technical audits) stay free. Upgrade to a paid plan to run premium reports. | quota_exceeded | 429 | The plan's allowance is zero |
You've used all <N> AI recommendations on the <PLAN> plan this month. Your quota resets on <date> (<in N days>). Upgrade to get more right away. | reco_quota_exceeded | 429 | The recommendations allowance is spent |
Hourly fair-use limit reached (100 light-tool calls/hour). This protects our infrastructure from abuse while keeping your monthly usage unlimited. Please wait ~<N> minutes for the next hourly reset. | hourly_rate_limit | 429 | Over 100 light calls in one clock hour |
Usage check temporarily unavailable, please retry. | quota_check_failed | 503 | The usage lookup failed. Nothing was charged |
Plan check temporarily unavailable, please retry. | plan_check_failed | 503 | The plan lookup failed. Nothing was charged |
Could not verify your team seat limit, please retry. | seat_check_failed | 503 | The seat count could not be read |
This tool is temporarily unavailable. Please try again shortly. | (none) | 503 | An operator switched that specific tool off |
Paste at least a couple of sentences to check. | too_short | 400 | Fewer than 25 words |
Fact-checking is not configured (the Perplexity key is not set). | not_configured | 503 | The research provider key is missing |
Weekly scheduling is available on paid plans. Upgrade to schedule automatic reports. | upgrade_required | 403 | Scheduled reports need Starter or above |
Billing is not configured yet. | stripe_not_configured | 503 | The billing key is not set in this environment |
No subscription found for this account yet. | no_customer | 400 | The address has never subscribed |
Key limit reached (5 active keys). Revoke one first. | (none) | 400 | Five API keys already exist |
Job not found or expired. Please re-submit. | (none) | 404 | A background article job older than an hour |
That host is not allowed | (none) | 400 | The address resolved to a private or reserved network |
Invalid tool type: <type> | (none) | 400 | An unrecognized tool id, rejected before anything is charged |
Error message reference carries the wider set of interface messages, including the ones that never reach an API response.
Failures without a code#
Plain validation failures return error only. They are grouped by feature in Error codes, which is the endpoint-by-endpoint catalogue. The families are:
| Family | Typical shape |
|---|---|
| Missing fields | user_email and member_email required, user_email, domain, keyword required |
| Bad tool input | Missing type, Missing url, Invalid URL, Only http(s) URLs are allowed |
| Size ceilings | Report too large — max 2MB, Too many strings (max 100 per request) |
| Team conflicts | You can't invite yourself., Already invited or this person is already a member. |
| Upstream fetch failures | The responsiveness analyzer's five 502 messages |
| Model failures | Failed to parse AI response, plus five friendly rewrites of provider errors |
| Administration | Unauthorized, Only an owner can manage customers., Unknown plan. |
Failures reported with HTTP 200#
Three places report a failure inside a successful response. Check the body, not the status.
| Where | Shape | Meanings |
|---|---|---|
| Saving a run to the Library | {"ok":false,"reason":"<reason>"} | missing_fields, no_db, unserialisable, data_too_large, failed |
| Saving a rendered report | {"ok":false} or {"ok":false,"reason":"too_large"} | The rendered HTML exceeded its ceiling |
| Pushing to a publishing target | {"ok":false,"enabled":false,…} | Publishing is not enabled yet and always refuses |
Checking for an existing saved result also always returns 200, with {"found":false} when nothing matches. That is not an error.
Thirty tools are marked as real-data tools and never invent numbers. When the provider has nothing they return 200 with noDataAvailable: true and the message No measured data was found for "<query>". This is a real-data tool, so Metric Vault shows nothing here rather than fabricating a number. Check the spelling, or try a more established domain, keyword or topic. That is a successful run with an honest empty result. See A tool returned no data.
Fail-open and fail-closed#
Knowing which way a gate falls tells you whether a 503 cost anything.
| Gate | On dependency failure | Result |
|---|---|---|
| Plan check | Closed | 503 plan_check_failed. Nothing ran, nothing was charged |
| Credit quota check | Closed | 503 quota_check_failed. Nothing ran, nothing was charged |
| Recommendations quota | Closed | 503 quota_check_failed or 503 plan_check_failed |
| Team seat check | Closed | 503 seat_check_failed |
| Billing webhook signature | Closed | 503 webhook_not_configured when the secret is unset |
| Internal job secret | Closed | 503 when unset, 401 when wrong |
| Hourly rate limiter | Open | A limiter failure allows the call rather than blocking it |
| Translation providers | Open | The original string is returned untranslated |
| Notification preferences | Open | All preferences default to on |
| Activity logging | Open | Logging never fails a request |
Retry guidance#
| Situation | Action |
|---|---|
503 with plan_check_failed or quota_check_failed | Retry with exponential backoff. Nothing was charged |
503 naming a missing provider or key | Do not retry. It needs an operator |
429 with hourly_rate_limit | Sleep for reset_in_minutes, then retry |
429 with quota_exceeded or reco_quota_exceeded | Do not retry. Wait for reset_date, or upgrade |
500 from a model call | Retry once. A second failure is worth reporting |
502 or 504 from the responsiveness analyzer | Retry once |
401 or 403 | Never retry. Fix the credential or the entitlement |
400 | Never retry. Fix the request |
Note: A retry after a 503 from a fail-closed gate cannot double-charge, because nothing was metered on the failed attempt. A retry after a 500 from a model call can, because the credit is taken before the model is called.
See also
Was this article helpful?
Thanks — feedback noted for the docs team.