> For clean Markdown of any page, append .md to the page URL. > For a complete documentation index, see https://contentful.com/developers/docs/references/experience-delivery-api/experience/llms.txt. > For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://contentful.com/developers/docs/_mcp/server. An experience is a hydrated representation of content authored in Contentful — the visual structure, personalization variants, and content of a published experience, ready for a client application to render. The XDA returns experiences in two ways: * **By ID** — a straightforward `GET` request returns the hydrated experience. * **With personalization input** — a `POST` request lets you send personalization overrides in the request body, which the API applies while hydrating the experience. Both operations support locale selection, variant targeting, optimization profiles, and experience parameters. ## Getting localized experiences By default, a request to the experience endpoint returns the experience hydrated with content in the environment's default locale: ``` GET https://xdn.contentful.com/spaces/{spaceId}/environments/{envId}/experiences/{expId} ``` To hydrate the experience with a different locale, add a `locale` query parameter with the locale code: ``` GET https://xdn.contentful.com/spaces/{spaceId}/environments/{envId}/experiences/{expId}?locale=en-US ``` Experience fields aren't localized, so changing the locale never affects the layout or the personalization rules applied — the experience and its rules stay the same. Only the content pulled from bound entries and assets changes: if a bound entry or asset has localized fields, the API returns the value matching the requested locale, following the fallback chain configured for the environment's locales. Locale selection also applies when requesting a variant of an experience: ``` GET https://xdn.contentful.com/spaces/{spaceId}/environments/{envId}/experiences/{expId}?locale=en-US&variant=Experience:3foFgkr9iq8bWSZQtb2H1G:1b414538-5894-483a-9441-2cbfa65a0aab ``` > **Info** > > On the production host, entries and assets must be published for the requested locale. Requesting a locale that hasn't been published, or that doesn't exist for the environment, returns a `404`. ## Passing parameters to an experience An experience can declare parameters that its data bindings read at request time, such as the entry shown on a product page, or how many posts a list includes. Pass their values as query parameters in a `GET` request, or in the `parameters` object of a `POST` request body. In a `GET` request, prefix every key with `parameter.`. The prefix keeps experience parameters apart from system query parameters, so `locale=de-DE¶meter.locale=fr-FR` sets the request locale and an experience parameter named locale separately. A key without the prefix returns a 422 error. ``` # String or Number parameter.= # ResourceLink, by entry ID parameter.= # ResourceLink, by field value parameter..fields.= # Record, for collections parameter..limit= parameter..order= ``` ``` GET https://xdn.contentful.com/spaces/{spaceId}/environments/{envId}/experiences/{expId}?parameter.item=4fJx2kQ9mN¶meter.featuredPosts.limit=6 ``` In a `POST` request body, key each value by its parameter ID, without the prefix: ```jsonc { "parameters": { // String or Number "heading": "Summer sale", // ResourceLink, by entry ID "author": "4fJx2kQ9mN", // ResourceLink, by field value "item": { "fields": { "slug": "standing-desk" } }, // Record, for collections "featuredPosts": { "limit": 6, "order": "-fields.title" } } } ``` ### Collection parameters A Record parameter controls a collection that the experience displays, such as a list of blog posts. It isn't a free-form object: it accepts only the collection fields the experience declares for it, and any other key is rejected as an unknown parameter. * `limit` sets the number of entries to return, within the range the experience allows. * `order` sets the sort order as a single field path, such as `fields.title` or `sys.createdAt`. Prefix it with a minus sign for descending order, such as `-fields.title`. Lists of several fields aren't accepted, and the experience can restrict which fields are allowed. ### Fallback values If you omit a String or Number parameter, or a collection's limit or order, the API uses the experience's fallback value. If the experience does not have a fallback value, it uses the one of the bound data assembly. > **Info** > > ResourceLink parameters have no fallback. A required parameter with no value and no fallback on the experience returns a 422 error. ### Looking up an entry by field value Instead of an entry ID, you can fill a ResourceLink parameter with a field value, such as the slug from a URL like `/products/standing-desk`. You no longer need a separate request to find the entry ID first. ``` GET https://xdn.contentful.com/spaces/{spaceId}/environments/{envId}/experiences/{expId}?parameter.item.fields.slug=standing-desk ``` ```json POST https://xdn.contentful.com/spaces/{spaceId}/environments/{envId}/experiences/{expId} { "parameters": { "item": { "fields": { "slug": "standing-desk" } } } } ``` The API finds the matching entry and returns the experience exactly as if you had passed the entry's ID. #### Content type A lookup searches one content type. If the parameter allows only one, the API uses it. Otherwise, name the content type with `parameter..content_type` in a `GET` request, or with `content_type` next to `fields` in a `POST` request body. > **Info** > > It must be one of the content types the parameter allows. ``` GET https://xdn.contentful.com/spaces/{spaceId}/environments/{envId}/experiences/{expId}?parameter.item.content_type=product¶meter.item.fields.slug=standing-desk ``` #### Matching rules * A lookup matches one field exactly. Pass a single, non-empty value. * Values are compared as strings, so look up by text fields such as a slug or SKU. A value such as "007" isn't converted to a number. * The field is matched against its value in the request locale only. A value the entry inherits from a fallback locale doesn't match, even if it appears in the response. If you request the **de-DE** locale, pass the slug stored in **de-DE**. * Uniqueness isn't enforced. If several entries match, the API uses one of them, with no guarantee which, and reports an error. Look up by a field whose values are unique. * You can't send a lookup and an entry ID for the same parameter. #### Lookup outcomes A lookup that passes validation always returns 200. If it doesn't find exactly one entry, the experience is still returned, and the problem is reported in its `errors` array, with one of these codes in `originalError.code`: | Outcome | Code | Result | | ------------------------------------------------- | ----------------------------- | --------------------------------------------------------------------------------------------------- | | No entry matches | `ENTRY_LOOKUP_NO_MATCH` | Content bound to the parameter is null. The bound data assembly also reports the missing parameter. | | Several entries match | `AMBIGUOUS_ENTRY_LOOKUP` | One of the matching entries is used. | | The content type has no such field | `INVALID_ENTRY_LOOKUP_FIELD` | The parameter isn't resolved. | | The content type doesn't exist in the environment | `LOOKUP_TARGET_NOT_IN_SCHEMA` | The parameter isn't resolved. | ```json { "sys": { "id": "productPage", "type": "Experience" }, "nodes": [ ... ], "errors": [ { "fieldPath": "Experience.productPage.dataAssembly", "message": "Lookup for parameter 'item' by fields.slug matched more than one entry", "originalError": { "code": "AMBIGUOUS_ENTRY_LOOKUP", "parameter": "item" } } ] } ``` ### Parameter validation errors The API validates parameters against the experience's declarations before it resolves any content. An invalid value returns a 422 error. Each item in its `details` array has a `code` and the `parameter` you sent: the full query key in a `GET` request, such as `parameter.item.fields.slug`, or the parameter ID in a `POST` request. | Code | Cause | | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `unknown_parameter` | The experience doesn't declare the parameter or collection field, the key is missing the prefix or is malformed, or two keys conflict for the same parameter. | | `missing_required` | A required parameter has no value and no fallback, or a lookup needs a content type because the parameter allows several. | | `type_mismatch` | The value has the wrong type, such as text for a Number parameter, or a lookup on a parameter that isn't a ResourceLink. | | `invalid_value` | The value isn't allowed: it's not one of the allowed values, the order isn't a single valid field path, the content type isn't one the parameter allows, or the lookup is empty or names more than one field. | | `out_of_range` | A number is outside the allowed range. | ## API Docs - Experience [Get experience by ID](https://contentful.com/developers/docs/references/experience-delivery-api/experience/get-experience.md) - Experience [Get experience by ID with personalization input](https://contentful.com/developers/docs/references/experience-delivery-api/experience/get-experience-with-overrides.md) ## OpenAPI Specification The raw OpenAPI 3.1 specification for this API is available at: - [OpenAPI JSON](https://contentful.com/developers/docs/openapi.json) - [OpenAPI YAML](https://contentful.com/developers/docs/openapi.yaml)