# SERP API errors: codes, causes, fixes and retries

> SerpCrypto SERP API error codes: 400, 401, 402, 429, 502 and 504, what causes each, how to fix or retry it, and why no error ever uses a credit.

- URL: https://serpcrypto.com/docs/errors/
- Last updated: 2026-10-22
- Publisher: SerpCrypto (https://serpcrypto.com)

Six error codes, one rule: an error never uses a credit. This page says what triggers each code, whether to retry, and gives retry code with exponential backoff in Python and Node.js.

**Quick answer — Which SERP API errors should I retry, and are failed searches charged?**

**SerpCrypto** never charges an error: only a successful search uses 1 credit. Retry `429 concurrency_limit`, `502 search_failed` and `504 search_timeout` with exponential backoff. Do not retry `400`, `401` or `402`: fix the parameter, the key or the balance first. Credits cost from $0.30/1K searches and never expire; payment is in crypto, with no KYC.

## What does each error code mean?

No row of this table uses a credit. 1 credit = 1 successful search request, which returns one page of results on any supported engine. A request that fails is not charged.

| HTTP | `code` | Cause | What to do | Retry? |
| --- | --- | --- | --- | --- |
| 400 | `invalid_parameter` | `q` is missing, or a value is outside the allowed list | Fix the request; the message names the parameter | No |
| 401 | `invalid_api_key` | No key, a mistyped key or a revoked key | Check the header or `api_key`; create a new key if needed | No |
| 402 | `insufficient_credits` | The account has 0 credits | Buy a pack from your balance | After buying |
| 429 | `concurrency_limit` | More searches at once than your account allows | Wait, lower parallelism, or ask support for a higher limit | Yes, with backoff |
| 502 | `search_failed` | The engine or the collection failed for this request | Retry the same request | Yes, with backoff |
| 504 | `search_timeout` | No answer in time | Retry the same request | Yes, with backoff |

## What does an error answer look like?

Every error has the same small JSON body with a stable `code` and a human-readable `message`. Branch on `code` in your program and log `message` for people. The HTTP status matches the table above.

*Body of a 402 answer*

```json
{
  "error": {
    "code": "insufficient_credits",
    "message": "Your account has no search credits left."
  }
}
```

## Why do I get 400 invalid_parameter?

A required value is missing or one value is outside the list the API accepts. The usual causes are an empty `q`, an engine name with a typo such as `google-news` instead of `google_news`, a `page` above 10, or a `device` other than `desktop` or `mobile`.

The `message` names the parameter at fault. Compare your request with the [API reference](https://serpcrypto.com/docs/search/); nothing is charged, so you can correct and resend at once.

## Why do I get 401 invalid_api_key?

The key did not reach the API, or it is not valid. Check that the header is exactly `Authorization: Bearer YOUR_KEY`, with a space after `Bearer`. Check also that your environment variable is set in the shell or service that runs the code, not only in your terminal. A revoked key keeps failing: create a new one in the dashboard.

## What should I do about 402 insufficient_credits?

Buy a credit pack from your balance, then resend. If the balance is too low, top up in crypto first; the minimum top-up is $25. Read `credits_remaining` from `GET https://serpcrypto.com/api/v1/credits` before a large job to avoid stopping halfway: that call is free.

## How do I handle 429 concurrency_limit?

Slow down rather than stop. The limit counts searches running at the same time, not searches per day. Each account can run a set number of searches at the same time; the limit is shown in your dashboard and raised on request. Read `concurrency_limit` from the credits endpoint and size your worker pool to it; the [Python](https://serpcrypto.com/docs/python/) and [Node.js](https://serpcrypto.com/docs/node/) pages show a pool that never exceeds it.

## When are 502 and 504 returned?

`502 search_failed` means the search could not be completed for this request; `504 search_timeout` means it did not complete in time. Both are free and both are worth retrying. Wait a little longer before each new attempt, as the code below does, and give up after five attempts so a stuck job does not loop for ever.

*Python: retry 429, 502 and 504 with exponential backoff*

```python
import os
import random
import time

import requests

API_URL = "https://serpcrypto.com/api/v1/search"
RETRYABLE = {429, 502, 504}


def search(params, max_attempts=5):
    headers = {"Authorization": "Bearer " + os.environ["SERPCRYPTO_API_KEY"]}
    for attempt in range(1, max_attempts + 1):
        r = requests.get(API_URL, params=params, headers=headers, timeout=90)
        if r.status_code == 200:
            return r.json()
        error = r.json().get("error", {})
        if r.status_code not in RETRYABLE or attempt == max_attempts:
            raise RuntimeError(f"{r.status_code} {error.get('code')}: {error.get('message')}")
        # exponential backoff with jitter: about 1 s, 2 s, 4 s, 8 s
        time.sleep(2 ** (attempt - 1) + random.random())
    raise RuntimeError("unreachable")


print(search({"q": "solar panel installers", "gl": "au", "hl": "en"})["search_metadata"])
```

*Node.js: the same logic with fetch*

```javascript
// retry.mjs — Node.js 18+
const API_URL = "https://serpcrypto.com/api/v1/search";
const RETRYABLE = new Set([429, 502, 504]);
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

export async function search(params, maxAttempts = 5) {
  const url = `${API_URL}?${new URLSearchParams(params)}`;
  const headers = { Authorization: `Bearer ${process.env.SERPCRYPTO_API_KEY}` };
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
    const res = await fetch(url, { headers });
    if (res.ok) return res.json();
    const body = await res.json().catch(() => ({}));
    const code = body.error?.code ?? "unknown";
    if (!RETRYABLE.has(res.status) || attempt === maxAttempts) {
      throw new Error(`${res.status} ${code}: ${body.error?.message ?? ""}`);
    }
    // exponential backoff with jitter: about 1 s, 2 s, 4 s, 8 s
    await sleep(1000 * 2 ** (attempt - 1) + Math.random() * 1000);
  }
  throw new Error("unreachable");
}

console.log((await search({ q: "solar panel installers", gl: "au", hl: "en" })).search_metadata);
```

## Should my HTTP timeout be short or long?

Long enough for a search to finish: a search runs live, so set your client timeout to 60 to 90 seconds rather than a few seconds. A client that gives up too early sends a second request while the first is still running, which wastes a concurrency slot and, if both succeed, two credits.

## Error questions

### Is a search charged if my program crashes before reading the answer?

Yes, if the API completed the search and returned 200: the credit is used when the search succeeds, not when you read it. That is another reason to set a long client timeout.

### Do retries cost extra?

Only the attempt that succeeds uses a credit. Failed attempts with 429, 502 or 504 are free, so a request that succeeds on its third try costs 1 credit in total.

### Where do I report an error that keeps coming back?

Write to support@serpcrypto.com with the `search_metadata.id` of a successful search or the time, engine and parameters of the failing one. Email replies within 12 hours, 7 days a week, including help with API integration.

---

SerpCrypto: SerpCrypto is a SERP API for developers and AI agents that returns Google and Bing results as JSON, paid in crypto with no ID upload. Prepaid search credits from $0.30/1K searches never expire. No KYC — no ID, no selfie, no documents. Pay in crypto.
