Skip to content

Batch API

Run up to 100 record operations atomically in a single request.

POST /api/v1/batch
Content-Type: application/json
{
"requests": [
{ "method": "POST", "url": "/api/v1/posts", "body": { "title": "a" } },
{ "method": "POST", "url": "/api/v1/posts", "body": { "title": "b" } },
{ "method": "PATCH", "url": "/api/v1/posts/<id>", "body": { "title": "x" } },
{ "method": "DELETE", "url": "/api/v1/posts/<id>" }
]
}

A batch with at least one write runs in one transaction. Any operation failing rolls back all of them, whatever the cause: a validation error, a rule denial, a conflict, or a missing record or collection (404). Either every operation succeeds or none do.

While a batch runs it has the database to itself: record writes from other requests wait until it commits or rolls back (so they can never be swept into, or rolled back with, the batch), and concurrent batches run one after another. Hooks run for every operation, as they would for the same request to the records API. Realtime events, webhooks and after* hooks for the batch’s writes fire only after COMMIT; a rolled-back batch emits none.

  • 100 operations per batch. Larger batches return 422.
  • Records-API methods only — POST/GET/PATCH/PUT/DELETE on /api/v1/<collection> or /api/v1/<collection>/<id> (/api/<collection> is accepted too). Any other path (admin, files, auth) or method fails the whole batch with 422 (Request N: …) before anything runs.
{
"data": [
{ "status": 201, "body": { ...created record... } },
{ "status": 201, "body": { ...created record... } },
{ "status": 200, "body": { ...updated record... } },
{ "status": 204, "body": null }
]
}

When a write batch fails, nothing is committed and the response is an error for the whole batch:

{
"error": "Batch failed at request 2: ...",
"code": 422,
"details": { ...validation details from the failing op... }
}

N in Batch failed at request N is the failing operation’s position, counting from 0. The HTTP status and code are that operation’s: 404 for a missing record or collection, 403 for a rule denial, 422 for a validation error, 409 for a restrict reference or a busy record, 405 for a view collection or for creating or deleting a singleton’s record, 400 for a bad filter, 500 otherwise.

A batch of only reads (GETs) changes nothing, so it doesn’t run in a transaction. It answers 200 with each operation’s status in data; a missing record is a 404 entry there, and the other reads still answer. A rule denial or a bad filter still fails the whole read batch.

{
"data": [
{ "status": 200, "body": { ...record... } },
{ "status": 404, "body": { "error": "Record not found", "code": 404 } }
]
}

The whole batch runs under a single Bearer token. Each operation is checked against its collection’s rules, as the same request to the records API would be; admins bypass rules. A denied operation fails the batch with 403.

  • Bulk imports — see CSV import for a higher-level variant.
  • Bulk deletes — the admin UI uses batch internally for the Records-page bulk-delete feature.
  • Multi-write transactions — atomic create + update sequences (e.g. insert an order + update inventory in one go).