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

# Datasets API Reference

> Complete reference for datasets, specs, runs, rows, and run events

## Endpoints

| Endpoint | Method | Purpose |
| - | - | - |
| `/v1/datasets/plan` | `POST` | Plan a spec from a prompt without creating |
| `/v1/datasets` | `POST` | Create a dataset and start filling it |
| `/v1/datasets` | `GET` | List your datasets, newest first |
| `/v1/datasets/{id}` | `GET` | Retrieve a dataset's spec, status, and last run |
| `/v1/datasets/{id}` | `DELETE` | Delete a dataset and its rows |
| `/v1/datasets/{id}/rows` | `GET` | Read one page of rows |
| `/v1/datasets/{id}/populate` | `POST` | Fill the dataset again |
| `/v1/datasets/{id}/runs/{run_id}/events` | `GET` | Follow a run over Server-Sent Events |
| `/v1/datasets/{id}/runs/{run_id}/cancel` | `POST` | Stop a run |

All endpoints use `https://agent.tinyfish.ai` and require the `X-API-Key` header. Datasets belong to your personal
account. Keys for an organization are refused with `403`.

## Plan a Spec

`POST /v1/datasets/plan` returns the columns TinySet would use for a prompt, so you can review and edit them before
anything runs. Planning can take up to a minute.

| Field | Type | Required | Notes |
| - | - | - | - |
| `prompt` | `string` | Yes | What the dataset should contain, 3-2,000 characters |

The response is `{ "spec": { ... } }`. Pass that `spec` to `POST /v1/datasets`.

## Create a Dataset

`POST /v1/datasets` takes exactly one of `prompt` or `spec`. With a prompt, TinySet plans the spec in the same
request, so the response can take up to a minute. With a spec, it returns right away.

| Field | Type | Required | Notes |
| - | - | - | - |
| `prompt` | `string` | One of | What the dataset should contain, 3-2,000 characters |
| `spec` | `object` | One of | A spec to use as is; see [Spec](#spec) |
| `target_rows` | `integer` | No | Rows to fill to, 1-250. Defaults to `100` |
| `populate` | `boolean` | No | Start filling right away. Defaults to `true` |

Returns the [dataset](#dataset): `202` when a run started, `201` when `populate` is `false`.

## Spec

A spec describes the dataset's columns. `plan` returns one, and you can write or edit it yourself.

| Field | Type | Required | Notes |
| - | - | - | - |
| `name` | `string` | Yes | Dataset name, up to 200 characters |
| `description` | `string` | Yes | What each row is |
| `columns` | `array` | Yes | 1-64 columns |
| `keep_when` | `object` | No | `{ "column": "<name>" }`: keep only rows where this boolean column is `true` |

Each column:

| Field | Type | Required | Notes |
| - | - | - | - |
| `name` | `string` | Yes | Key for the cell in each row |
| `label` | `string` | No | Display name, up to 200 characters |
| `type` | `string` | Yes | `string`, `number`, `boolean`, `url`, or `date` |
| `is_primary_key` | `boolean` | Yes | At least one column must be `true` |
| `retrieval_hint` | `string` | No | Where or how to find the value, up to 2,000 characters |

Primary key columns decide when two rows are the same entity, so TinySet doesn't add duplicates.

```json theme={null}
{
  "name": "Oakland Specialty Coffee Shops",
  "description": "Specialty coffee shops located in Oakland, California",
  "columns": [
    { "name": "shop_name", "label": "Shop Name", "type": "string", "is_primary_key": true },
    { "name": "website", "label": "Website", "type": "url", "is_primary_key": false },
    {
      "name": "rating",
      "label": "Google Rating",
      "type": "number",
      "is_primary_key": false,
      "retrieval_hint": "The star rating on the shop's Google Maps listing"
    },
    { "name": "roasts_on_site", "label": "Roasts On Site", "type": "boolean", "is_primary_key": false }
  ],
  "keep_when": { "column": "roasts_on_site" }
}
```

## Dataset

| Field | Type | Notes |
| - | - | - |
| `id` | `UUID` | Dataset ID |
| `name` | `string` | Dataset name |
| `spec` | `object` | The dataset's [spec](#spec) |
| `status` | `string` | See [Statuses](#statuses) |
| `target_rows` | `integer` | Rows a populate fills to |
| `row_count` | `integer` | Rows in the dataset now |
| `run` | `object` | The latest run, or `null` if the dataset never ran |
| `created_at` | `string` | ISO 8601 timestamp |

`run` has `id`, `status`, `started_at`, and, once it ends, `finished_at`. A failed run also has `error`.

## Statuses

| Dataset `status` | Meaning |
| - | - |
| `empty` | No rows yet |
| `populating` | A run is adding rows |
| `ready` | The last run finished; rows are ready |
| `failed` | The last run failed |
| `cancelled` | The last run was cancelled |

A run's `status` is `queued`, `running`, `done`, `failed`, or `cancelled`. `queued` and `running` are in progress.

## List Datasets

`GET /v1/datasets` returns `{ "data": [...], "pagination": { "next_cursor": "...", "has_more": true } }`.

| Query | Notes |
| - | - |
| `limit` | 1-100. Defaults to `20` |
| `cursor` | The previous page's `next_cursor` |

## Read Rows

`GET /v1/datasets/{id}/rows` returns one page of rows. Cells in `data` are keyed by column `name`.

| Query | Notes |
| - | - |
| `limit` | 1-2,000. Defaults to `500` |
| `after` | The previous page's `next_cursor` |

```json theme={null}
{
  "columns": [
    { "name": "shop_name", "label": "Shop Name", "type": "string", "is_primary_key": true }
  ],
  "rows": [
    { "_id": "c1d2...", "data": { "shop_name": "Blue Bottle Coffee", "website": "https://bluebottlecoffee.com" } }
  ],
  "truncated": true,
  "next_cursor": "..."
}
```

`truncated` is `true` when more rows follow at `next_cursor`.

## Populate Again

`POST /v1/datasets/{id}/populate` starts a new run and returns the dataset with `202`. An empty body uses the defaults.

| Field | Type | Notes |
| - | - | - |
| `reset` | `boolean` | Empty the dataset before filling it. Defaults to `false` |
| `target_rows` | `integer` | Rows to fill to, 1-250. Defaults to the dataset's target |

Without `reset`, the run keeps existing rows and adds new ones until the dataset has `target_rows` rows. If it
already has that many, the run ends without adding any. Only one run per dataset can be in
progress: a second populate returns `409`.

## Event Stream

`GET /v1/datasets/{id}/runs/{run_id}/events` returns `text/event-stream`. Each message has an `event` name and a JSON
`data` line.

| Event | Data | Purpose |
| - | - | - |
| `status` | `status`, optional `error` and `finished_at` | The run's status changed |
| `row` | `row_id`, `data`, optional `deleted: true` | A row was added, or with `deleted`, removed |
| `log` | `text` | A one-line progress note |
| `done` | `{}` | Always last; the stream closes after it |

```text theme={null}
event: status
data: {"status":"running"}

event: row
data: {"row_id":"c1d2...","data":{"shop_name":"Blue Bottle Coffee","website":"https://bluebottlecoffee.com"}}

event: status
data: {"status":"done","finished_at":"2026-10-28T21:44:10Z"}

event: done
data: {}
```

Closing the stream doesn't stop the run. The SDKs' `events()` reconnect if the connection drops.

## Cancel a Run

`POST /v1/datasets/{id}/runs/{run_id}/cancel` returns `{ "run_id": "...", "status": "cancelling" }`. The event stream
confirms with a final `status` of `cancelled`. Rows added before the cancel are kept.

## Delete a Dataset

`DELETE /v1/datasets/{id}` returns `{ "id": "...", "deleted": true }`. Cancel any run in progress first; deleting
during a run returns `409`.

## Errors

Errors use the standard JSON error body with an `error.code`.

| HTTP | `error.code` | When |
| - | - | - |
| `400` | `INVALID_INPUT` | Body is not valid JSON, or a field is invalid |
| `403` | `FORBIDDEN` | The key belongs to an organization; use a personal key |
| `404` | `FEATURE_NOT_AVAILABLE` | Datasets isn't enabled for this account |
| `404` | `NOT_FOUND` | The dataset or run doesn't exist |
| `409` | `RETRY_REQUIRED` | A run is already in progress on this dataset |
| `422` | `INVALID_INPUT` | The spec passed validation but TinySet couldn't use it |
| `429` | `RATE_LIMIT_EXCEEDED` | Daily run limit reached; wait for the `Retry-After` header's seconds |
| `503` | `SERVICE_BUSY` | Datasets is temporarily unavailable; retry shortly |
| `504` | `TIMEOUT` | Planning took too long; retry, or send a `spec` |

## Limits

| Limit | Value |
| - | - |
| Runs | 3 per rolling 24 hours during the beta |
| Rows per dataset | 1-250 per populate |
| Columns | 1-64 |
| Prompt | 3-2,000 characters |
| Runs per dataset | 1 in progress at a time |
| Typical run time | 5-20 minutes |

Runs are free during the beta.


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