Bulk entry content operations
Bulk entry content operations provide an efficient way to create, update, delete, or export multiple entries simultaneously, reducing the number of API calls required for bulk content management. This feature is particularly useful when working with large datasets, as it significantly reduces the time and complexity required for handling a larger number of entries. The operation is asynchronous and returns immediately with a job ID that you can use to track the progress of the bulk operation.
Why bulk entry content operations?
Traditional entry management requires individual API calls for each entry, which can be time-consuming and inefficient when dealing with hundreds or thousands of entries, leading to several challenges:
- API rate limits: Creating entries one-by-one can quickly exhaust rate limits, especially for large datasets
- Network overhead: Each API call introduces latency, making bulk operations slow
- Complexity: Managing individual requests, error handling, and retry logic adds significant complexity to client code
- Resource consumption: Multiple HTTP connections consume more resources on both client and server
Key benefits
The bulk entry content operations feature uses an asynchronous processing model to handle large volumes of entries efficiently, providing the following benefits:
- Performance: Create, update, delete, or export hundreds of entries in seconds instead of minutes
- Efficiency: Significantly reduce API calls for a large number of operations
- Non-blocking operations: Your application can continue working while entries are being processed in the background
- Progress tracking: Real-time visibility into how many entries have been processed
- Reliability: Built-in error handling with detailed reporting for individual entry failures
- Graceful error handling: Individual entry failures don’t halt the entire operation
- Scalability: The system can handle multiple concurrent bulk operations across different spaces
How it works
1. Prepare your data
Structure your entries as a JSON array in a file (e.g., entries.json). For create, sys.id is optional. For update, include sys.id and sys.version (required; sys.version must match the current entry version).
-
Create: Each entry must include
sys.contentTypeandfields. Omitsys.idto auto-generate an entry ID, or includesys.idto use a specific ID. If an entry with that ID already exists, the item fails withEntryExistsError. -
Update: Each entry must include
sys.id,sys.version(must match the current version), andfields. -
Delete: Each entry must include only
sys.id.Example: Preparing entries for bulk creation
The first entry omits
sys.id(auto-generated). The second suppliessys.idto create an entry with that ID.Example: Preparing entries for bulk deletion
2. Upload entries file
Upload your entries file to https://upload.contentful.com/spaces/{space_id}/environments/{environment_id}/uploads
Upload using cURL:
Response:
The API returns an Upload resource containing the upload ID:
3. Receive upload ID
The API returns an Upload resource with an ID.
4. Trigger bulk operation
Send a POST request to the factory endpoint for the desired action:
-
Create:
POST https://api.contentful.com/spaces/{space_id}/environments/{environment_id}/bulk_operations/entries/create -
Update:
POST https://api.contentful.com/spaces/{space_id}/environments/{environment_id}/bulk_operations/entries/update -
Delete:
POST https://api.contentful.com/spaces/{space_id}/environments/{environment_id}/bulk_operations/entries/delete -
Export:
POST https://api.contentful.com/spaces/{space_id}/environments/{environment_id}/bulk_operations/entries/export(optionalqueryin the body; no upload step)Trigger a bulk creation using cURL:
Response:
The API returns a
BulkContentOperationresource withsys.statusset toin_progress:
5. Receive operation ID
The API returns a BulkContentOperation resource. Use sys.id to poll for status.
6. Track progress
Poll https://api.contentful.com/spaces/{space_id}/environments/{environment_id}/bulk_operations/{operation_id} until sys.status is completed or failed.
Check status using cURL:
Response when completed:
7. Retrieve results
For create, update, and delete jobs, once sys.status is completed, check result.items for per-entry outcomes. Each item has a status of "succeeded" or "failed". Failed items include an error object with a machine-readable sys.id and a human-readable message.
For export jobs, use result.files instead — see Bulk export.
Error handling
It’s important to understand the difference between the status of the bulk operation itself and the status of individual entries within that operation.
The bulk operation status is available at sys.status and reflects the job lifecycle:
"in_progress": The operation is queued or currently being processed"completed": The operation finished processing (even if some individual entries failed)"failed": The entire bulk operation failed due to a system error (e.g., invalid upload file, timeout, or server error)
When a bulk operation fails at the job level (sys.status is "failed"), the response includes a top-level error field:
When a create, update, or delete job completes (sys.status is "completed"), check result.items to see which individual entries succeeded and which failed. Each item has:
status:"succeeded"or"failed"entity(on success): minimalsysobject identifying the created/updated/deleted entryerror(on failure):sys.type,sys.id(error code), andmessage(minimal error shape; nodetails)
Individual entry failures typically occur due to validation errors (e.g., missing required fields, invalid field values, or content type mismatches). On bulk create, supplying an sys.id that already exists returns EntryExistsError for that item.
Updating existing entries
To update existing entries, include sys.id, sys.version, and fields in your entries array. sys.version is required and must match the current entry version.
The workflow for updates is the same as for creates, but use the update endpoint when triggering the bulk operation:
Deleting entries in bulk
To delete multiple entries at once, prepare a file containing the IDs of the entries to delete:
Upload the file the same way as for create/update, then trigger the delete operation:
When completed, check result.items. Each successfully deleted entry has status: "succeeded" and an entity with sys.type DeletedEntry and the entry sys.id:
List bulk operations
GET https://api.contentful.com/spaces/{space_id}/environments/{environment_id}/bulk_operations returns a cursor-paginated list of bulk operations.
Optional query parameters:
-
sys.status[in]— filter by status (in_progress,completed,failed) -
sys.bulkType[in]— filter by type (EntriesCreate,EntriesUpdate,EntriesDelete,EntriesExport) -
sys.createdAt[gte]/sys.createdAt[lte]— filter by creation date -
sys.updatedAt[gte]/sys.updatedAt[lte]— filter by update date -
order— sort bysys.createdAt,-sys.createdAt,sys.updatedAt, or-sys.updatedAt(default:-sys.createdAt) -
limit— page size (1–100) -
pageNext/pagePrev— cursor pagination (mutually exclusive)Example:
The response includes items, limit, and optional pages.next / pages.prev cursors. See the API reference for the full response shape.
Bulk export
Export uses POST .../bulk_operations/entries/export with an optional query in the body to filter the desired entries. No upload step is required.
1. Define your query
Send an optional query object to filter which entries to export. Omit query or send {} to export all entries in the environment, capped at 10,000 entries.
Allowed export query parameters
Only the following parameters are allowed; all other entry query parameters are rejected. Filters apply to sys.* properties and content_type only — filtering on fields.* or metadata.* is not supported. order is not supported.
Export all entries of a content type
Export entries of multiple content types
Export archived entries
Export articles updated since a date
2. Create the export job
The API returns a BulkContentOperation with bulkType EntriesExport and sys.status in_progress.
3. Poll until complete
Poll GET .../bulk_operations/{bulk_operation_id} until sys.status is completed or failed (same endpoint as create, update, and delete jobs).
4. Download the export archives
When sys.status is completed, result.files contains signed download URLs for one or more ZIP archives. Each item has url.href and url.expiresAt (URLs expire after 1 hour). A new URL is signed on every GET, so download before it expires.
Response when completed:
Archive contents
The export produces a ZIP file. Inside it, everything is placed under a single directory. Directory and data file names are dynamic and unique for each export — use metadata.json to discover them and to get the names of the data files to read.
Each {bulkOperationId}_N.json file is a JSON array of exported entries, split by a max file size limit. Large exports can have multiple numbered data files.
metadata.json
Describes the full export. Use it to see how many entities were exported, how to interpret the data files, and to verify completeness before processing.
Read metadata.json first to know how many data files to expect, then process each file listed in files in order.
Limitations
- Maximum number of entries per request is subject to technical limits (currently 10,000 entries per file)
- Create and update: entries must reference valid content types. Upload files follow standard upload size limits
- Export: results are capped at 10,000 entries per job and delivered as JSON files inside downloadable ZIP archives
- Export: signed download URLs in
result.files[].urlexpire after 1 hour - At most one bulk operation can be in flight per space at a time (create, update, delete, or export)
For detailed API reference documentation, see the Bulk entry content operations API reference.