# Errors

Every error type, its status code, and whether it is billed.

## Error shape

Every error, on every endpoint, has the same envelope:

```json
{
  "error": {
    "type": "unprocessable_target",
    "message": "This profile is private or has been deleted.",
    "requestId": "req_9k2mm4xd"
  }
}
```

`requestId` rides along on every response, success or failure - include it when you contact support.

## Error types

| Status | Type | When | Billed |
|---|---|---|---|
| 400 | `invalid_request` | Malformed body, unknown field, or a bad URL format | No |
| 401 | `invalid_api_key` | Missing, malformed, or revoked key | No |
| 402 | `insufficient_credits` | The key is valid, but your balance cannot cover the call | No |
| 404 | `not_found` | The batch job referenced by `job_id` does not exist | No |
| 422 | `unprocessable_target` | The target profile, company, or post is not found, private, deleted, or unreachable | Yes - everywhere, including `profiles/activity` and each batch item |
| 429 | `rate_limited` | Over the request, search, or concurrent-batch limit; honor `Retry-After` | No |
| 500 | `internal_error` | Something broke on our side | No |
| 504 | `upstream_timeout` | The live scrape did not finish in time | No |
| n/a | `upstream_error` | Another upstream scrape failure, distinct from a timeout | No |

> **Info:** `upstream_error` covers an upstream scrape failure other than a timeout. It's never billed, and safe to retry the same as `upstream_timeout`.

## Retries

429, 500, and 504 (and `upstream_error`) are safe to retry - outside a batch job, none of them are billed. Use exponential backoff with jitter, and treat `Retry-After` as a floor, not a fixed delay.

`POST /v1/batch` accepts an `Idempotency-Key` header so a retried submission returns the original job instead of creating a duplicate - see [Batch jobs](https://up2data.ai/docs/v1/guides/batch-jobs/).

## Batch items fail individually

A batch job never fails as a whole because one item did. Each entry in `results` or `failures` carries its own status and its own `operation`. A `profile`, `company`, `post`, or Sales Navigator search item was already billed in full at submission: a success, an `unprocessable_target` failure, and a `500` or `504` failure all bill the same - 1 credit per enrich item, or a `max_results`-sized reservation per Sales Navigator search item - and the only refund is the unused part of that reservation, once the search runs. Every other operation item reserves the same worst case at submission but settles once its queue finishes: a failed item (`unprocessable_target`, or a company or job that isn't found) costs 0 and refunds in full, the same as an empty result. Items still processing affect neither count. See [Credits](https://up2data.ai/docs/v1/guides/credits/) for the full rate table and [Rate limits](https://up2data.ai/docs/v1/guides/rate-limits/) for the quota headers.

