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

> ## Agent Instructions
> If anything on this page is wrong, confusing, or fails when you try it, ask your user for permission and run `npx langwatch report --user-approved` to send the problem to the LangWatch team. No login is needed and secrets are redacted locally. `npx langwatch report --help` has the details.

# Datasets: programmatic access from SDKs, CLI and MCP

> Manage datasets from the Python and TypeScript SDKs, the CLI and the MCP server for offline evaluations and automated workflows.

The SDKs, the CLI and the MCP server list, create, read, update and delete datasets and their records. Every call needs a LangWatch API key.

## Setup

> [tab] Python
>
> ```python theme={null}
> import langwatch
>
> # Initialize the SDK (or set LANGWATCH_API_KEY environment variable)
> langwatch.setup()
> ```

> [tab] TypeScript
>
> ```typescript theme={null}
> import { LangWatch } from "langwatch";
>
> const langwatch = new LangWatch();
> ```

> [!NOTE]
>
> If you are using a **service API key** (e.g. for CI/CD or multi-project setups), you must also set
> the `LANGWATCH_PROJECT_ID` environment variable so the SDK knows which project to access. You can
> find the project ID in your project settings.

## List Datasets

Retrieve all datasets for your project with pagination support.

> [tab] Python
>
> ```python theme={null}
> import langwatch
>
> # List all datasets (first page, default limit)
> result = langwatch.dataset.list_datasets()
>
> for ds in result.data:
>     print(f"{ds.name} ({ds.slug}) - {len(ds.columnTypes)} columns")
>
> print(f"Page {result.pagination.page} of {result.pagination.totalPages}")
>
> # List with explicit pagination
> result = langwatch.dataset.list_datasets(page=2, limit=10)
> ```

> [tab] TypeScript
>
> ```typescript theme={null}
> import { LangWatch } from "langwatch";
>
> const langwatch = new LangWatch();
>
> // List all datasets (first page, default limit)
> const result = await langwatch.datasets.list();
>
> for (const ds of result.data) {
>   console.log(`${ds.name} (${ds.slug}) - ${ds.columnTypes.length} columns`);
> }
>
> console.log(`Page ${result.pagination.page} of ${result.pagination.totalPages}`);
>
> // List with explicit pagination
> const page2 = await langwatch.datasets.list({ page: 2, limit: 10 });
> ```

## Create a Dataset

Create a new dataset with an optional column schema.

> [tab] Python
>
> ```python theme={null}
> import langwatch
>
> # Create with name and column types
> info = langwatch.dataset.create_dataset(
>     "User Feedback",
>     columns=[
>         {"name": "input", "type": "string"},
>         {"name": "output", "type": "string"},
>     ],
> )
> print(f"Created: {info.name} (slug: {info.slug})")
>
> # Create with just a name (columns can be added later)
> info = langwatch.dataset.create_dataset("Simple Dataset")
> ```

> [tab] TypeScript
>
> ```typescript theme={null}
> import { LangWatch } from "langwatch";
>
> const langwatch = new LangWatch();
>
> // Create with name and column types
> const info = await langwatch.datasets.create({
>   name: "User Feedback",
>   columnTypes: [
>     { name: "input", type: "string" },
>     { name: "output", type: "string" },
>   ],
> });
> console.log(`Created: ${info.name} (slug: ${info.slug})`);
>
> // Create with just a name
> const simple = await langwatch.datasets.create({ name: "Simple Dataset" });
> ```

## Get a Dataset

Fetch a dataset by slug or ID, including all its entries.

> [tab] Python
>
> ```python theme={null}
> import langwatch
>
> # Fetch dataset by slug or ID
> dataset = langwatch.dataset.get_dataset("your-dataset-slug")
>
> # Access entries
> for entry in dataset.entries:
>     print(entry.id, entry.entry)
>
> # Convert to pandas DataFrame for easy manipulation
> df = dataset.to_pandas()
> print(df.head())
> ```

> [tab] TypeScript
>
> ```typescript theme={null}
> import { LangWatch } from "langwatch";
>
> const langwatch = new LangWatch();
>
> // Fetch dataset by slug or ID
> const dataset = await langwatch.datasets.get("your-dataset-slug");
>
> // Access entries
> for (const entry of dataset.entries) {
>   console.log(entry.entry);
> }
> ```

## Update a Dataset

Update a dataset's name or column types.

> [tab] Python
>
> ```python theme={null}
> import langwatch
>
> # Update the name
> updated = langwatch.dataset.update_dataset("my-dataset", name="New Name")
> print(f"New slug: {updated.slug}")
>
> # Update column types
> updated = langwatch.dataset.update_dataset(
>     "my-dataset",
>     columns=[{"name": "question", "type": "string"}, {"name": "answer", "type": "string"}],
> )
> ```

> [tab] TypeScript
>
> ```typescript theme={null}
> import { LangWatch } from "langwatch";
>
> const langwatch = new LangWatch();
>
> // Update the name
> const updated = await langwatch.datasets.update("my-dataset", {
>   name: "New Name",
> });
> console.log(`New slug: ${updated.slug}`);
>
> // Update column types
> const withCols = await langwatch.datasets.update("my-dataset", {
>   columnTypes: [
>     { name: "question", type: "string" },
>     { name: "answer", type: "string" },
>   ],
> });
> ```

## Delete a Dataset

Archive a dataset by slug or ID.

> [tab] Python
>
> ```python theme={null}
> langwatch.dataset.delete_dataset("my-dataset")
> ```

> [tab] TypeScript
>
> ```typescript theme={null}
> await langwatch.datasets.delete("my-dataset");
> ```

## List Records

Retrieve records from a dataset with pagination.

> [tab] Python
>
> ```python theme={null}
> import langwatch
>
> # List records (first page, default limit)
> result = langwatch.dataset.list_records("my-dataset")
>
> for record in result.data:
>     print(record.id, record.entry)
>
> print(f"Total: {result.pagination.total}")
>
> # List with explicit pagination
> result = langwatch.dataset.list_records("my-dataset", page=2, limit=20)
> ```

> [tab] TypeScript
>
> ```typescript theme={null}
> import { LangWatch } from "langwatch";
>
> const langwatch = new LangWatch();
>
> // List records (first page, default limit)
> const result = await langwatch.datasets.listRecords("my-dataset");
>
> for (const record of result.data) {
>   console.log(record.id, record.entry);
> }
>
> console.log(`Total: ${result.pagination.total}`);
>
> // List with explicit pagination
> const page2 = await langwatch.datasets.listRecords("my-dataset", {
>   page: 2,
>   limit: 20,
> });
> ```

## Create Records

Batch-add records to an existing dataset.

> [tab] Python
>
> ```python theme={null}
> import langwatch
>
> records = langwatch.dataset.create_records(
>     "my-dataset",
>     entries=[
>         {"input": "What is LangWatch?", "output": "An LLM observability platform."},
>         {"input": "How do I get started?", "output": "Install the SDK and call setup()."},
>     ],
> )
> for r in records:
>     print(f"Created record: {r.id}")
> ```

> [tab] TypeScript
>
> ```typescript theme={null}
> import { LangWatch } from "langwatch";
>
> const langwatch = new LangWatch();
>
> const records = await langwatch.datasets.createRecords("my-dataset", [
>   { input: "What is LangWatch?", output: "An LLM observability platform." },
>   { input: "How do I get started?", output: "Install the SDK and call setup()." },
> ]);
> for (const r of records.data) {
>   console.log(`Created record: ${r.id}`);
> }
> ```

## Update a Record

Update (or upsert) a single record by ID.

> [tab] Python
>
> ```python theme={null}
> import langwatch
>
> record = langwatch.dataset.update_record(
>     "my-dataset",
>     "rec-1",
>     entry={"input": "updated question", "output": "updated answer"},
> )
> print(f"Updated: {record.id} -> {record.entry}")
> ```

> [tab] TypeScript
>
> ```typescript theme={null}
> import { LangWatch } from "langwatch";
>
> const langwatch = new LangWatch();
>
> const record = await langwatch.datasets.updateRecord("my-dataset", "rec-1", {
>   input: "updated question", output: "updated answer",
> });
> console.log(`Updated: ${record.id}`);
> ```

## Delete Records

Batch-delete records by their IDs.

> [tab] Python
>
> ```python theme={null}
> import langwatch
>
> deleted_count = langwatch.dataset.delete_records(
>     "my-dataset",
>     record_ids=["rec-1", "rec-2"],
> )
> print(f"Deleted {deleted_count} records")
> ```

> [tab] TypeScript
>
> ```typescript theme={null}
> const result = await langwatch.datasets.deleteRecords("my-dataset", ["rec-1", "rec-2"]);
> console.log(`Deleted ${result.deletedCount} records`);
> ```

## Upload a File

Upload a CSV, JSON, or JSONL file to a dataset. If the dataset does not exist, it is created automatically.

> [tab] Python
>
> ```python theme={null}
> import langwatch
>
> # Upload to existing or create new (default: append)
> result = langwatch.dataset.upload("my-dataset", file_path="data.csv")
> print(f"Created {result.recordsCreated} records")
>
> # Replace all records (delete existing, then upload)
> result = langwatch.dataset.upload("my-dataset", file_path="data.csv", if_exists="replace")
>
> # Error if dataset already exists (create-only)
> result = langwatch.dataset.upload("my-dataset", file_path="data.csv", if_exists="error")
> ```
>
> The `if_exists` parameter controls how conflicts are handled:
>
> | Value | Behavior |
> | - | - |
> | `"append"` (default) | Append rows to the existing dataset, or create it if it doesn't exist |
> | `"replace"` | Delete all existing records first, then upload. Creates the dataset if it doesn't exist |
> | `"error"` | Raise an error if the dataset already exists. Creates it otherwise |

> [tab] TypeScript
>
> ```typescript theme={null}
> import { LangWatch } from "langwatch";
>
> const langwatch = new LangWatch();
>
> // Upload to existing or create new (default: append)
> const file = new File([csvContent], "data.csv", { type: "text/csv" });
> const result = await langwatch.datasets.upload("my-dataset", file);
> console.log(`Created ${result.recordsCreated} records`);
>
> // Replace all records (delete existing, then upload)
> await langwatch.datasets.upload("my-dataset", file, { ifExists: "replace" });
>
> // Error if dataset already exists (create-only)
> await langwatch.datasets.upload("my-dataset", file, { ifExists: "error" });
> ```
>
> The `ifExists` parameter controls how conflicts are handled:
>
> | Value | Behavior |
> | - | - |
> | `"append"` (default) | Append rows to the existing dataset, or create it if it doesn't exist |
> | `"replace"` | Delete all existing records first, then upload. Creates the dataset if it doesn't exist |
> | `"error"` | Throw an error if the dataset already exists. Creates it otherwise |

## Using with Evaluations

Datasets are commonly used to run offline evaluations against your LLM or agent.

> [tab] Python
>
> ```python theme={null}
> import langwatch
>
> langwatch.setup()
>
> # Fetch dataset
> df = langwatch.dataset.get_dataset("your-dataset-slug").to_pandas()
>
> # Initialize evaluation
> evaluation = langwatch.experiment.init("my-evaluation")
>
> for index, row in evaluation.loop(df.iterrows()):
>     # Run your LLM/agent
>     output = my_llm(row["input"])
>
>     # Log evaluation metrics
>     evaluation.log("response_quality", index=index, score=0.9)
> ```

> [tab] TypeScript
>
> ```typescript theme={null}
> import { LangWatch } from "langwatch";
>
> const langwatch = new LangWatch();
>
> // Fetch dataset
> const dataset = await langwatch.datasets.get("your-dataset-slug");
>
> // Initialize evaluation
> const evaluation = await langwatch.experiments.init("my-evaluation");
>
> await evaluation.run(
>   dataset.entries.map((e) => e.entry),
>   async ({ item, index }) => {
>     // Run your LLM/agent
>     const output = await myLLM(item.input);
>
>     // Log evaluation metrics
>     evaluation.log("response_quality", { index, score: 0.9 });
>   },
>   { concurrency: 4 }
> );
> ```

## Dataset Entry Structure

Each dataset entry contains:

| Field | Description |
| - | - |
| `id` | Unique identifier for the entry |
| `entry` | The actual data (e.g., `input`, `expected_output`, `contexts`) |
| `datasetId` | ID of the parent dataset |
| `projectId` | ID of the project |
| `createdAt` | Timestamp of creation |
| `updatedAt` | Timestamp of last update |

Which fields you get back depends on the operation. `list_records` returns all six. `create_records` returns `id`, `entry` and `createdAt`. In Python, `get_dataset` returns entries with `id` and `entry` only.

## Typed Datasets (TypeScript)

You can define types for your dataset entries for better type safety:

```typescript theme={null}
import { LangWatch } from "langwatch";

const langwatch = new LangWatch();

type MyDatasetEntry = {
  input: string;
  expected_output: string;
  contexts?: string[];
};

const dataset = await langwatch.datasets.get<MyDatasetEntry>("my-dataset");

// Now entry.entry is typed as MyDatasetEntry
for (const entry of dataset.entries) {
  console.log(entry.entry.input);  // Typed as string
  console.log(entry.entry.expected_output);  // Typed as string
}
```

## CLI

The `langwatch` CLI has the same operations. Set `LANGWATCH_API_KEY` and run:

```bash theme={null}
langwatch dataset list
langwatch dataset create "User Feedback" --columns input:string,output:string
langwatch dataset get user-feedback
langwatch dataset update user-feedback --name "Support feedback"
langwatch dataset upload user-feedback ./rows.csv --if-exists append
langwatch dataset download user-feedback --format jsonl
langwatch dataset delete user-feedback

langwatch dataset records list user-feedback --page 1 --limit 20
langwatch dataset records add user-feedback --file ./records.json
langwatch dataset records update user-feedback RECORD_ID --json '{"input": "..."}'
langwatch dataset records delete user-feedback RECORD_ID
```

A service API key also needs `LANGWATCH_PROJECT_ID`, so the CLI knows which project to use. A project API key from the project settings page already carries the project.

`--if-exists` takes `append` (default), `replace` or `error`. `records add` reads a JSON array from `--json`, `--file` or `--stdin`. Every command accepts `--format json` for machine-readable output.

## MCP Tools (AI Coding Agents)

If you're using an AI coding agent (Claude Code, Cursor, etc.) with the [LangWatch MCP server](https://langwatch.ai/docs/integration/mcp.md), dataset tools are available directly:

| Tool | Description |
| - | - |
| `platform_list_datasets` | List all datasets with record counts |
| `platform_get_dataset` | Get dataset metadata, columns, and record preview |
| `platform_create_dataset` | Create a new dataset with optional column definitions |
| `platform_update_dataset` | Update dataset name or column types |
| `platform_delete_dataset` | Archive a dataset |
| `platform_list_dataset_records` | List the records of a dataset, one page at a time |
| `platform_create_dataset_records` | Add records in batch (max 1000) |
| `platform_update_dataset_record` | Update a single record |
| `platform_delete_dataset_records` | Delete records by IDs |

The `platform_list_datasets`, `platform_get_dataset` and `platform_list_dataset_records` tools support a `format` parameter, use `"json"` for raw data or `"digest"` (default) for AI-readable markdown.

## Finding Your Dataset Slug or ID

Every call accepts the dataset slug or its ID.

1. Go to the **Datasets** page and click your dataset.
2. The URL reads `app.langwatch.ai/{project}/datasets/{id}`; the ID starts with `dataset_`.
3. The slug is shown under the **Name** field when you open **Edit columns**, and in the `slug` field of every SDK and CLI response.


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