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

# Troubleshooting

> How the Dataville API reports errors and what to do about each one.

Dataville uses standard HTTP status codes: `4xx` for a problem with the request or account, `5xx` for a problem on Dataville's side or with an upstream source. Every error body includes a human-readable message that is safe to show to a user. Data-source failures also include a stable `data.code` for clients to branch on.

Errors are never billed. Credit is deducted only after results are returned, and a download that fails part-way is refunded. Error responses do not count toward the anonymous request limit.

## Error format

Data lookups (`GET /{source}/{keywords}`) return errors in the same envelope as results:

```json theme={null}
{
  "status": "error",
  "account_state": "authenticated",
  "data": {
    "error": "arXiv is not responding right now, so this search could not be completed. Nothing is wrong with your query and no credit was used — please try again shortly, or pick a different source.",
    "code": "UPSTREAM_UNAVAILABLE"
  }
}
```

`data.code` is present on upstream and internal failures. Validation errors on these routes (an unsupported source, no results) use the same envelope without a `code`.

Export, SQL, and connected-sheet routes return a flat shape:

```json theme={null}
{ "status": "error", "message": "Only SELECT statements are allowed." }
```

Check the HTTP status first, then `data.code` or `error` where present.

## Data source errors

These codes appear in `data.code` on data lookups. The `UPSTREAM_*` codes come from sources Dataville fetches live: Wikipedia, arXiv, Stack Exchange, PyPI, and OpenAlex. Sources served from Dataville's own database fail as `INTERNAL_ERROR` or return a **404**.

| Code | Status | When | What to do |
| - | - | - | - |
| `UPSTREAM_RATE_LIMITED` | 503 | The source is rate-limiting Dataville. | Wait for `Retry-After` if present, otherwise a minute or two, then retry. |
| `UPSTREAM_UNAVAILABLE` | 503 | The source is not responding. | Retry shortly with backoff, or use another source. |
| `UPSTREAM_FORBIDDEN` | 502 | The source refused Dataville's request. | Use another source. Dataville is alerted automatically. |
| `UPSTREAM_BAD_RESPONSE` | 502 | The source returned something Dataville couldn't parse. | Retry shortly. |
| `SOURCE_UNAVAILABLE` | 500 | The source exists but couldn't be loaded. Rare. | Retry shortly. |
| `INTERNAL_ERROR` | 500 | Dataville failed to complete the search. | Retry with backoff. If it persists, email [support@dataville.com](mailto:support@dataville.com) with the source and keywords. |

`UPSTREAM_RATE_LIMITED` is the only error that sets a `Retry-After` header (in seconds, only when the source said how long to wait). Its message tells the caller it is not a limit on their account:

```json theme={null}
{
  "status": "error",
  "account_state": "authenticated",
  "data": {
    "error": "arXiv is refusing requests right now, so this search could not be completed. This happens on arXiv's side when their service is under load — it is not a limit on your account and not caused by anything you did. No credit was used. Try again in about 2 minutes.",
    "code": "UPSTREAM_RATE_LIMITED"
  }
}
```

## Validation errors (400)

The message states what to fix. On data lookups it is in `data.error`; elsewhere it is the flat shape. An unsupported source name returns the full list of valid names and aliases in the message.

`POST /api/v1/query` accepts `SELECT` statements only. The validator rejects `CREATE`, `DROP`, `ALTER`, `INSERT`, `UPDATE`, `DELETE`, `MERGE`, `TRUNCATE`, `COPY`, `ATTACH`, `DETACH`, `INSTALL`, `LOAD`, `EXPORT DATABASE`, `IMPORT DATABASE`, `read_csv`, `read_parquet`, `read_json`, `read_ndjson`, `GLOB`, and `postgres_scan` with `Disallowed operation: {OP}`. Matching is textual, so these words are rejected inside string literals too.

`LIMIT` is not validated. Queries are capped at 1,000 rows and the `sql_executed` field in the response shows the query that ran.

## Authentication errors (401)

Routes that require a key return **401** when none is sent. Export and SQL routes use the flat shape:

```json theme={null}
{ "status": "error", "message": "Authentication required. Include your API key in the Authorization header." }
```

Connected-sheet routes return:

```json theme={null}
{ "error": "Unauthorized", "message": "No token provided" }
```

An invalid or revoked key is handled the same way as no key: a **401** on these routes, and the anonymous allowance on data lookups. If keyed requests are hitting the anonymous rate limit, check `account_state` in the response. `"anonymous"` means the key was not accepted. See [Create an API key](/api-reference/authentication).

## Insufficient credit (402)

Returned when your credit balance can't cover the request. Nothing is charged.

```json theme={null}
{
  "status": "error",
  "error": "insufficient_credit",
  "message": "Insufficient Dataville credits. Your monthly credit is exhausted — upgrade your plan or wait for the next monthly reset.",
  "credits": { "remaining_usd": 0, "request_cost_usd": 0.005 }
}
```

Upgrade or top off from the [Billing](https://app.dataville.com/billing) page, or wait for the monthly reset. `X-Credits-Remaining` on successful responses shows the balance falling. See [Usage limits](/api-reference/usage-limits).

## Access errors (403 and 404)

These mean the thing you asked for doesn't exist or isn't yours.

| Status | When | Message |
| - | - | - |
| 404 | A data lookup found nothing. Not billed. Try broader keywords or another source. | `No results found for "{keywords}" in {source}` |
| 404 | A connected-sheet route can't find the dataset, or it belongs to another account. | `Dataset not found.` |
| 403 | `POST /api/v1/query` names a dataset in `datasetIds` that isn't yours or isn't ready. | `Dataset {id} was not found, isn't ready, or you don't have access to it.` |
| 404 | The path doesn't exist. | `{ "error": "Not Found", "message": "The requested resource {path} was not found" }` |

## Timeouts (408)

SQL queries and sheet searches are limited to 30 seconds:

```json theme={null}
{ "status": "error", "message": "Query timed out (30 s limit)" }
```

Narrow the query: fewer columns, a `WHERE` clause, or a lower `LIMIT`. Nothing is billed.

## Rate limit errors (429)

Three limits produce a **429**, each with a different body. The limits themselves are on [Usage limits](/api-reference/usage-limits).

**Anonymous request limit** (50 per 24 hours per IP):

```json theme={null}
{
  "status": "error",
  "error": "Rate limit exceeded",
  "account_state": "anonymous",
  "message": "You have exceeded the anonymous request limit, which is shared by everyone using your IP address.",
  "limit": 50,
  "remaining": 0,
  "hint": "Authenticate to raise this limit: send your API key as an \"Authorization: Bearer <key>\" header. Create one at https://app.dataville.com/api-keys."
}
```

**Export and SQL limit** (1,000 per day per account):

```json theme={null}
{ "status": "error", "message": "Export rate limit exceeded. Maximum 1,000 exports per day per account." }
```

The connected-sheet routes have a separate 1,000-per-day limit with an equivalent body. Both send `RateLimit-*` headers and `Retry-After`.

**Burst limit** (100 per 15 minutes per IP) returns a plain-text body, not JSON: `Too many requests from this IP, please try again after 15 minutes`.

Back off until `Retry-After` or the window resets, and send an API key. The anonymous limit is the one you will hit first.

## Server errors (5xx)

Unexpected failures return **500** with `{ "error": { "message": "Internal Server Error" } }`, or the `INTERNAL_ERROR` envelope on data lookups. **502** and **503** are upstream-source problems covered by the codes above. Retry with exponential backoff. If a **500** persists, email [support@dataville.com](mailto:support@dataville.com) with the request path and time.

## Stale results are not errors

When arXiv is down and Dataville holds a stored copy of the result, the request succeeds with a **200** and a `stale` notice inside `data`:

```json theme={null}
{
  "stale": {
    "message": "arxiv is unavailable right now, so this result comes from our stored copy and may be out of date or incomplete. Nothing is wrong with your query.",
    "cached_at": "2026-08-24T09:12:00Z"
  }
}
```

`cached_at` is `null` when the copy's age isn't known. Stale responses are billed normally. If no stored copy matches, you receive the `UPSTREAM_*` error instead.

## MCP errors

The [MCP endpoint](/ai-tools) reports errors at two levels:

* Tool errors are the common case. `search_dataville` returns a tool result with `isError: true` and a plain-text message mirroring the REST errors: unsupported source, with the valid names listed, no results, insufficient credit, or `Search failed for {source}: {reason}`. These arrive as HTTP **200**.
* HTTP errors come from the endpoint itself, which is stateless and POST-only. `GET` or `DELETE /mcp` returns **405**. Rate limiting returns the same **429** bodies as the REST routes, not a JSON-RPC envelope.

The `/mcp` endpoint has no authentication error. With no key, or with an invalid one, the connection runs on the anonymous allowance and its limit of 50 requests per 24 hours.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.