# Errors

The Ingest API uses standard HTTP status codes plus a partial-success body for batched writes. This page explains each code and how to retry safely.

Every response carries a meaningful HTTP status code. A `2xx` means the batch was accepted for processing; anything else signals a problem your client should handle explicitly. Error bodies, when present, are JSON.

## Success shape

Batched writes (`POST /stream/contacts`, `POST /stream/events`) return **`202 Accepted`** with a per-item outcome so partial failures do not block the rest of the batch:

```json
{
  "entity": {
    "successful": 2,
    "failed": 1,
    "items": [
      { "status": "success", "_user_id": "USER-1" },
      { "status": "success", "_user_id": "USER-2" },
      { "status": "error",   "_user_id": "USER-3", "errors": { "_email": ["Invalid format"] } }
    ]
  }
}
```

Inspect `items[]` before assuming the whole batch landed. Failed items are safe to retry once fixed.

## Status codes

### `202 Accepted`

The batch is accepted for processing. Check the body's `items[]` for per-record outcomes — some may still have validation errors.

### `400 Bad Request`

The request body is malformed or has the wrong shape (not an array, wrong types, exceeds 100 items). Not retryable — fix the payload first.

```json
{ "message": "Body should be an array" }
```

### `401 Unauthorized`

The token is missing, revoked or malformed. Not retryable with the same token — check the [Authentication](/ingest-api/authentication) guide.

### `404 Not Found`

The `project` or `datasource` in the URL does not exist, or the token is not authorized to write to it.

### `422 Unprocessable Entity`

**Every item in the batch failed validation.** The body lists per-item errors with the same shape as a partial-success `202`. Not retryable as-is — fix the items and resubmit.

### `429 Too Many Requests`

You hit the rate-limit window. Retry after `X-RateLimit-Reset`. See [Rate limits](/ingest-api/rate-limits).

### `500 Internal Server Error`

Something broke on our side. Retry with exponential backoff; if the failure persists, contact support with the request id.

## Retry policy

> **Tip**: Retry `429` and `5xx` with exponential backoff, starting at 1 s and capping at a minute or so. For `202` with partial failures, only retry the items that came back with `status: "error"` — and use the same `_event_id` so duplicates are discarded. Everything else in the `4xx` range means the request itself is wrong.

## What's next

- **[Rate limits](/ingest-api/rate-limits)** — how the `X-RateLimit-*` headers work.
- **[API Reference](/ingest-api/reference)** — per-endpoint responses and schemas.
