# Errors (/api/errors)

## Error format [#error-format]

Every error has a JSON body with a `code` and a `detail`:

```json
{ "code": "INVALID_API_KEY", "detail": "Invalid API key" }
```

- `code` is a stable value in upper case. Use it in your code to select an action.
- `detail` is a text for people. It can change. Do not parse it.

Some errors add more fields:

| Code | More fields |
| --- | --- |
| `INSUFFICIENT_FUNDS` | `currency`, `missing_topup`: the top-up that permits the approval. |
| `MONTHLY_LIMIT_REACHED` | `currency`, `monthly_limit`: the monthly spend limit of the customer. |
| `ESTIMATE_CHANGED` | `estimate`: the new quote of the job. |

### Validation errors [#validation-errors]

If the request has a wrong field type, or a required field is missing, the API answers `422` with the code `VALIDATION_ERROR`. Then `detail` is a list, with one item for each error:

```json
{
  "code": "VALIDATION_ERROR",
  "detail": [
    {
      "type": "missing",
      "loc": ["query", "project_id"],
      "msg": "Field required",
      "input": null
    }
  ]
}
```

`loc` shows where the error is: the part of the request (`body`, `query` or `path`), then the field.

If the body is not valid JSON, the error has the type `json_invalid`. If the API cannot read the body at all, for example because it is not UTF-8, the API answers `400` with the code `INVALID_BODY`.

## HTTP status codes [#http-status-codes]

| Status | Meaning |
| --- | --- |
| `200 OK` | The request is complete. A create request also answers `200` when it returns an existing job instead of a new one. |
| `201 Created` | The API created a new resource. |
| `204 No Content` | The API removed the resource. There is no body. |
| `302 Found` | A redirect to a download URL. |
| `400 Bad Request` | The values in the request are not correct. |
| `401 Unauthorized` | The API key is missing, unknown or revoked. |
| `402 Payment Required` | The credit is too low. |
| `403 Forbidden` | Your role does not permit the action, or a limit stops it. |
| `404 Not Found` | The resource does not exist, or you cannot access it. |
| `409 Conflict` | The current state of a job or a website does not permit the action. |
| `422 Unprocessable Content` | The request does not agree with the schema. |
| `502 Bad Gateway` | The payment or the invoicing provider has a problem. Try again later. |
| `503 Service Unavailable` | Billing or file downloads are temporarily not available. Try again later. |

## Error codes [#error-codes]

### Authentication and access [#authentication-and-access]

| Status | Code | Cause | What to do |
| --- | --- | --- | --- |
| `401` | `NOT_AUTHENTICATED` | The request has no API key. | Send the key in the `Authorization` header. |
| `401` | `INVALID_API_KEY` | The key is unknown or revoked. | Use a valid key. |
| `401` | `USER_NOT_FOUND` | The user of the key does not exist any more. | Create a new key. |
| `403` | `INSUFFICIENT_SCOPE` | A key for one project (`PROJECT` scope) is used on an account, customer or billing endpoint. | Use a key with the `USER` scope. See [Key scopes](/api/authentication.md#key-scopes). |
| `403` | `INSUFFICIENT_ROLE` | Your role in the customer or in the project does not permit the action. | Ask an administrator for a role. See [Roles](/api/authentication.md#roles). |
| `403` | `PROJECT_ACCESS_DENIED` | You have no role in the project. | Ask an administrator of the project. |
| `403` | `PROJECT_DISABLED` | The project of a `PROJECT` key is stopped. | Contact DeepScout. |
| `403` | `MANAGED_CUSTOMER` | DeepScout manages this customer, so self-service actions are not available. | Contact your account manager. |
| `403` | `EXPORTS_NOT_ENABLED` | The project has no exports. | Contact DeepScout. |

### Not found [#not-found]

| Status | Code | Cause |
| --- | --- | --- |
| `404` | `PROJECT_NOT_FOUND` | The project does not exist, or you cannot access it. |
| `404` | `WEBSITE_NOT_FOUND` | The website is not in the project. For a matching: the website has no completed scrape in the project. |
| `404` | `JOB_NOT_FOUND` | The job does not exist, or it is in a project that you cannot access. |
| `404` | `CUSTOMER_NOT_FOUND` | The customer does not exist. For most customer endpoints, an unknown customer gets `403 INSUFFICIENT_ROLE` instead. |
| `404` | `API_KEY_NOT_FOUND` | The key does not exist, or it is not your key. |
| `404` | `INVOICE_NOT_FOUND` | The invoice does not exist. |
| `404` | `TOPUP_NOT_FOUND` | The top-up session does not exist. |
| `404` | `DATASET_NOT_FOUND` | The project has no such dataset, or the dataset has no files for one website. |
| `404` | `EXPORT_FILE_NOT_FOUND` | The export file is not written yet. |
| `404` | `SAMPLE_NOT_FOUND` | The sample of the website is not ready yet. |
| `404` | `URL_RESULTS_NOT_FOUND` | The URL scrape has no URL results yet. They are available after the settlement of the job. See [Scrape a URL list](/api/url-scrape.md#when-the-results-are-available). |
| `404` | `NOT_FOUND` | The URL does not exist. |

### Request values [#request-values]

| Status | Code | Cause |
| --- | --- | --- |
| `400` | `INVALID_WEBSITE_URL` | The website URL is not valid. |
| `400` | `INVALID_LOCALE` | The country is not a two-letter country code, or the currency is not an ISO 4217 code. |
| `400` | `INVALID_URL_LIST` | The API accepted no URL of the list, or the list is too large. Split a large list into more jobs. |
| `400` | `INVALID_MATCHING_SCOPE` | The matching websites are not correct, for example the reference website is also a competitor. |
| `400` | `INVALID_TOPUP_AMOUNT` | The top-up amount is not from 5 to 5,000 EUR. |
| `400` | `PROJECT_ID_REQUIRED` | The export download needs `project_id` with a `USER` key. |
| `400` | `PROJECT_LIMIT_REACHED` | The customer has 50 projects. |
| `400` | `JOB_NOT_APPROVABLE` | The job cannot be approved, for example it has no quote. |
| `422` | `VALIDATION_ERROR` | The request does not agree with the schema. |
| `400` | `INVALID_BODY` | The API cannot read the body, for example because it is not UTF-8. Send JSON in UTF-8. |
| `405` | `METHOD_NOT_ALLOWED` | The endpoint does not accept this HTTP method. |

### Credit and limits [#credit-and-limits]

| Status | Code | Cause | What to do |
| --- | --- | --- | --- |
| `402` | `INSUFFICIENT_FUNDS` | The spendable credit is less than the `cap` of the job, or less than the price of a paid [website analysis](/api/add-website.md#before-you-start). | Top up at least `missing_topup` (and at least 5 EUR), then send the request again. |
| `403` | `MONTHLY_LIMIT_REACHED` | The job does not fit in the monthly spend limit. | Wait until running jobs settle or the next month starts, or contact DeepScout. A top-up does not help. |

### Job and website state [#job-and-website-state]

| Status | Code | Cause | What to do |
| --- | --- | --- | --- |
| `409` | `ESTIMATE_CHANGED` | The price went up since the quote. | Read the new `estimate`. If you accept it, approve again. |
| `409` | `CATALOG_EXPIRED` | The full catalog of the website is older than 31 days. | Run a full scrape, or create the URL scrape with `urls_only`. See [Scrape a URL list](/api/url-scrape.md#when-the-catalog-is-too-old). |
| `409` | `CATALOG_DELIVERED` | Your project already got the newest full scrape of the website, and that scrape ended less than 18 hours ago. | Use the current data. |
| `409` | `JOB_NOT_AWAITING_APPROVAL` | The job is not a quote any more. For a full scrape: another full or `URLS_ONLY` scrape of the website runs in the project. | Read the job. Wait for the other scrape to end. |
| `409` | `JOB_NOT_CANCELLABLE` | The job is approved, settling or finished, or it is a free matching sample. | None. An approved job runs to its end. |
| `409` | `JOB_RUNNING` | The setup job runs, so you cannot remove it. | Wait for it to end, or cancel it. |
| `409` | `SETUP_JOB_HAS_WEBSITE` | The setup job found its website. | Remove the website instead. |
| `409` | `WEBSITE_BUSY` | A job of the website runs, so you cannot remove the website. | Wait for the job to end. |
| `409` | `MATCHING_BUSY` | Another matching of the project is not finished. This includes a quote that waits for approval. | Wait, or cancel the quote. |
| `409` | `MATCHING_SAMPLE_USED` | The project already used its free matching sample. | Run a paid matching. |
| `409` | `CONTACT_REASON_MISMATCH` | The job did not fail, or the reason is not its `contact_reason`. | Send the `contact_reason` of the failed job. |

### Server and providers [#server-and-providers]

| Status | Code | Cause |
| --- | --- | --- |
| `502` | `STRIPE_ERROR` | The payment provider has a problem. |
| `502` | `ACCOUNTING_ERROR` | The invoicing system has a problem. |
| `503` | `BILLING_UNAVAILABLE` | Billing is temporarily not available. |
| `503` | `MAINTENANCE` | File downloads are temporarily not available. |
| `500` | `INTERNAL_ERROR` | An unexpected error. |

If you get a `5xx` status, try again after some time. Increase the wait after each try.

### Other errors [#other-errors]

| Status | Code | Cause |
| --- | --- | --- |
| Any | `HTTP_ERROR` | An error that has no code of its own. Read the status and the `detail`. |

<Callout title="Codes of the console sign-in">
The `ErrorCode` list in the OpenAPI specification also has codes of the console sign-in, for example `SESSION_EXPIRED`. The API endpoints in these docs do not return them.
</Callout>
