> ## Documentation Index
> Fetch the complete documentation index at: https://docs.retailgrid.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Errors

> Status codes used by the Retailgrid Public API and the shape of validation errors.

## Status codes

| Status                     | Meaning                                | When you see it                                                         |
| -------------------------- | -------------------------------------- | ----------------------------------------------------------------------- |
| `200 OK`                   | Success                                | `GET`, `PATCH`, and `/bulk` responses                                   |
| `201 Created`              | Resource created                       | `POST /v1/<entity>/{entity_id}` and `POST /v1/competitor_prices`        |
| `202 Accepted`             | Job queued                             | `POST /v1/imports/*` returning a `JobHandle`                            |
| `204 No Content`           | Success, no body                       | `DELETE /v1/<entity>/{entity_id}`                                       |
| `401 Unauthorized`         | API key missing, malformed, or rotated | Auth header issue - see [Authentication](/api-reference/authentication) |
| `404 Not Found`            | The path's `entity_id` is unknown      | Patching or deleting a non-existent row                                 |
| `422 Unprocessable Entity` | Validation failed                      | Bad payload shape, missing required field, type mismatch                |
| `429 Too Many Requests`    | Rate limit hit                         | Back off and retry per `Retry-After`                                    |
| `5xx`                      | Server error                           | Retry with exponential backoff; if persistent, contact support          |

## Validation error shape

When a request fails schema validation, the API returns a `422` with an `HTTPValidationError` body:

```json theme={null}
{
  "detail": [
    {
      "loc": ["body", "items", 0, "regular_price"],
      "msg": "field required",
      "type": "missing"
    },
    {
      "loc": ["body", "items", 1, "observed_at"],
      "msg": "input should be a valid datetime",
      "type": "datetime_parsing"
    }
  ]
}
```

Each entry in `detail` is a `ValidationError`:

* `loc` - JSON path to the offending field, including array indices.
* `msg` - human-readable message.
* `type` - machine-readable error code (e.g., `missing`, `string_too_long`, `value_error`).

## Bulk endpoints: per-item errors

`/bulk` endpoints return `200 OK` even when individual rows fail. Per-row failures are reported inside `BulkResponse.results` rather than as a top-level error:

```json theme={null}
{
  "results": [
    { "index": 0, "ok": true,  "id": "1c2d..." },
    { "index": 1, "ok": false, "code": "validation_error", "message": "product_name too long" }
  ]
}
```

Always iterate `results` and check `ok`. A `200` does **not** mean every row was written.

## Common 422 cases by endpoint family

* **Products** - missing `item_id`, missing `sku`, or a decimal field that can't parse.
* **Product store** - missing `item_id`, `store_id`, or `current_price`; `store_id` referencing an unknown store.
* **Transactions** - missing `item_id`, malformed `transaction_timestamp`, decimal field that can't parse.
* **Competitor prices** - missing one of the four required fields (`competitor_name`, `observed_at`, `regular_price`, `effective_price`).
* **Imports (CSV)** - file not attached, file empty, file not UTF-8, header row missing.

## Retry strategy

* **Idempotent endpoints** (`POST /v1/<entity>/{entity_id}`, `PATCH`, `DELETE`, `/bulk`) - retry on 5xx and on transport errors with exponential backoff (start at 1s, cap at 30s, max 5 attempts).
* **`POST /v1/competitor_prices`** - this is a non-idempotent create (no path id). Retrying after a successful response will create a duplicate row. Check the network outcome before retrying.
* **Async imports** - if `POST /v1/imports/*` fails, retry the upload. If it succeeds and the subsequent job fails, fix the CSV and submit a fresh job rather than retrying the same upload.
