# Error handling

> Handle Search1API authentication, payment, validation, rate-limit, and upstream errors.

Canonical URL: https://s1.dev/docs/essentials/error-handling

Every API error is a JSON object with `ok: false`, an `error` label, and a human-readable `message`. Validation errors also include an `errors` array. Match on the status code or the `error` label, and show `message` to people.

```json
{ "error": "Not Found", "message": "Task not found: task_123", "ok": false }
```

`POST /screenshot` is the exception on success: it returns image bytes with an `image/png`, `image/jpeg`, or `image/webp` content type. Its error responses still use JSON, so check `response.ok` before choosing how to read the body.

## Status codes

| Status | Meaning | What to do |
| --- | --- | --- |
| `400` | Malformed JSON or an invalid request value handled outside schema validation | Fix the request body and headers. |
| `401` | Invalid, revoked, or malformed bearer token | Check the key and the `Authorization` header. |
| `402` | Payment challenge or insufficient account credits | Add a bearer token, complete the payment flow, or add credits. |
| `403` | Screenshot target failed URL safety or hostname-resolution checks | Use a public HTTP or HTTPS URL without credentials. Verify that its public DNS resolves before retrying. |
| `404` | A deepcrawl task was not found, or the `/crawl` target server confirmed the URL does not exist | For deepcrawl, verify the task ID before polling again. For `/crawl`, the dead link is a verified, completed answer (see below) — fix the URL instead of retrying. `/search` and `/news` no longer return `404` — zero results come back as `200` with an empty `results` array. |
| `410` | A discontinued reasoning endpoint, or the `/crawl` target server confirmed the URL was permanently removed | Remove calls to `/v1/chat/completions` and `/v1/models`. For `/crawl`, treat it like a verified dead link. |
| `422` | Request body failed schema validation | Read the `errors` array and fix the named fields. |
| `429` | The endpoint's per-key rate limit was exceeded | Wait for the `Retry-After` interval, then retry. |
| `502` | An upstream service failed or timed out | Retry with backoff. |
| `500` | An unexpected gateway or service error | Retry; contact support if it persists. |

## Understand 402 responses

A paid endpoint can return `402` for two different reasons.

### No bearer token

Search1API supports pay-per-request payment protocols. A request without a bearer token receives a payment challenge such as:

```json
{
  "type": "https://paymentauth.org/problems/payment-required",
  "title": "Payment Required",
  "status": 402,
  "detail": "Payment is required (Search1API - Search API)."
}
```

If you intended to use account credits, add `Authorization: Bearer YOUR_API_KEY`. A bearer token that is present but invalid returns `401` instead.

### Not enough credits

When a request succeeds but its final cost is higher than the remaining balance, billing replaces the success response with `402` and an insufficient-credits message. Check the balance with `GET /usage`, [top up credits](https://s1.dev/pricing#pay-as-you-go), or turn on auto top-up in the [dashboard](https://app.s1.dev/).

## Fix validation errors

Search, news and Maps validation failures return `422` with an `errors` array:

```json
{
  "ok": false,
  "message": "Query cannot be empty",
  "errors": [
    { "field": "query", "message": "Query cannot be empty", "code": "too_small" }
  ]
}
```

Common causes include an empty `query`, `max_results` below 1, or `crawl_results` greater than `max_results`. See the endpoint's API reference for its complete schema.

## Zero results vs. service failures

`/search` and `/news` distinguish between a search that finds nothing and a search that could not run:

- **Zero results**: when the search completes and the engines confirm there are no matches, the response is `200` with an empty `results` array. This is a successful, completed search and is charged normally (1 credit). Broaden the query or remove narrow site and time filters to get matches.
- **Service failure**: when the search could not be completed on our side (upstream outage, timeout, anti-bot interference), the response is `502` and the request is **not** charged. Retry with backoff.

`/crawl` follows the same principle for dead links:

- **Verified dead link**: when the target server itself confirms the URL does not exist (`404`) or was permanently removed (`410`), the crawl ran to completion and the authoritative answer is "there is nothing here". The response passes that status through with an explanatory message, and the request is charged normally (1 credit) — the same way a confirmed zero-result search is. Do not retry; fix the URL.
- **Service failure**: when we could not reach or process the target (timeout, upstream outage, blocked exit), the response is `502` and the request is **not** charged. Retry with backoff.

## Retry safely

Search, crawl, and screenshot requests have no write-side effects. Retry `429`, `502`, `503`, `504`, and transient `500` responses with exponential backoff and jitter. Do not retry `400`, `401`, `402`, or `422` until you have changed the request or account state. Do not retry a `/crawl` `404` or `410` — the target server has confirmed the URL is dead, and each retry is a new billable lookup. For a Screenshot `403`, correct the target or confirm that its public DNS resolves before retrying.

> **info:** Requests that fail because the service could not complete them are not charged. Two completed outcomes are charged even though they carry a non-`200` status or empty payload: a search that completes with zero results (`200` with an empty `results` array), and a `/crawl` whose target server confirmed the URL is dead (`404`/`410`) — both are verified answers, not failures. In a batch request, successful items — including confirmed zero-result searches — are charged, and failed items have a cost of zero.

Status semantics that changed over time (such as zero-result searches moving from `404` to `200`) are dated on the [changelog](https://s1.dev/docs/changelog).

If a `500` or `502` persists, contact [sys@search1api.com](mailto:sys@search1api.com) with the endpoint, timestamp, and a redacted request body. Never send your API key.