> This page is for Developer Documentation.

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.benchling.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.benchling.com/_mcp/server.

# What's new in v3

The V3 REST API is designed to provide consistent, complete coverage across all Benchling types, in accordance with [FAIR principles](https://www.benchling.com/blog/applying-fair-principles-to-data-management-with-benchling). The REST API, V3 Events, and the [V3 Python SDK](/docs/getting-started-with-the-v3-sdk) are all powered by the Benchling Domain Graph, which is what keeps coverage and naming consistent across developer interfaces. It also lets us apply platform-wide frameworks, such as generic batch writes and field selection, uniformly across the API rather than endpoint by endpoint.

> **Info**
>
> ### Use V3 for new development
>
> V3 has broader coverage than V2, a `stable` tier with a 90-day breaking-change notice, and features that V2 does not have: batch writes, field selection, and typed filters. New endpoints ship to V3 first.
>
> For Python, use the [V3 Python SDK](/docs/getting-started-with-the-v3-sdk). V2 and V3 can be used side by side in the same integration.

## Base path

All V3 endpoints are served from a single base path on your tenant:

**`Base path`**

```text title="Base path"
https://{tenant_name}.benchling.com/api/v3
```

## Stability

V3 has two stability levels. They track the state of the underlying product: a generally available feature typically has a `stable` API, while features in beta or Limited Availability expose `beta` endpoints.

|                           | **stable**             | **beta**                    |
| ------------------------- | ---------------------- | --------------------------- |
| Base path                 | `/api/v3`              | `/api/v3`                   |
| How to access             | Default                | `EARLY-ACCESS: true` header |
| Breaking change frequency | Rare                   | Frequent                    |
| Breaking change window    | 90 days                | No notice                   |
| Recommended environments  | prod, test, dev, other | test and dev only           |

To call a beta operation, send the `EARLY-ACCESS` header:

**`Endpoint`**

```text title="Endpoint"
GET /api/v3/entry/etr_8rVKW0g7/parts/items
EARLY-ACCESS: true
```

Beta operations are open to everyone; the header is all they need. If a request to a documented endpoint returns `404`, check whether the operation is beta and retry with the header.

For the full guarantees, notice periods, and how breaking changes are communicated, see the [V3 Stability Guidelines](/docs/stability-guidelines).

## Authentication

Authenticate exactly as you do for V2. Tokens are issued from `/oauth/token`, which sits at the root of your tenant and is deliberately not tied to a versioned API path, so the same token works for V2 and V3:

**`Endpoint`**

```text title="Endpoint"
POST /oauth/token
```

The endpoint supports the `client_credentials`, `authorization_code`, and `refresh_token` grant types. Send the resulting token as a bearer token on every V3 call. For the full walkthrough of app credentials, delegated auth, and personal API keys, see [Authentication](/docs/authentication).

## The Python SDK

The [V3 Python SDK](/docs/getting-started-with-the-v3-sdk) provides typed access to the V3 API:

```sh
pip install benchling-v3-sdk-python
```

**`Python`**

```python title="Python"
from benchling_v3_sdk import Benchling

benchling = Benchling(
    base_url="https://{tenant_name}.benchling.com",
    client_id="<your-client-id>",
    client_secret="<your-client-secret>",
)

for container in benchling.container.list():
    print(container.id, container.name)
```

Pass `early_access="true"` to the client to call beta endpoints. See [Getting Started with the V3 Python SDK](/docs/getting-started-with-the-v3-sdk) for API key, client credentials, and bearer token authentication, and [Common SDK Interactions and Examples](/docs/common-sdk-interactions-and-examples-v3) for pagination, async tasks, error handling, and retries.

The [V2 Python SDK](/docs/getting-started-with-the-sdk) is a separate package, `benchling-sdk`, that calls V2 endpoints. Both SDKs can be used in the same integration.

### Calling V3 without the SDK

Any HTTP client works:

**`Python`**

```python title="Python"
import requests

BASE_URL = "https://{tenant_name}.benchling.com/api/v3"
access_token = "..."  # obtained from POST /oauth/token

response = requests.get(
    f"{BASE_URL}/container/items",
    headers={
        "Accept": "application/json",
        "Authorization": f"Bearer {access_token}",
    },
    params={"sort": "modifiedAt:desc", "pageSize": 25},
    timeout=30,
)
response.raise_for_status()

page = response.json()
for container in page["items"]:
    print(container["id"], container["name"])
```

## Listing endpoints

Every collection exposes an `/items` listing endpoint that returns a consistent envelope: an `items` array plus a `nextToken` for pagination.

> **Note**
>
> In V2, listing endpoints wrap results in a key named after the resource, such as `results`. In V3 the key is always `items`, on every listing endpoint.

**`Endpoint`**

```text title="Endpoint"
GET /api/v3/container/items?sort=modifiedAt:desc&pageSize=2
```

**`Example Response`**

```json title="Example Response"
{
  "items": [
    {
      "__typename": "Container",
      "id": "con_dU8Qh1Wn",
      "name": "Tube 1",
      "barcode": "BC-000123",
      "archived": false,
      "archiveReason": null,
      "createdAt": "2026-08-14T17:22:03.481219+00:00",
      "modifiedAt": "2026-08-27T09:04:55.102338+00:00",
      "creator": { "__typename": "User", "id": "ent_yXrL3BjX" },
      "project": { "__typename": "Project", "id": "src_hh9ffHqV" },
      "schema": { "__typename": "ContainerSchema", "id": "ts_9kPqLmXe" },
      "parentStorage": { "__typename": "Box", "id": "box_R2vNtQ4d" },
      "quantity": {
        "__typename": "Measurement",
        "value": 500,
        "unit": { "__typename": "Unit", "id": "unit_uL" }
      }
    }
  ],
  "nextToken": "eyJ2YWx1ZSI6ICIyMDI2LTA4LTI3VDA5OjA0OjU1LjEwMjMzOCJ9"
}
```

Every object carries a `__typename`, which identifies its concrete type. This matters wherever a field can hold more than one kind of object: `parentStorage` on a container may be a Box, Plate, or Location, and `__typename` is how you tell which one you received.

### Pagination

Pass `pageSize` to control page length; it defaults to 50 and is capped at 100. Pass the `nextToken` from a response as the `nextToken` parameter of your next request to continue from where you left off.

### Field selection

Listing and get endpoints accept two mutually exclusive parameters that control which top-level fields come back. Use them to cut response size when you only need a few fields:

* `returning`: a comma-separated list of top-level fields to include.
* `omit`: a comma-separated list of top-level fields to exclude.

**`Endpoint`**

```text title="Endpoint"
GET /api/v3/container/items?returning=id,name,barcode
```

### Filtering

V3 filters are typed and encode the operator in the parameter name rather than in the value, so they need no special escaping. The exact set varies by endpoint; the reference lists them per operation. Common shapes:

* `id.anyOf`, `name.anyOf`: match any value in a comma-separated list.
* `createdAt.gt`, `createdAt.gte`, `createdAt.lt`, `createdAt.lte`: timestamp comparisons. `modifiedAt` supports the same four.
* `parentStorage.eq`: match a single referenced object by ID.
* `sort`: `createdAt` or `modifiedAt`, with `:asc` or `:desc`. Defaults to `modifiedAt:desc`.

## Writes

### Single-object writes

Creates are a `POST` to the collection; updates are a `PATCH` to the individual resource:

**`Endpoint`**

```text title="Endpoint"
POST /api/v3/container
PATCH /api/v3/container/con_dU8Qh1Wn
```

### Batch and bulk writes

Write endpoints expose batch operations that act on many objects in one request, using a `:action` suffix on the collection:

**`Endpoint`**

```text title="Endpoint"
POST  /api/v3/container:batch-create
PATCH /api/v3/container:batch-update
```

Batch operations take their input inline as JSON, which makes them the right choice for most multi-object writes. Note that the method follows the operation: creates are `POST`, updates are `PATCH`. Some resources additionally support `POST :batch-delete`.

For data sets too large to send inline, the bulk operations `POST :bulk-create` and `PATCH :bulk-update` accept a file instead. Bulk is an asynchronous, file-based flow: upload a JSONL file of input objects through the scratch file endpoints, start the operation with that file's ID, then poll for results:

**`Endpoint`**

```text title="Endpoint"
GET /api/v3/tasks/bulk/{task_id}
GET /api/v3/tasks/bulk/{task_id}/successful-results
GET /api/v3/tasks/bulk/{task_id}/errors
```

Each line of the input file is one JSON object with a `request_id` you choose and an `input_object` shaped like the body of the resource's single-object `POST`. Results and errors are keyed by your `request_id`. A single bulk request accepts up to 30,000 input objects or a 100 MB file, whichever is lower, and only one bulk mutation per type can run at a time. See [Importing Data with the Bulk Import API](/docs/bulk-import) for the full flow.

The same `:action` convention is used for operations that are neither plain creates nor updates, such as `POST /api/v3/container:transfer` and `POST /api/v3/container/{container_id}:remove-content`.

## Nested listings

When a property is a list of other objects, V3 exposes it as a nested listing endpoint with the parent's ID as a path parameter, rather than embedding the whole list in the parent response. This keeps parent payloads bounded and lets you paginate the children. For example:

**`Endpoint`**

```text title="Endpoint"
GET /api/v3/project/{project_id}/collaborations/items
GET /api/v3/entry/{entry_id}/parts/items
GET /api/v3/aa-sequence/{aa_sequence_id}/annotations/items
```

## Rate limits

Each V3 request draws from the calling user's or app's own budget and from a budget shared by the whole tenant. When a budget is exhausted, the API returns `429`, sometimes with a `Retry-After` header saying how long to wait. See [V3 Rate Limits](/docs/v3-rate-limits) for how budgets work and how to handle rejected requests.

## What V3 adds

Beyond the platform-wide features above, some capabilities exist only in V3:

* **Collaboration and Policy APIs.** Manage permissions programmatically through `/collaboration` and `/policy`, including per-project collaborations.
* **Notebook document APIs.** Read and edit entry content. Currently beta; see [Introduction to Documents](/docs/introduction-to-documents).
* **Procedures APIs.** Interact with BioProcess and Studies, including flowcharts and method execution.
* **Entity schema, results bulk write, and audit log APIs.** Available as beta endpoints.
* **Bulk import and export.** Write at scale from a file, or export entire data types as serialized JSON through an asynchronous task. See [Bulk Import](/docs/bulk-import) and [Bulk Export](/docs/bulk-export).
* **V3 events.** Webhook notifications for changes to V3 resources. See [V3 Events](/docs/v3-events). Beta.
* **Agents API.** Send prompts to Benchling AI and poll for the result. See [AI Agents API](/docs/agents-api). Private beta.

## Frequently asked questions

### Can I use V2 and V3 at the same time?

Yes. Mixing the two is expected and supported, and both accept the same token. Use the highest-stability version of each endpoint you need: if an operation exists only in V3 beta, use it alongside stable V2 calls; if an operation is not in V3 yet, use V2.

### Should I migrate an existing V2 integration?

There is no requirement to. V2 remains supported, and V2 endpoints are not being retired as part of the V3 rollout. Migrate when you want a specific V3 capability such as broader coverage, batch writes, or field selection, or when you are already changing the code for other reasons. Build anything new against V3.

### How do I keep up with breaking changes?

Subscribe to the [changelog RSS feed](/docs/changelog.rss). Many teams pipe it into Slack or Teams with an RSS integration ([Slack RSS app](https://slack.com/help/articles/218688467-Add-RSS-feeds-to-Slack), [Teams RSS app](https://rss.app/blog/how-to-add-rss-feeds-to-channels-in-microsoft-teams-QwFCa1)). Breaking changes to `stable` endpoints come with at least 90 days' notice; beta endpoints can change without notice.