Batch API
Run up to 100 record operations atomically in a single request.
POST /api/v1/batchContent-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>" } ]}Atomicity
Section titled “Atomicity”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.
Limits
Section titled “Limits”- 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 with422(Request N: …) before anything runs. - No lists in a batch that writes. A batch with at least one write can’t
contain a list (
GET /api/v1/<collection>). It fails with422naming the list before anything runs. Send lists in their own batch of reads. A get by id still works in a write batch. - Expand is capped as on the records API: 6 levels and 20 paths, or
400.
Response
Section titled “Response”{ "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.
A list on a singleton collection is checked against its view_rule, as a
read of its record is everywhere else.
An op on a view with parameters answers
400: read it directly. So does every op when the batch request’s own URL
carries params[...].
For non-admins, a hidden collection
(list, view and create rules all admin-only; for a singleton, view and
update; for a view, list and view) answers like a missing one: a 404 entry in a read batch, and a
404 failure in a write batch.
Writes return the written record only if the caller’s view rule admits it.
Otherwise the op’s body is null.
Rate limiting
Section titled “Rate limiting”A batch is charged one rate-limit token per operation, from the bucket that
operation would use on its own. A batch larger than a rule’s limit (for
example 100 creates against a limit of 60) goes through only when that bucket
is full, and leaves it in debt: the client then waits as long as it would
have for the same operations sent one by one. Batching never gets more
requests past a limit. An op’s URL is matched as the batch runs it: a
relative api/v1/posts or a trailing slash (/api/v1/posts/) is still a
create, charged to the create rule.
Use cases
Section titled “Use cases”- 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).