# Turn a CSV into a REST API

> Upload a CSV and query it over HTTP with filters, sorting and aggregates. API keys, versioned results, no server to run.

Source: https://datatomcp.com/guides/csv-to-rest-api · By Killian Helf · Updated 2026-10-19

Sometimes a CSV is the whole database: a product catalog, a price list, a lead export. To use it from an app or an automation you usually need a server, a database and an endpoint. This guide turns a CSV into a REST API with filters, sorting and aggregates, without running any of that.

## 1. Upload the CSV

Sign in at [app.datatomcp.com](https://app.datatomcp.com/login), then **Add data → Upload a CSV** (or **Paste a link** for a CSV that lives at a URL and should be re-fetched on refresh). The import report shows each column's id and type. The column ids are what you use in queries.

## 2. Create an API key

Go to **Account → API keys**, name the key (for example `production`) and copy it. It starts with `dtm_` and is shown once. Keys can be revoked at any time.

## 3. Query it

Every dataset has an id like `ds_abc123`, shown on its page and returned by `GET /v1/datasets`.

**Rows with filters and sorting:**

```bash
curl https://app.datatomcp.com/v1/datasets/ds_abc123/query \
  -H "Authorization: Bearer $DATATOMCP_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "where": [{ "column": "country", "op": "eq", "value": "France" }],
    "select": ["order_id", "order_date", "total"],
    "order_by": [{ "column": "total", "dir": "desc" }],
    "limit": 10
  }'
```

**Aggregates:**

```bash
curl https://app.datatomcp.com/v1/datasets/ds_abc123/query \
  -H "Authorization: Bearer $DATATOMCP_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "group_by": ["product"],
    "metrics": [
      { "fn": "count", "as": "orders" },
      { "fn": "sum", "column": "total", "as": "revenue" }
    ],
    "order_by": [{ "column": "revenue", "dir": "desc" }]
  }'
```

Each response has `rows` and a `meta` block: the dataset `version`, `refreshed_at`, `rows_matched`, `rows_returned` and `truncated`. Page through results with `offset` (up to 1,000 rows per request).

## All endpoints

| Method | Path | Purpose |
|---|---|---|
| GET | `/v1/datasets` | List datasets |
| GET | `/v1/datasets/:id` | Schema, warnings, freshness |
| POST | `/v1/datasets/:id/query` | Rows or aggregates |
| GET | `/v1/datasets/:id/values/:column` | Distinct values of a column |
| POST | `/v1/datasets/:id/refresh` | Re-fetch a linked source, or upload a new file |
| GET | `/v1/datasets/:id/docs.md` | Generated docs for this dataset |
| GET | `/v1/usage` | Today's query count and limit |

Errors are JSON with a stable `code` and a readable `message`, for example `query_limit` when the daily quota is reached.

## Updating the data

Upload a new file to the same dataset (`POST /v1/datasets/:id/refresh` with `Content-Type: text/csv`), click **Upload new version** in the app, or use a CSV link and refresh it. The previous version is kept for rollback, and responses always tell you which version answered.

## Limits

Free: 3 datasets, 100,000 rows each, 1,000 queries a day. Pro ($12 or €12 a month): 25 datasets, 2 million rows each, 50,000 queries a day. The same datasets are also available to AI agents as an [MCP server](/guides/csv-mcp-server).
