Handling indexing API errors
Published
Indexing API errors fall into two groups: ones the client must fix, such as a bad key (401), invalid payload or low balance (422 on v1, 402 on v2) and unknown jobs (404), and temporary ones, such as rate limiting (429) and server errors (500). Only the temporary group should be retried, with a delay.
Two families of error
When a call to an indexing API fails, the first question is whether sending it again could ever succeed. A wrong key, a malformed body or a job ID from another account will fail the same way every time; the fix is in the client or the account. A rate limit or a temporary server problem will clear by itself, and a delayed retry is correct. Mixing the two up produces the classic failure modes: a tight loop hammering a 401, or a script that gives up on a 429.
The tables below list what the IndexChex API returns, verbatim. IndexChex publishes this handbook; the endpoint reference is on its API documentation.
v1 responses
v1 errors carry a message field. Validation errors add an errors object keyed by field name.
| HTTP | Body | Cause | Retry? |
|---|---|---|---|
| 401 | {"message": "Invalid API credentials."} | Missing, wrong or revoked key | No |
| 404 | {"message": "Resource not found."} | Unknown job ID, a job owned by another user, or a wrong path | No |
| 422 | {"message": "...", "errors": {"urls": ["..."]}} | Validation: missing urls, empty entries, more URLs than allowed, name over 255 characters, checker outside 0 to 5 | No; fix the body |
| 422 | {"message": "Insufficient credits. You need N credits but only have M available."} | Balance too low for the job | No; top up first |
| 422 | {"message": "No valid URLs provided"} | None of the entries is a valid web address | No |
| 422 | {"message": "Report not ready. Check status and retry later."} | Check report requested before the job completed | Yes, after polling |
| 429 | {"message": "Too many requests. Please wait and retry."} | Over 120 requests per minute | Yes, after Retry-After |
| 500 | {"message": "Unable to create index submission job at this time."} | Temporary failure on submission create | Yes, after confirming no job exists |
| 500 | {"message": "Unable to create index check job at this time."} | Temporary failure on check create | Yes, after confirming no job exists |
Successful create calls return 202; status, report and balance calls return 200.
v2 responses
v2 keeps the SpeedyIndex envelope, so errors carry a numeric code alongside the HTTP status. The exception is authentication, which returns the same 401 body as v1 with no code.
| HTTP | code | message | Cause |
|---|---|---|---|
| 401 | none | Invalid API credentials. | Bad key |
| 402 | 1 | Insufficient credits. You need N credits but only have M available. | Balance too low |
| 404 | 404 | Task not found. | Unknown task ID or one owned by another user |
| 422 | 3 | Invalid request payload. plus errors | Validation failure on create, status, report or single-URL calls |
| 422 | 3 | Unsupported search engine. | Engine other than google |
| 422 | 3 | Unsupported task type. | Type other than indexer or checker |
| 422 | 3 | Report not ready. Check status and retry later. | Task or its scheduled check still running |
| 422 | 3 | Report not available for this task. | Indexer task created without checker |
| 429 | 2 | Too many requests. Please retry shortly. | Rate limit |
| 500 | 2 | The server is currently unavailable. Please try again later. | Temporary failure on task create |
The single-URL endpoint POST /v2/google/url returns a bare {"code": 2} with HTTP 500 on a temporary failure and {"code": 0} on success. Migration notes for v2 are in migrating a SpeedyIndex integration.
A Python handler
import time, requests
class PermanentError(Exception): pass
class RetryLater(Exception): pass
def call(method, url, headers, retries=5, **kw):
for attempt in range(retries):
r = requests.request(method, url, headers=headers, timeout=30, **kw)
if r.status_code in (200, 202):
return r.json()
body = r.json() if r.headers.get("content-type", "").startswith("application/json") else {}
msg = body.get("message", r.text[:200])
if r.status_code == 429:
time.sleep(int(r.headers.get("Retry-After", 30)))
continue
if r.status_code >= 500:
if method == "POST":
raise RetryLater(f"create may or may not have succeeded: {msg}")
time.sleep(2 ** attempt * 5)
continue
if r.status_code == 422 and "not ready" in msg.lower():
raise RetryLater(msg)
raise PermanentError(f"{r.status_code}: {msg} {body.get('errors', '')}")
raise RetryLater(f"gave up after {retries} attempts")
Three decisions are built in. Rate limits are waited out using the server's Retry-After header. Server errors on reads are retried with exponential back-off, but on create calls they are raised, because the job may already exist. And "report not ready" is surfaced as a retry-later signal rather than a failure, so the caller can go back to polling.
Avoiding errors in the first place
- Validate before sending. Trim, deduplicate and drop blank lines; keep
nameshort; cap each job at 10,000 URLs, or 1,000 for instant. Rate limits and batching has a chunking helper. - Check the balance first.
GET /v1/balancereturns{"balance": N}. Compare it with the URL count (times 60 for instant, plus 1 per URL for a scheduled check) before creating a job. - Send
Accept: application/json. Without it, some validation failures may not come back as JSON. - Store job IDs immediately. Most 404s in practice come from IDs lost or mixed up between accounts.
Errors that are not errors
A finished index check can list URLs under failed_links. Those lookups could not complete; the API call itself succeeded. Likewise, a completed submission with URLs that later prove unindexed is not a failure of the request: IndexChex guarantees Googlebot crawling, and indexing remains Google's decision. How to read those results is covered in checking index status via an API and submitting URLs via an API.
Related
Error semantics are a fair test when comparing backlink indexer APIs and the wider category of backlink indexer software. The IndexChex entry lists the account-level API facts.
FAQ
Should I retry a 500 on a create call?
Only after checking whether the job exists. List recent jobs in the dashboard or, on v2, with the list endpoint, and resubmit only if nothing was created. A blind retry can create a duplicate job and charge twice.
Why does a job I can see in the dashboard return 404 over the API?
Jobs belong to the user that created them. A key from a different account, or a typo in the job ID, returns the same 404 so that job IDs from other accounts are never confirmed.
Is a URL in failed_links an error?
Not an API error. The job finished; that URL's lookup could not complete. Check it again later instead of treating it as not indexed.
What does code 3 mean on v2?
The request could not be accepted as sent: an invalid payload, an unsupported engine or task type, too many URLs, or a report that is not ready or not available. The message field says which.
Terms used on this page
Sources
Cite this entry
IndexChex. (2026, October 8). Handling indexing API errors. backlinkindexersoftware.com. https://backlinkindexersoftware.com/api-error-handling/