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

# Batch API

> Execute multiple API requests in a single transaction

The Batch API allows you to execute multiple API requests atomically within a single database transaction. This is useful for maintaining data consistency across related operations and reducing network round trips.

<Info>
  All requests in a batch are executed sequentially within a single database transaction. If any request fails, all changes are rolled back.
</Info>

## Execute batch requests

Submit multiple requests to be executed atomically in a single transaction.

```bash theme={null}
POST /api/batch
```

**Authentication:** Optional (inherited by all batch requests)

### Request body

<ParamField body="requests" type="array" required>
  Array of internal request objects to execute. Each request object contains:
</ParamField>

<ParamField body="requests[].method" type="string" required>
  HTTP method: `POST`, `PATCH`, `PUT`, or `DELETE`
</ParamField>

<ParamField body="requests[].url" type="string" required>
  API endpoint URL (relative path starting with `/api/`)
</ParamField>

<ParamField body="requests[].body" type="object">
  Request body data (for POST, PATCH, PUT requests)
</ParamField>

<ParamField body="requests[].headers" type="object">
  Custom headers for this specific request (Authorization headers are ignored - auth is inherited from the parent request)
</ParamField>

### Response

Returns an array of results for each request in the batch.

<ResponseField name="body" type="any">
  Response body from the individual request
</ResponseField>

<ResponseField name="status" type="number">
  HTTP status code from the individual request
</ResponseField>

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST http://127.0.0.1:8090/api/batch \
    -H "Authorization: Bearer TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
      "requests": [
        {
          "method": "POST",
          "url": "/api/collections/posts/records",
          "body": {
            "title": "First Post",
            "content": "Hello world"
          }
        },
        {
          "method": "POST",
          "url": "/api/collections/posts/records",
          "body": {
            "title": "Second Post",
            "content": "Another post"
          }
        }
      ]
    }'
  ```

  ```json Response theme={null}
  [
    {
      "status": 200,
      "body": {
        "id": "RECORD_ID_1",
        "collectionId": "posts",
        "collectionName": "posts",
        "title": "First Post",
        "content": "Hello world",
        "created": "2024-01-15T10:00:00.000Z",
        "updated": "2024-01-15T10:00:00.000Z"
      }
    },
    {
      "status": 200,
      "body": {
        "id": "RECORD_ID_2",
        "collectionId": "posts",
        "collectionName": "posts",
        "title": "Second Post",
        "content": "Another post",
        "created": "2024-01-15T10:00:00.000Z",
        "updated": "2024-01-15T10:00:00.000Z"
      }
    }
  ]
  ```
</CodeGroup>

## Supported operations

The Batch API supports the following record operations:

### Create record

```json theme={null}
{
  "method": "POST",
  "url": "/api/collections/{collection}/records",
  "body": { /* record data */ }
}
```

### Update record

```json theme={null}
{
  "method": "PATCH",
  "url": "/api/collections/{collection}/records/{id}",
  "body": { /* updated fields */ }
}
```

### Upsert record

```json theme={null}
{
  "method": "PUT",
  "url": "/api/collections/{collection}/records",
  "body": {
    "id": "RECORD_ID",
    /* record data */
  }
}
```

<Note>
  PUT (upsert) operations automatically determine whether to create or update based on whether the record ID exists. If the ID exists, it updates; otherwise, it creates a new record.
</Note>

### Delete record

```json theme={null}
{
  "method": "DELETE",
  "url": "/api/collections/{collection}/records/{id}"
}
```

## Use cases

### Atomic operations

Ensure multiple related records are created or updated together, or none at all:

<CodeGroup>
  ```bash Create user with profile theme={null}
  curl -X POST http://127.0.0.1:8090/api/batch \
    -H "Content-Type: application/json" \
    -d '{
      "requests": [
        {
          "method": "POST",
          "url": "/api/collections/users/records",
          "body": {
            "username": "john_doe",
            "email": "john@example.com",
            "password": "secure_password",
            "passwordConfirm": "secure_password"
          }
        },
        {
          "method": "POST",
          "url": "/api/collections/profiles/records",
          "body": {
            "user": "@request.data.requests.0.body.id",
            "bio": "Software developer",
            "avatar": ""
          }
        }
      ]
    }'
  ```

  ```bash Update related records theme={null}
  curl -X POST http://127.0.0.1:8090/api/batch \
    -H "Authorization: Bearer TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
      "requests": [
        {
          "method": "PATCH",
          "url": "/api/collections/orders/records/ORDER_ID",
          "body": {
            "status": "completed"
          }
        },
        {
          "method": "PATCH",
          "url": "/api/collections/inventory/records/ITEM_ID",
          "body": {
            "stock": 45
          }
        }
      ]
    }'
  ```
</CodeGroup>

### Reducing round trips

Submit multiple independent operations in a single request:

```bash theme={null}
curl -X POST http://127.0.0.1:8090/api/batch \
  -H "Authorization: Bearer TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "requests": [
      {
        "method": "DELETE",
        "url": "/api/collections/tasks/records/TASK_1"
      },
      {
        "method": "DELETE",
        "url": "/api/collections/tasks/records/TASK_2"
      },
      {
        "method": "DELETE",
        "url": "/api/collections/tasks/records/TASK_3"
      }
    ]
  }'
```

## File uploads

When uploading files in batch requests, use `multipart/form-data`:

<CodeGroup>
  ```bash With files theme={null}
  curl -X POST http://127.0.0.1:8090/api/batch \
    -H "Authorization: Bearer TOKEN" \
    -F '@jsonPayload={
      "requests": [
        {
          "method": "POST",
          "url": "/api/collections/posts/records",
          "body": {
            "title": "Post with image"
          }
        }
      ]
    }' \
    -F 'requests.0.image=@/path/to/image.jpg'
  ```
</CodeGroup>

<Note>
  When using multipart/form-data:

  * Put regular fields in `@jsonPayload` as serialized JSON
  * Name file fields as `requests.N.fieldName` or `requests[N].fieldName` where N is the request index
</Note>

## Error handling

If any request in the batch fails, the entire transaction is rolled back and no changes are persisted.

<CodeGroup>
  ```json Error response theme={null}
  {
    "code": 400,
    "message": "Failed to create record.",
    "data": {
      "requests": {
        "1": {
          "code": "batch_request_failed",
          "message": "Batch request failed.",
          "response": {
            "code": 400,
            "message": "Failed to create record.",
            "data": {
              "title": {
                "code": "validation_required",
                "message": "Missing required value."
              }
            }
          }
        }
      }
    }
  }
  ```
</CodeGroup>

The error response indicates:

* Which request in the batch failed (index in the `requests` object)
* The specific error from that request in the `response` field

## Configuration

The Batch API must be enabled in your PocketBase settings. Configure these options:

<ParamField body="batch.enabled" type="boolean" default={false}>
  Enable or disable batch requests globally
</ParamField>

<ParamField body="batch.maxRequests" type="number" default={10}>
  Maximum number of requests allowed per batch
</ParamField>

<ParamField body="batch.maxBodySize" type="number" default={134217728}>
  Maximum total body size in bytes (default: 128 MB)
</ParamField>

<ParamField body="batch.timeout" type="number" default={3}>
  Transaction timeout in seconds
</ParamField>

<Warning>
  If batch requests are disabled or the timeout is reached, the request will fail and return a 403 Forbidden or timeout error.
</Warning>

## Limitations and best practices

### Limitations

1. **Supported operations only** - Only record create, update, upsert, and delete operations are supported
2. **No nested batches** - Cannot include batch requests within a batch
3. **Sequential execution** - Requests execute sequentially, not in parallel
4. **Shared authentication** - All requests inherit auth from the parent batch request
5. **Timeout constraints** - Long-running batches may timeout (default: 3 seconds)

### Best practices

1. **Keep batches small** - Use batches for related operations, not bulk data imports
2. **Handle errors gracefully** - Prepare for all-or-nothing transaction behavior
3. **Monitor timeout** - Ensure batch operations complete within the configured timeout
4. **Use for consistency** - Ideal for maintaining referential integrity across collections
5. **Consider alternatives** - For large bulk operations, use individual requests or direct database access

### Performance considerations

* Batch requests execute within a database transaction, which locks tables
* Each request in the batch is validated and executed sequentially
* File uploads in batches count toward the total body size limit
* Transaction timeout prevents long-running operations from blocking the database

## Common errors

| Code | Description                                              |
| ---- | -------------------------------------------------------- |
| 400  | Invalid batch request data or one of the requests failed |
| 403  | Batch requests are disabled or not allowed               |
| 408  | Batch transaction timeout exceeded                       |
| 413  | Request body exceeds maximum size limit                  |

## Example: Multi-step workflow

Create a complete blog post with tags and metadata atomically:

```bash theme={null}
curl -X POST http://127.0.0.1:8090/api/batch \
  -H "Authorization: Bearer TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "requests": [
      {
        "method": "POST",
        "url": "/api/collections/posts/records",
        "body": {
          "title": "Getting Started with PocketBase",
          "slug": "getting-started-pocketbase",
          "content": "PocketBase is an open-source backend...",
          "published": true
        }
      },
      {
        "method": "POST",
        "url": "/api/collections/tags/records",
        "body": {
          "name": "tutorial",
          "post": "@request.data.requests.0.body.id"
        }
      },
      {
        "method": "POST",
        "url": "/api/collections/tags/records",
        "body": {
          "name": "pocketbase",
          "post": "@request.data.requests.0.body.id"
        }
      },
      {
        "method": "POST",
        "url": "/api/collections/post_stats/records",
        "body": {
          "post": "@request.data.requests.0.body.id",
          "views": 0,
          "likes": 0
        }
      }
    ]
  }'
```

<Info>
  Use `@request.data.requests.N.body.id` to reference the ID of a record created in a previous request within the same batch.
</Info>
