# Ingest API 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.

**Language:** en
**Audience:** developer
**Docs index (every page):** https://staging-instasent-docs-nextjs.oscar-284.workers.dev/llms.txt
**This zone's index:** https://staging-instasent-docs-nextjs.oscar-284.workers.dev/platform-api/ingest-api/llms-full.txt
**This page:** https://staging-instasent-docs-nextjs.oscar-284.workers.dev/platform-api/ingest-api/errors/ (HTML) · https://staging-instasent-docs-nextjs.oscar-284.workers.dev/platform-api/ingest-api/errors.md (Markdown)

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`** when at least one item is accepted. `202` means **queued, not yet stored**: accepted items are stored shortly after the response, usually within a few seconds. Items that fail validation are reported in the same body, so partial failures do not block the rest of the batch:

```json
{
  "success": true,
  "status": 202,
  "errors": [
    {
      "position": 1,
      "error": "_user_id not found at item #1 (ORDER-988): USER-456 Contact with that id must be created prior sending any events",
      "item": { "_user_id": "USER-456", "_event_id": "ORDER-988", "_event_type": "purchase" }
    }
  ],
  "accepted": [
    {
      "position": 0,
      "item": { "_user_id": "USER-123", "_event_id": "ORDER-987", "_event_type": "purchase" }
    }
  ],
  "entitiesSuccess": 1,
  "entitiesFailures": 1
}
```

Each entry in `errors[]` gives the item's `position` in the array you sent, the `error` message and the `item` itself; `accepted[]` lists the items that were queued. `entitiesSuccess` and `entitiesFailures` count both. The body also carries the project, datasource and stream identifiers and usage counters (items above are trimmed; full schema in the [API Reference](/platform-api/ingest-api/reference)).

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

## Status codes

### `202 Accepted`

At least one item was accepted and queued; it is stored shortly after, usually within a few seconds. A write that depends on it and is sent immediately afterwards, such as an event for a contact you just sent, may not find it yet. Check the body's `errors[]` for items that were rejected.

### `400 Bad Request`

**Every item in the request was rejected**, so nothing was queued. The body carries the first item's message in `error`:

```json
{
  "success": false,
  "status": 400,
  "error": "_user_id not found at item #0 (ORDER-987): USER-123 Contact with that id must be created prior sending any events",
  "entitiesSuccess": 0,
  "entitiesFailures": 1
}
```

Most messages mean an item is wrong (a missing or invalid field, an unsupported event type or parameter): fix the items and resend. Two messages call for a specific response:

- **`Contact with that id must be created prior sending any events`** — the event refers to a `_user_id` that is not stored yet: the contact does not exist, or it was sent to `POST /stream/contacts` moments ago and is still being processed. Send the event with the contact embedded in `_user_data`, or retry it after a short wait (for example 1, 2 and 4 seconds). See [Sending a new contact and its event together](/platform-api/ingest-api/guide#sending-a-new-contact-and-its-event-together).
- **`_event_id already exists`** — an event with this `_event_id` and `_event_type` is already stored. It was not duplicated; treat it as delivered.

In a batch where other items were accepted, the same messages appear in `errors[]` of a `202` instead.

### `401 Unauthorized`

The token is missing, revoked or malformed. Not retryable with the same token — check the [Authentication](/platform-api/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`

The request as a whole cannot be processed: the body is empty or holds more than 100 items. Nothing was queued, and the reason is in `error`. Not retryable as-is — fix the payload first.

### `429 Too Many Requests`

You hit the rate-limit window. Retry after `X-RateLimit-Reset`. See [Rate limits](/platform-api/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 listed in `errors[]` — and use the same `_event_id` so duplicates are rejected. The `400` for an event whose contact is still being processed is worth retrying after a short wait; everything else in the `4xx` range means the request itself is wrong.

## What's next

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

---

This is one page of the Instasent documentation. For the complete machine-readable index of every guide and API reference, fetch https://staging-instasent-docs-nextjs.oscar-284.workers.dev/llms.txt — start there for full context.
