Aautoranko
Menu

Getting started

Errors & rate limits

Every error comes back in the same JSON envelope, with a machine-readable code to branch on. This page lists every code, the rate limit and its headers, and when a retry is safe.

Error envelope

Any response that isn't 2xx carries one error object. Branch on code; message explains the problem to a person and is the same text you see in the tables below.

Response · 404 Not Found
{
  "error": {
    "code": "not_found",
    "message": "Site not found."
  }
}
Error bodyapplication/json
  • errorobject
    What went wrong.
    error keys(2)
    • codestring
      Machine-readable error code. Branch on this.

      One ofmissing_tokeninvalid_tokenrate_limitednot_foundinvalid_bodyplan_limitinvalid_stateforbiddenfetch_failedupstream_error

    • messagestring
      Human-readable explanation.
An unexpected server failure returns HTTP 500, possibly without the envelope (an HTML page from a proxy, for example). Check the status code before parsing the body, and retry a 500 with backoff.

HTTP status codes

StatusMeaningError codes
200OKSuccess.None
201CreatedCreated: POST /sites/{id}/jobs started a job.None
400Bad RequestThe body isn't valid JSON or fails validation.invalid_body
401UnauthorizedThe token is missing, malformed, unknown or revoked.missing_tokeninvalid_token
403ForbiddenThe token is valid but not allowed to do this.forbidden
404Not FoundThe resource doesn't exist, or belongs to another account.not_found
409ConflictThe job isn't in a state that allows the action.invalid_state
422Unprocessable EntityThe target URL could not be fetched or parsed (fetch_failed on SEO toolkit endpoints).fetch_failed
429Too Many RequestsToo many requests for the token, or the plan's AI/article allowance is used up.rate_limitedplan_limit
500Internal Server ErrorUnexpected server error. The body may not use the error envelope.None
502Bad GatewayThe AI provider failed (upstream_error) — retry the call.upstream_error

Error codes

Every code the API returns, the exact messages it sends with it, and what to do.

missing_token401Unauthorized

The request has no Authorization: Bearer … header.

What to do: Send Authorization: Bearer ar_live_… with every request.

Message

  • Missing bearer token. Create one in Dashboard → API.

Returned by every endpoint.

invalid_token401Unauthorized

The token is malformed (it doesn't start with ar_live_), unknown, or revoked.

What to do: Check the token for copy-paste mistakes and stray whitespace. If it was revoked or lost, create a new one in Dashboard → API.

Messages

  • Invalid token format.
  • Invalid or revoked token.

Returned by every endpoint.

rate_limited429Too Many Requests

The token made more than 120 requests in the last 60 seconds.

What to do: Wait for the number of seconds in the Retry-After header (30), then retry. Spread requests out, or use webhooks instead of tight polling.

Message

  • Rate limit exceeded (120 requests/minute).

Returned by every endpoint.

not_found404Not Found

The site, job, article or saved SEO analysis doesn't exist, or it belongs to another account. The API doesn't reveal which.

What to do: Check the ID. List what your token can see with GET /sites, GET /sites/{id}/jobs and GET /seo/analyses.

Messages

  • Site not found.
  • Job not found.
  • Article not found.
  • Analysis not found.

Returned by GET /sites/{id}, GET /sites/{id}/articles, GET /sites/{id}/articles/{postId}, GET /sites/{id}/jobs, POST /sites/{id}/jobs, GET /jobs/{id}, POST /jobs/{id}/publish, POST /sites/{id}/keywords, GET /seo/analyses/{id}.

invalid_body400Bad Request

The request body isn't valid JSON or fails validation. message names the problem. The SEO toolkit is strict: it rejects unknown fields (suggesting the right name for a near miss), answers with HTTP 413 instead of 400 for a body over 2 MB, and also uses this code for an unknown kind in GET /seo/analyses.

What to do: Fix the body as message describes. Retrying the same request won't help.

Messages

  • Invalid JSON body.
  • topic is required.
  • topic is too long (max 300 chars).
  • scheduled_for must be an ISO 8601 datetime.
  • keywords array is required.
  • Unknown task.
  • Unknown model for this task kind.
  • The JSON body must be an object.
  • Unknown field "targetKeyword" (did you mean "target_keyword"?). Allowed fields: url, topic, target_keyword, brand, language, save.
  • url is required.
  • Request body too large (max 2 MB).

Returned by POST /sites/{id}/jobs, POST /sites/{id}/keywords, POST /seo/analyze, POST /seo/keywords, POST /seo/article, POST /seo/article-short, POST /seo/meta-tags, POST /seo/open-graph, POST /seo/schema, POST /seo/headings, POST /seo/content-brief, POST /seo/rewrite, POST /seo/alt-texts, POST /seo/robots-sitemap, POST /seo/serp-preview, GET /seo/analyses, PUT /ai/models.

plan_limit429Too Many Requests

A plan allowance is used up: the site's article allowance (Free: 3 in total; Starter: 8 per calendar month; Pro: 30 per calendar month; failed jobs don't count) when creating a job, or the account's monthly AI-call allowance (Free 300, Starter 1,500, Pro 6,000 AI calls per calendar month) on the SEO toolkit.

What to do: Upgrade the plan, or wait for the next calendar month (the Free plan's article allowance never resets). This isn't a rate limit: there's no Retry-After, and retrying won't succeed until the allowance resets.

Messages

  • Free plan article allowance used — upgrade to keep publishing.
  • Monthly article limit reached (8).
  • Monthly article limit reached (30).
  • Monthly AI call allowance reached (300/300 on the free plan). Upgrade at https://autoranko.com/pricing or wait for next month.
  • Monthly AI call allowance reached (1499/1500 on the starter plan; this endpoint needs 2 calls). Upgrade at https://autoranko.com/pricing or wait for next month.

Returned by POST /sites/{id}/jobs, POST /seo/analyze, POST /seo/keywords, POST /seo/article, POST /seo/article-short, POST /seo/meta-tags, POST /seo/open-graph, POST /seo/schema, POST /seo/headings, POST /seo/content-brief, POST /seo/rewrite, POST /seo/alt-texts.

invalid_state409Conflict

The job can't be published in its current state. Only jobs in review that have a generated article can be.

What to do: Check status with GET /jobs/{id} and publish once it's review. Jobs that are publishing, published or failed can't be published.

Message

  • Only jobs in 'review' with generated content can be published.

Returned by POST /jobs/{id}/publish.

forbidden403Forbidden

The token is valid but not allowed to do this: only admin tokens can change AI model settings.

What to do: Read the configuration with GET /ai/models. Changes are made by Autoranko administrators.

Message

  • Only admin tokens can change platform AI models.

Returned by PUT /ai/models.

fetch_failed422Unprocessable Entity

An SEO toolkit endpoint couldn't fetch or read the page at url: the domain doesn't resolve, the connection fails or takes over 15 seconds, the host is on a private network, it redirects more than 3 times, or the page answers with an HTTP status of 400 or more. message names the URL and the reason.

What to do: Check that the URL is public and returns an HTML page with a status below 400, for example by opening it in a private browser window. A 401, 403, 429 or 503 often means the server blocks crawlers: allow the AutorankoBot user agent, or retry later.

Messages

  • https://gladiacteur.com/page-introuvable/ answered HTTP 404. Check the URL.
  • https://example.com/ answered HTTP 403. The server may be blocking crawlers: allow the AutorankoBot user agent, or retry later.
  • Could not fetch https://example.com/slow-page: the server did not answer within 15 seconds.
  • Could not fetch https://www.example.invalid/: Could not resolve www.example.invalid.

Returned by POST /seo/analyze, POST /seo/keywords, POST /seo/meta-tags, POST /seo/open-graph, POST /seo/schema, POST /seo/headings, POST /seo/alt-texts.

upstream_error502Bad Gateway

The AI provider failed or returned an unusable result during a synchronous AI request: keyword research or an SEO toolkit endpoint. Nothing was saved.

What to do: Retry after a few seconds, backing off exponentially if it keeps failing. On the SEO toolkit, AI calls that completed before the failure still count toward the monthly allowance.

Messages

  • Keyword research failed, please retry.
  • The AI request failed, please retry.
  • The AI returned an incomplete result, please retry.
  • The AI returned no keywords, please retry.
  • The AI returned an incomplete outline, please retry.
  • The AI returned a truncated article, please retry.
  • The AI returned invalid JSON-LD, please retry.
  • The AI returned an empty rewrite, please retry.
  • The AI returned no alt texts, please retry.

Returned by POST /sites/{id}/keywords, POST /seo/analyze, POST /seo/keywords, POST /seo/article, POST /seo/article-short, POST /seo/meta-tags, POST /seo/open-graph, POST /seo/schema, POST /seo/headings, POST /seo/content-brief, POST /seo/rewrite, POST /seo/alt-texts.

Rate limits

requests per token
120
sliding window
60 s
Retry-After when limited
30 s
  • The limit is 120 requests per token over a sliding 60-second window. Each token has its own budget.
  • The X-RateLimit-* headers come with every successful response and with 429 rate_limited. Other error responses don't include them, except on the SEO toolkit (/seo/…), where every error after authentication does.
  • There's no remaining-requests header. Keep to about 2 requests per second on average, and when you get 429 rate_limited, wait Retry-After seconds before retrying.
  • Requests rejected with 401 don't count. Every authenticated request does, including ones that fail with 400 or 404.
  • Running out of articles is a different error, 429 plan_limit, and waiting won't fix it.

Headers

HeaderExampleDescription
X-RateLimit-Limit120Requests allowed per window for this token.
X-RateLimit-Window60sLength of the sliding window the limit is counted over.
X-RateLimit-Scopetoken-6The bucket the request was counted against: one per API token (the number is the token's internal ID).
Retry-After30Sent only with 429 rate_limited: seconds to wait before retrying.

What they look like on the wire (header names arrive in lowercase over HTTP/2):

200 OK · headers (abridged)
HTTP/2 200
content-type: application/json
x-ratelimit-limit: 120
x-ratelimit-scope: token-6
x-ratelimit-window: 60s
429 rate_limited
HTTP/2 429
content-type: application/json
retry-after: 30
x-ratelimit-limit: 120
x-ratelimit-scope: token-6
x-ratelimit-window: 60s

{
  "error": {
    "code": "rate_limited",
    "message": "Rate limit exceeded (120 requests/minute)."
  }
}

Two different 429s

429 rate_limited clears by itself: wait Retry-After seconds. 429 plan_limit means the site has used its article allowance; it has no Retry-After, and retrying won't help until the allowance resets or the plan changes.

Retries and idempotency

Retry when the request didn't take effect, or when repeating it can't do harm.

ResponseRetry?How
429 rate_limitedYesAfter the Retry-After seconds (30). The request was turned away before it did anything.
502 upstream_errorYesAfter a few seconds, doubling the wait each time. Only keyword research returns it, and nothing was saved.
500, a timeout or a network errorDependsSafe for GET requests, PUT /ai/models and POST /jobs/{id}/publish. Keyword research is safe too, though each request counts as an AI call. For POST /sites/{id}/jobs, check before retrying (below).
409 invalid_stateNoCheck the job with GET /jobs/{id}. After a retried publish, it means the first call went through.
429 plan_limitNoNot until the allowance resets (next calendar month on paid plans) or the plan is upgraded.
400 invalid_body401 invalid_token403 forbidden404 not_foundNoThe same request fails the same way. Fix the body, token or ID first.

Creating jobs isn't idempotent

POST /sites/{id}/jobs has no idempotency key: every call that reaches the server creates a job and uses an article from the allowance. If a create request times out, list the site's newest jobs with GET /sites/{id}/jobs and look for your topic before you send it again.

Publishing is safe to retry

POST /jobs/{id}/publish only acts on a job in review. If the call times out, send it again: when the first call went through, the job is already publishing or published and the retry gets 409 invalid_state instead of creating a second post. Treat that 409 as success and confirm with GET /jobs/{id}. Don't send publish calls for the same job in parallel.

Webhook deliveries can repeat

A webhook can arrive twice when a retry overlaps a slow response. Make handlers idempotent: a repeated delivery has the same event, data.id and created_at. Delivery rules

A retry helper

A small wrapper that retries the two errors that are always safe to retry, 429 rate_limited and 502 upstream_error, and throws everything else so your code can decide.

# --retry waits for Retry-After on a 429 and backs off on 5xx responses and timeouts.
# Use it for GET requests: a retried POST /sites/{id}/jobs could create a second job.
curl --retry 4 https://autoranko.com/api/public/v1/sites \
  -H "Authorization: Bearer $AUTORANKO_TOKEN"