# Search errors and limits

> PacketStream Search API error codes and what to do about each, which requests are billed, rate limits, timeouts, and how to retry safely.

**Early beta.** Fields, limits, and availability may change during the beta. These pages describe what the API serves today. Questions or problems? [Contact support](https://packetstream.io/contact-us/).

## Error format

Errors are JSON with a stable `code` and a readable `message`:

```json
{
  "request_id": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5f",
  "error": {
    "code": "unsupported_option",
    "message": "country is not supported for type scholar"
  }
}
```

Handle errors by `code`. Messages explain the problem but can change. Every error also carries the request ID in the `X-Request-Id` header.

## Error codes

| Code | HTTP | Meaning | Does retrying help? |
| --- | --- | --- | --- |
| `invalid_request` | 400 | The JSON or query string is malformed, or a field is missing, unknown, repeated, mistyped, or out of range. The message names the field. | No. Fix the request. |
| `unsupported_option` | 400 | The type does not accept an option you sent, or no search source can serve this combination right now. | No. Remove the option or change the type. |
| `unauthorized` | 401 | The API key is missing, malformed, revoked, or unknown. | No. Check the key. |
| `insufficient_balance` | 402 | Your balance is below the cost of one search ($0.0025), or other requests spent it while this one ran. | After you add funds. |
| `method_not_allowed` | 405 | The path does not accept this HTTP method. The `Allow` header lists the methods it does accept. | No. Use a listed method. |
| `request_too_large` | 413 | The POST body or GET query string is over 4,096 bytes, or a batch body is over 16 KiB. | No. Shorten the request. |
| `rate_limited` | 429 | More than 5 requests per second, with bursts of 10, for this key. Search, batch items, fetch, and MCP tool calls share the limit. | Yes, after the `Retry-After` header. |
| `internal_error` | 500 | An unexpected server error. Rarely, the search was charged just before the error. | Retry once. If a duplicate charge matters, check your balance with `GET /v1/account` first. |
| `search_unavailable` | 503 | No search source returned a usable answer. | Yes, with backoff. |
| `capacity_exceeded` | 503 | Search capacity is temporarily full. | Yes, with backoff. |
| `search_disabled` | 503 | Search is temporarily turned off. | Yes, later. |

The Fetch API has [its own codes](https://packetstream.io/docs/fetch-api/errors-limits/). For errors inside MCP tool calls, see the [MCP reference](https://packetstream.io/docs/mcp/reference/#errors).

## Billing

- Only successful searches are billed, at $0.0025 each for every type and option.
- Errors are free, with one rare exception: a `500 internal_error` can follow a charge that went through just before the failure, for example when a database connection drops while the charge is recorded. Your balance, from `GET /v1/account` or the dashboard, is authoritative. The dashboard's recent requests can show such a request as not charged.
- A successful search that finds no results is billed.
- A batch is billed for each successful item.
- The MCP server bills each successful `packetstream_search` or `packetstream_fetch` call. Listing tools is free.

## Rate limits

- Each key may send 5 requests per second, with bursts of up to 10. Searches, batch items, fetch requests, and MCP tool calls share this limit. `GET /v1/account` does not count against it.
- Requests rejected during validation do not use the limit.
- Over the limit, the API returns `429 rate_limited` with `Retry-After: 1`.
- The limit is tracked on each API server, so `X-RateLimit-Remaining` is approximate and the effective limit can be somewhat higher. Do not depend on going over the nominal limit.

## Timeouts

- The server finishes or gives up on every request within 25 seconds. Most searches take a few seconds.
- Use a client timeout of at least 35 seconds. If your client gives up first, the search can still finish and be billed. Check the dashboard's recent requests before you retry an expensive job.

## Retrying safely

- Retry a `429` after the `Retry-After` delay.
- Retry a `503` with exponential backoff and some random jitter.
- Retry a `500` at most once.
- Do not retry a `400`, `401`, `402`, `405`, or `413` until you change the request, the key, or your balance.
