# Errors

Every error has the same shape, on every endpoint:

```json
{
  "error": {
    "code": "insufficient_credits",
    "message": "This job needs up to 120 credits, but your account has 80.",
    "docs_url": "https://moleman.io/docs/errors#insufficient_credits"
  }
}
```

- `code` never changes; build your logic on it. `message` is for people and may get clearer over time.
- `invalid_request` also has `details`: every problem with its path, for example `{ "path": "body.rows.0.website", "message": "Too long" }`.
- Nothing is charged for a request that ends in an error.

## All codes

### `missing_api_key`

**HTTP 401.** The request has no API key.

**What to do:** Send the header Authorization: Bearer <your key>. Keys are on the API page in the app.

### `invalid_api_key`

**HTTP 401.** The API key is not one of ours (a typo, a cut-off copy, or a key from another account that was deleted).

**What to do:** Copy the whole key again. It starts with mm_live_ or mm_test_. If you lost it, create a new one; we cannot show it again.

### `revoked_api_key`

**HTTP 401.** The API key was revoked on the API page. Revoking takes effect at once.

**What to do:** Create a new key on the API page and use that one.

### `wrong_key_mode`

**HTTP 403.** A test key asked for something made with a live key, or the other way round. Test and live data never mix.

**What to do:** Use a key of the same kind (mm_test_ or mm_live_) that created the job or list.

### `invalid_request`

**HTTP 400.** A parameter is missing, has the wrong type or is out of range. `details` lists every problem with its path.

**What to do:** Fix the parameters named in `details` (see the endpoint page for the allowed values).

### `invalid_json`

**HTTP 400.** The body is not valid JSON.

**What to do:** Send a JSON body with the header Content-Type: application/json.

### `invalid_cursor`

**HTTP 400.** The cursor is not one we gave out (they are opaque; do not build them yourself).

**What to do:** Start again without `cursor`, then pass the `nextCursor` of each page unchanged.

### `invalid_webhook_url`

**HTTP 400.** The webhook URL is not a public https address (private networks and other schemes are refused).

**What to do:** Use a public https URL, for example the one your Make, Zapier or n8n webhook trigger gives you.

### `no_eligible_rows`

**HTTP 400.** None of the rows can run this action (for example: no website or domain to find an email for, or no email to check).

**What to do:** Check the rows: find-contacts needs a website or domain, verify needs an email, deep-search needs a company or website.

### `insufficient_credits`

**HTTP 402.** The account has fewer credits than this request could use at most. We hold the maximum while the job runs and give back what it does not use.

**What to do:** Buy credits or pick a plan in the app, or send fewer rows. GET /api/v1/account shows your credits.

### `not_found`

**HTTP 404.** The job or list does not exist, belongs to another account, or was deleted.

**What to do:** Check the id. Lists you deleted, and results older than 30 days that were removed, cannot come back.

### `place_not_found`

**HTTP 404.** We could not find the place you searched in.

**What to do:** Use a city, county, state or ZIP / post code that exists in the country you sent.

### `route_not_found`

**HTTP 404.** There is no endpoint at this path with this method.

**What to do:** Check the path and method against the reference. All endpoints start with /api/v1/.

### `idempotency_in_progress`

**HTTP 409.** A request with the same Idempotency-Key is still running.

**What to do:** Wait a moment and send it again with the same key; you then get the first answer.

### `payload_too_large`

**HTTP 413.** The body is larger than 20 MB.

**What to do:** Send fewer rows per request (up to 50,000 rows; 10,000 for deep-search).

### `unsupported_media_type`

**HTTP 415.** The body is not JSON.

**What to do:** Send Content-Type: application/json.

### `idempotency_key_reused`

**HTTP 422.** The Idempotency-Key was already used in the last 24 hours with a different request.

**What to do:** Use a new key for every new request. Reuse a key only to retry exactly the same request.

### `rate_limited`

**HTTP 429.** More than 120 requests in one minute with this key.

**What to do:** Wait for the number of seconds in the Retry-After header, then go on. Big lists go in one request, not one request per row.

### `server_error`

**HTTP 500.** Something broke on our side. Nothing was charged for it.

**What to do:** Retry with the same Idempotency-Key after a few seconds. If it keeps happening, write to us.

### `search_unavailable`

**HTTP 503.** Business search is not available right now.

**What to do:** Try again in a few minutes.

