> Searlo API Docs — Rate Limiting
> Source: https://searlo.tech/docs#rate-limiting
> Full docs: https://searlo.tech/docs/full.md

# Rate Limiting

API requests are rate-limited per user with sliding windows. **Every account gets the same limits**, free credits included: buying credits does not change them.

Limits are tracked **per endpoint group**, and each group has its own buckets:

| Group | Covers | Per Second | Per Minute | Per Hour | Per Day |
|-------|--------|-----------|-----------|---------|---------|
| `search` | `/search/*`, `/serp`, `/rankings`, AI answers, web data, async tasks | 600 | 20,000 | 400,000 | 1,800,000 |
| `local` | `/search/maps`, `/search/places`, `/search/reviews`, `/search/place-details` | 20 | 48 | 2,400 | 16,000 |
| `email_verifier` | `/email-verifier/*` | 40 | 200 | 10,000 | 40,000 |
| `mcp` | `/mcp` | 60 | 1,200 | 16,000 | 150,000 |

`local` is a tighter cap inside the search budget: a maps, places, reviews or place-details call must pass both. API-key and dashboard requests share one bucket per user. On top of the windows, an account can have at most 64 web, image, site, advanced and batch searches in flight at once (see Searches in Flight).

### Per API Key

Each API key also has its own caps, inside the account's budget: **20,000 requests a minute and 400,000 a day** by default for a new key. The dashboard shows each key's caps. They are fixed windows that start with the key's first request in each window. An account can hold several keys, and all of them draw on the account's limits above.

High-volume accounts get custom limits for the account and for each key, and reserved capacity on the SERP hosts: ask support with your expected daily volume and peak rate.

### Searches in Flight

Web, image, site, advanced and batch search (and the `GET /search` alias) also cap how many of an account's requests may run at once. Past the cap a request is refused at once with `429`, `code` `4203` and `errorCode` `CONCURRENCY_LIMIT`, a `Retry-After` of 1 second and an `X-Concurrency-Limit` header that carries the cap: 64 in flight per account, on every plan. It is refused before any credit is reserved, so it is never charged. A slot frees as soon as a response finishes: retry after the second.

```json
{
  "success": false,
  "message": "Too many searches in flight for this account (at most 64 at once). Retry in 1s.",
  "code": 4203,
  "errorCode": "CONCURRENCY_LIMIT",
  "retryable": true,
  "retryAfter": 1,
  "limit": 64
}
```

### Rate Limit Headers

Every rate-limited response carries the state of each window, and the Unix time (seconds) at which the earliest one resets:

```
X-RateLimit-Limit-Second: 600
X-RateLimit-Remaining-Second: 599
X-RateLimit-Limit-Minute: 20000
X-RateLimit-Remaining-Minute: 19987
X-RateLimit-Limit-Hour: 400000
X-RateLimit-Remaining-Hour: 399421
X-RateLimit-Limit-Day: 1800000
X-RateLimit-Remaining-Day: 1771305
X-RateLimit-Reset: 1790690460
```

### Handling Rate Limits

A `429 Too Many Requests` from the account limits has `code: 4201`:

```json
{
  "success": false,
  "message": "Rate limit exceeded. Please slow down and try again later.",
  "code": 4201,
  "retryAfter": 60,
  "limits": { "perSecond": { "limit": 600, "remaining": 0, "resetAt": 1790690000 } }
}
```

Wait `retryAfter` seconds (the same value is in the `Retry-After` header) before retrying. `limits` shows each window's state; the example is trimmed. A key's own cap answers the same way with `code: 4202` and `"message": "API key rate limit exceeded."`. The in-flight cap answers `code: 4203` (above).

A `429` can also mean a lane is full rather than a limit reached: `AT_CAPACITY` (Full SERP, AI answers, shopping), `BUSY` (SERP HTML) and `VERIFIER_BUSY` (e-mail verifier) carry a `Retry-After` too, and are not charged. See [Error Handling](#errors).

## Code Examples

### JavaScript

```javascript
async function fetchWithRetry(url, options, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    const res = await fetch(url, options);

    if (res.status === 429) {
      const retryAfter = res.headers.get("Retry-After") || 60;
      await new Promise(r => setTimeout(r, retryAfter * 1000));
      continue;
    }

    return res;
  }
  throw new Error("Max retries exceeded");
}
```

### Python

```python
import time
import requests

def fetch_with_retry(url, headers, max_retries=3):
    for i in range(max_retries):
        response = requests.get(url, headers=headers)

        if response.status_code == 429:
            retry_after = int(response.headers.get("Retry-After", 60))
            time.sleep(retry_after)
            continue

        return response

    raise Exception("Max retries exceeded")
```


---
*From https://searlo.tech/docs — see full docs at https://searlo.tech/docs/full.md*