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

Contentful's Content Management API (CMA) helps you manage content in your spaces. To learn more about how to model your content, [read our modeling guide](/concepts/data-model).

**NOTE**: You can use the CMA to deliver and manage content, but you shouldn't use it to deliver large amounts of content and instead use the [Content Delivery API](/references/content-delivery-api/overview). The structure of responses from the CMA differs from the CDA as `GET` responses retrieve the entirety of items (i.e. all localized and unpublished content).

> **Info**
>
> **Note:** For EU data residency customers, the Base URL is *[https://api.eu.contentful.com](https://api.eu.contentful.com)*.

## Basic API information

API Base URL `https://api.contentful.com`
*This is a read/write API*

## Authentication

You access the API securely via HTTPS, and it will be available to clients authenticating with an access token.

Learn about authenticating to the CMA and get your access token from the [developer center](/references/authentication#the-content-management-api).

## Resource IDs

When creating resources, you can specify an ID or let the API generate a random ID for you. If you choose an ID yourself it must adhere to the following rules:

* It has a length between 1 and 64 characters.

* It only includes alphanumeric characters, dots `.`, hyphens `-` or underscores `_`.

Represented as a regular expression, this is `/^[a-zA-Z0-9-_.]{1,64}$/`.

## Updating content

Contentful doesn't merge changes made to content, so when updating content, you need to send the entire body of an entry. If you update content with a subset of properties, you will lose all existing properties not included in that update.

You should always update resources in the following order:

* Fetch current resource.

* Make changes to the current resource.

* Update the resource by passing the changed resource along with current version number.

This way no unseen changes are overridden and unexpected conflicts are unlikely to occur.

**Note**: You can't update any of the `sys` property fields, including `sys.id`.

## Updating and version locking

Contentful uses [optimistic locking](https://en.wikipedia.org/wiki/Optimistic_concurrency_control). When updating an existing resource, you need to specify its current version with the `X-Contentful-Version` HTTP header (this header is automatically set when using our [official client libraries](/sdks/javascript/sdks)). Contentful compares this version with the current version stored to ensure that a client doesn't overwrite a resource that has since been updated. If the version changed in-between, Contentful would reject the update.

## API rate limits

API Rate limits specify the number of requests a client can make to Contentful APIs in a specific time frame. Every request counts against a per second rate limit.

By default the Contentful Management API enforces rate limits of **7** requests per second. Higher rate limits may apply depending on your current plan.

The following table lists all headers returned in *every* response by the Content Management API which give a client information on rate limiting:

| Header                                    | Description                                                                       |
| ----------------------------------------- | --------------------------------------------------------------------------------- |
| `X-Contentful-RateLimit-Second-Limit`     | The maximum amount of requests which can be made in a second.                     |
| `X-Contentful-RateLimit-Second-Remaining` | The remaining amount of requests which can be made until the next secondly reset. |
| `X-Contentful-RateLimit-Reset`            | The number of seconds until the next request can be made.                         |

Here is a part of a Contentful Management API response example showing the headers for rate limiting:

```
X-Contentful-RateLimit-Reset: 0
X-Contentful-RateLimit-Second-Limit: 7
X-Contentful-RateLimit-Second-Remaining: 9
```

When a client gets rate limited, the API responds with the **429 Too Many Requests** HTTP status code and sets the value `X-Contentful-RateLimit-Reset` header to an integer specifying the time before the limit resets and another request will be accepted.
As the client is rate limited per second, the header will return 1, which means the next second.

Examples:

The current rate limits for a client are the default 7 per second.

**Example 1**

Client: 5 requests in 1 second

```
HTTP/1.1 2xx
X-Contentful-RateLimit-Reset: 0
```

Meaning: not rate limited. More requests are allowed.

**Example 2**

Client: 11 requests in 1 second

```
HTTP/1.1 429
X-Contentful-RateLimit-Reset: 1
```

Meaning: wait 1 second before making more requests.

**Example 3**

Client: 9000 requests in 15 minutes, 9000 requests in following 15 minutes, 9000 requests in following 15 minutes, 9000 requests in following 15 minutes

```
HTTP/1.1 429
X-Contentful-RateLimit-Reset: 900
```

Meaning: wait 15 minutes before making more requests (which frees up 9000 requests - 15 minutes later 9000 requests get freed up and so on).

## Common resource attributes

Every resource returned by the API includes a `sys` property. The `sys` object contains system managed and resource dependent information. At minimum `sys` defines the `sys.type` property.

During entity creation, the value of `sys.id` is either automatically generated or can be specified in the URL (e.g. `/environments/master/entries/my-custom-id`) of the initial `PUT` request. The only entity which always uses generated `sys.id` values is `spaces`.

**Note**: `sys` metadata fields can not be changed programmatically.

The `sys.id` property is defined for every resource that is not a collection. For example, a `Space` resource will have a `sys.type` and `sys.id`:

```json
{
  "sys": {
    "type": "Space",
    "id": "yadj1kx9rmg0"
  }
}
```

| Field                | Type    | Description                                                | Applies to                     |
| -------------------- | ------- | ---------------------------------------------------------- | ------------------------------ |
| sys.type             | String  | Type of a resource.                                        | All                            |
| sys.linkType         | String  | Type of an entity the link is referring to.                | Links                          |
| sys.id               | String  | Unique ID of a resource.                                   | All except arrays              |
| sys.space            | Link    | Link to a resource's space.                                | Entries, assets, content types |
| sys.environment      | Link    | Link to a resource's environment.                          | Entries, assets, content types |
| sys.contentType      | Link    | Link to an entry's content type.                           | Entries                        |
| sys.publishedCounter | Integer | Number of times a resource was published.                  | Entries, assets, content types |
| sys.publishedVersion | Integer | Published version of a resource.                           | Entries, assets, content types |
| sys.version          | Integer | Current version of a resource.                             | Entries, assets, content types |
| sys.firstPublishedAt | Date    | Date and time a resource was published for the first time. | Entries, assets, content types |
| sys.createdAt        | Date    | Date and time a resource was generated in the system.      | Entries, assets, content types |
| sys.createdBy        | Link    | Link to creating a user.                                   | Entries, assets, content types |
| sys.publishedAt      | Date    | Date and time a resource was published after an update.    | Entries, assets, content types |
| sys.publishedBy      | Link    | Link to publishing a user.                                 | Entries, assets, content types |
| sys.updatedAt        | Date    | Date and time a resource was updated in the system.        | Entries, assets, content types |
| sys.updatedBy        | Link    | Link to updating a user.                                   | Entries, assets, content types |

> **Info**
>
> **Sys properties - difference in meaning in Contentful APIs**
>
> Some of `sys` properties, while having the same label, render different kinds of data depending on the API. Please see the descriptions of these properties per API in the table below:
>
> | Property name per API |           |           |                                                            |
> | --------------------- | --------- | --------- | ---------------------------------------------------------- |
> | CMA                   | CDA       | CPA       | **Description**                                            |
> | firstPublishedAt      | createdAt | -         | Date and time a resource was published for the first time. |
> | publishedAt           | updatedAt | -         | Date and time a resource was published after an update.    |
> | createdAt             | -         | createdAt | Date and time a resource was generated in the system.      |
> | updatedAt             | -         | updatedAt | Date and time a resource was updated in the system.        |

## Date and time format

Date and time must be formatted according to [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601).

> **Info**
>
> **Important**: When setting time, ensure to indicate timezone. With no timezone specified, UTC+0 is applied as a default one.

The table below displays the supported date and time formatting:

| Data type                      | Format                 | Examples     |
| ------------------------------ | ---------------------- | ------------ |
| Date only                      | "YYYY-MM-DD"           | "2015-11-06" |
| Date + time                    | "YYYY-MM-DDThh:mm:ss"  |              |
| "YYYY-MM-DDThh:mm:ss.sss"      | "2015-11-06T09:45"     |              |
| Date + time + timezone         | "YYYY-MM-DDThh:mm:ssZ" |              |
| "YYYY-MM-DDThh:mm:ss±\[hh:mm]" | "2015-11-06T09:45:27Z" |              |
| "2015-11-06T09:45:27+00:00"    |                        |              |
| "2015-11-06T09:45:27-08:00"    |                        |              |

## Collection resources and pagination

Contentful returns collections of resources in a wrapper object that contains extra information useful for paginating over large result sets:

```json
{
  "sys": { "type": "Array" },
  "skip": 0,
  "limit": 100,
  "total": 1256,
  "items": [ /* 100 individual resources */ ]
}
```

In the above example, a client retrieves the next 100 resources by repeating the same request, changing the `skip` query parameter to `100`. You can use the `order` parameter when paging through larger result sets to keep ordering predictable. For example, `order=sys.createdAt` will order results by the time the resource was first published.

> **Info**
>
> Don't use `skip` to iterate through an entire collection. Performance degrades as the offset grows, particularly for complex queries and deep link resolution. `skip`/`limit` is intended for random access into a bounded result set (for example, "page 3 of 20" in a UI).
>
> To fetch or process every item in a large collection, such as exports, analytics, migrations, or any exhaustive traversal, use [cursor pagination](#cursor-pagination) instead.

## Filter results

You can use a variety of filter parameters to search and filter items in the response from collection endpoints.

### Example usage

```
name[match]=fred&sys.user.sys.id[in]=abc123,zyx987&sys.updatedAt[lt]=2018-09-01
```

In general the format of a filter parameter is as follows:

```
field[operator]=value
```

### Operators

For each supported field, one or more operators is available. This table explains their usage:

| Operator | Description                                                                                                                                                                           |
| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `eq`     | The resource field exactly matches the specified value. E.g. `name[eq]=fred` (or `name=fred` for short). Maximum length for text fields is 256 characters                             |
| `ne`     | The resource field does *not* match the specified value. E.g. `name[ne]=fred`                                                                                                         |
| `match`  | The resource field starts with the specified value. E.g. `name[match]=fre`                                                                                                            |
| `in`     | The resource field matches at least one of the specified values in a comma separated list. E.g. `sys.user.sys.id[in]=abc123,zyx987`. Maximum length for text fields is 256 characters |
| `nin`    | The resource field does *not* match any of the specified values in a comma separated list. E.g. `sys.user.sys.id[nin]=abc123,zyx987`                                                  |
| `exists` | The resource field is not empty if the specified value is `true`, or empty if the specified value is `false`. E.g. `sys.updatedAt[exists]=true`                                       |
| `lt`     | The resource field is less than the specified value. E.g. `sys.updatedAt[lt]=2018-09-01`                                                                                              |
| `lte`    | The resource field is less than or equal to the specified value. E.g. `sys.updatedAt[lte]=2018-09-01`                                                                                 |
| `gt`     | The resource field is greater than the specified value. E.g. `sys.updatedAt[gt]=2018-09-01`                                                                                           |
| `gte`    | The resource field is greater than or equal to the specified value. E.g. `sys.updatedAt[gte]=2018-09-01`                                                                              |
| `all`    | Returns only items whose selected field contains every value you specify in the filter                                                                                                |

## Cursor pagination

#### Overview

Cursor pagination is an approach to paginating datasets in Contentful APIs. Unlike traditional offset-based pagination, which uses skip and limit parameters, cursor-based pagination uses opaque cursor tokens and dedicated next/prev links to mark the position in the dataset. This can dramatically improve performance, especially for datasets with large numbers of items.

Use cursor pagination whenever you need to traverse a whole collection, for example, exporting a space, running analytics over all entries, or processing every asset.

Use offset pagination (`skip`/`limit`) only for bounded, random-access cases, such as showing a specific page of results in a UI.

#### How cursor pagination works

* **Initial Request:**\
  Add the query parameter `cursor=true` to your API request, e.g.

  ```
  GET /spaces/:space_id/entries?cursor=true
  ```

  The response contains:

  * `items`: The current page of resources.
  * `pages`: Contains `next` (and optionally `prev`) URLs for paginating forward and backward.

* **Paginate forward:**\
  Use the `pages.next` URL from the previous response for the next page request:

  ```
  GET /spaces/:space_id/entries?pageNext={cursor_token}
  ```

  Continue this process until the `next` link is omitted, which means you've reached the end of the dataset.

* **Paginate backward:**
  If the response contains a `pages.prev` link, you can fetch previous pages:

  ```
  GET /spaces/:space_id/entries?pagePrev={cursor_token}
  ```

* **Consistency:**
  All query parameters used in the initial request apart from `limit` are locked and encoded in the cursor token. They cannot be changed between pages.

  The `limit` parameter can be updated, and the new value will be persisted for subsequent pages until it's updated again.

  ```
  GET /spaces/:space_id/entries?pageNext={cursor_token}&limit={limit}
  ```

  This can be useful when you need to adjust the page size, for example, if a page size (in bytes) exceeds the response limit.

* **Total count:**
  The response does not include a total count or a skip property. This is a key feature for performance, as the API avoids costly full counts on large datasets.

#### Example response

```json
{
  "sys": { "type": "Array" },
  "limit": 100,
  "items": [ ... ],
  "pages": {
    "next": "/spaces/:space_id/entries?pageNext=cursor_token",
    "prev": "/spaces/:space_id/entries?pagePrev=cursor_token"
  }
}
```

#### Advantages

* Considerable performance improvements, particularly for large datasets.

If your environment contains 10s of thousands of entries or assets, consider switching to cursor pagination, it can make requests to fetch lists of entries or assets of such an environment much faster.

If you don't paginate at all, particularly when working with CDA and you don't need the `total` property from the response, we highly recommend using requests with `cursor=true`.

#### Limitations

* **The "total" property is not included in the API responses.** Make sure you don't rely on it in your workflow prior to making the switch.

#### Enablement and access

* The feature is currently available in the CMA, CPA and CDA.

## API versioning

All API requests should specify a `Content-Type` header of `application/vnd.contentful.management.v1+json`.

If not specified, Contentful will route your request to the latest version of the API. This could break clients which expect the outdated API version. To be certain, always specify the `Content-Type` header.