---
title: "AI Content Drop API Error Codes | AI Content Drop"
description: "Every error code the AI Content Drop API returns across REST, GraphQL, and MCP, with what causes it and what to do next."
canonical: "https://aicontentdrop.com/docs/errors"
source: "https://aicontentdrop.com/docs/errors"
---

# AI Content Drop API Error Codes

Errors are always JSON, on every surface and every status code. An HTML error page from this API would be a bug.

```json
{ "error": { "code": "insufficient_credits", "message": "This generation needs 22 credits; the account holds 4." } }
```

## The codes

Codes are lower snake_case. Branch on `error.code`, never on the message text — the message is written for a human reading a log and may be reworded.

| Code | HTTP | Cause | What to do |
| --- | --- | --- | --- |
| `invalid_request` | 400 | Missing or malformed field | Fix the body; the message names the field |
| `invalid_json` | 400 | Body is not parseable JSON | Send a complete object with `Content-Type: application/json` |
| `missing_query` | 400 | `/ask` called with no question | Supply `q` (GET) or `query` (POST) |
| `unauthorized` | 401 | No key, or a revoked or malformed one | Read `WWW-Authenticate`; see [authentication](https://aicontentdrop.com/docs/authentication) |
| `unauthenticated` | 401 | Session surface reached without a session | Use `/v1`; most of it needs no credential |
| `invalid_client` | 401 | Agent client id or secret does not match | Re-register at `/agent/auth/register` |
| `forbidden` | 403 | Authenticated, but not your object | Do not retry |
| `not_found` | 404 | No such job or document | Re-read the catalogue |
| `unknown_endpoint` | 404 | No API endpoint at that path | `GET /v1` lists the surface |
| `unknown_model` | 404 | No such model id | Re-read `/v1/models`; check id normalisation |
| `method_not_allowed` | 405 | Right path, wrong verb | The message names the verbs that work |
| `insufficient_credits` | 402 | Balance below the model cost | Top up, or pick a cheaper model |
| `idempotency_conflict` | 409 | Same `Idempotency-Key`, different body | Use a new key |
| `payload_too_large` | 413 | Body over the endpoint's limit | Split the work; see `POST /v1/batch` |
| `query_too_large` | 413 | GraphQL query over 8,000 characters | Split the query |
| `rate_limited` | 429 | Too many requests | Wait `Retry-After` seconds |
| `safety_rejected` | 400 | Content-safety gate refused the prompt | Rewrite the prompt; nothing was charged |
| `unsupported_operation` | 400 | Operation not available in a batch | Call the endpoint directly |
| `operation_failed` | 400 | One batch item failed | Read that item's own `error` |
| `provider_error` | 502 | Upstream model provider failed | Retry is reasonable; nothing was charged |
| `internal_error` | 500 | Our bug | Retry once, then tell us |

### The two envelopes

Agent-facing paths — `/v1`, `/graphql`, `/ask`, `/agent/*`, `/mcp`, `/.well-known/*` — always answer with `{ "error": { "code", "message" } }`.

The two generation endpoints are path aliases onto the handlers the website itself calls, so a validation failure from **those two** still carries the website's older `{ "error": "message" }` string shape. A client that reads `error.code` when `error` is an object and falls back to `error` when it is a string handles both. Everything else on `/v1` is the structured envelope.

## What a failure costs

Nothing. Billing is post-deduct: credits are charged when a generation **succeeds**. A safety refusal, a provider failure, and a job that times out all cost zero, which is why there is no refund endpoint in this API — there is nothing to refund.

A job that fails after it started shows `status: "failed"` with `creditsUsed: 0` and an `error_message` you can read.

## In GraphQL

Two channels, deliberately. Request-level failures use the standard top-level `errors` array with the same code in `extensions.code`. Failures you can act on come back as typed `UserError` values inside the payload, so a partial success is still a success. See [the GraphQL docs](https://aicontentdrop.com/docs/graphql).

## In MCP

A tool that fails returns a normal result with `isError: true` and the reason as text, rather than a JSON-RPC error — the model needs to read the message to correct its own call. Protocol-level problems (unknown method, malformed message) use JSON-RPC error codes.
