Menu
Guides
API Reference

Guides

Using GraphQL API for querying CMS content

GraphQL API reference for querying published CMS content through generic and typed schemas.

8 min read

The GraphQL API lets storefronts and other integrations retrieve content published with the CMS. It provides a single GraphQL endpoint with two ways to query content:

  • Generic queries return the complete content document as JSON and work with any Content Type.
  • Typed queries expose fields generated from a JSON Schema published to the Schema Registry, enabling field selection and type generation.

Because the Data Plane serves published content, this API is read-only and doesn't support mutations.

Endpoint

Send GraphQL queries as POST requests to:


_10
https://api.vtexcommercestable.com.br/api/content-platform/graphql/{account}/{storeId}

Replace the path parameters as follows:

Path parameterDescription
accountVTEX account name.
storeIdStore or site identifier within the account, for example, faststore.

If you don't know the store ID, list the stores in the account:


_10
GET https://api.vtexcommercestable.com.br/api/content-platform/manage/{account}/stores
_10
Authorization: Bearer {token}

Use the id field from the response as the storeId.

Authentication

Every request requires a bearer token in the Authorization header:


_10
Authorization: Bearer {token}

To get a token with the VTEX CLI, log in to the account and run:


_10
vtex local token

The account used with vtex login must match the {account} segment in the request URL. Authentication is required for all queries, including queries through the generic surface.

Schema overview

The API provides two query surfaces:

  • Generic surface: Available by default and returns content as a JSON scalar. Use it for diagnostics, schema-independent tooling, or Content Types without a published schema.
  • Typed surface: Enabled with the X-Content-Schema header and exposes fields generated from a schema in the Schema Registry.

Generic surface

The generic surface exposes the entry and entries query fields for every Content Type.

FieldArgumentsDescription
entrycontentTypeId, exactly one of id or slug, locale, and optional branchIdRetrieves one published entry.
entriescontentTypeId, locale, and optional branchId, scroll, sort, and orderRetrieves a paginated list of published entries.

Get an entry

Use entry to retrieve one entry by id or slug:


_10
query Entry($contentTypeId: String!, $id: ID!, $locale: String!) {
_10
entry(contentTypeId: $contentTypeId, id: $id, locale: $locale) {
_10
id
_10
contentTypeId
_10
name
_10
createdAt
_10
updatedAt
_10
content
_10
}
_10
}

The content field contains the complete stored document as a JSON scalar.

The lookup must contain exactly one of the following arguments:

ArgumentTypeDescription
idIDEntry ID.
slugStringEntry slug.

To retrieve an entry by slug:


_10
query EntryBySlug($contentTypeId: String!, $slug: String!, $locale: String!) {
_10
entry(contentTypeId: $contentTypeId, slug: $slug, locale: $locale) {
_10
id
_10
content
_10
}
_10
}

Slug matching checks content.slug first and then content.seo.slug. Slugs with and without a leading slash match, but including the leading slash, for example, /black-friday, is recommended.

List entries

Use entries to retrieve entries of a Content Type:


_11
query Entries($contentTypeId: String!, $locale: String!, $scroll: String) {
_11
entries(contentTypeId: $contentTypeId, locale: $locale, scroll: $scroll) {
_11
entries {
_11
id
_11
name
_11
updatedAt
_11
content
_11
}
_11
scroll
_11
}
_11
}

Generic response fields

The generic entry object exposes the following fields:

FieldDescription
idEntry ID.
contentTypeIdContent Type that defines the entry.
nameEntry name.
createdAtDate and time when the entry was created.
updatedAtDate and time when the entry was last updated.
contentComplete stored document as a JSON scalar.

The entries result contains:

FieldDescription
entriesEntries in the current page.
scrollCursor for retrieving the next page, or null on the last page.

Typed surface

To query fields generated from a registered JSON Schema, send the X-Content-Schema header:


_10
X-Content-Schema: {account}.{name}[@version]

For example:


_10
X-Content-Schema: vtex.faststore

Schema versions are resolved as follows:

Header valueResolution
vtex.faststoreLatest published version, cached for approximately 60 seconds.
vtex.faststore@latestLatest published version, cached for approximately 60 seconds.
vtex.faststore@4.0.1Exact published version. Recommended for CI and code generation.

The schema account prefix must match {account} in the URL. Shared schemas can use the vtex prefix, such as vtex.faststore. Otherwise, the API returns 400 Schema "..." does not belong to tenant "...".

If the header is missing, malformed, or can't be resolved to a valid schema, the request uses the generic surface instead of returning a schema-resolution error. If a query unexpectedly exposes only entry and entries, check the header value.

Typed query fields

Each Content Type in the registered schema generates a camel-cased query root with:

  • A field for retrieving one entry by id or slug.
  • A <contentType>List field for retrieving multiple entries.
  • A connection type containing the entries and pagination cursor.

For example, the landingPage Content Type generates:


_10
query Page($id: ID!, $locale: String!) {
_10
landingPage(id: $id, locale: $locale) {
_10
id
_10
slug
_10
seo {
_10
title
_10
description
_10
}
_10
}
_10
}

The following table illustrates the naming convention:

Content Type IDSingle lookupList lookup
landingPagelandingPage(id: ...) or landingPage(slug: ...)landingPageList(...)
homehome(id: ...)homeList(...)

For example, landingPageList returns a LandingPageConnection.

Content Type IDs that aren't valid GraphQL identifiers are sanitized. For example, 404 becomes the Type404 type and the type404 and type404List query fields:


_10
query {
_10
type404(id: "...", locale: "en-US") {
_10
id
_10
}
_10
type404List(locale: "en-US") {
_10
entries {
_10
id
_10
}
_10
}
_10
}

Query arguments

The following arguments apply to generic and typed queries:

ArgumentRequiredDescription
localeYesActive locale whose projected content should be returned.
branchIdNoContent branch. Defaults to main.

Omitting locale causes a GRAPHQL_VALIDATION_FAILED error before query execution. Passing an unknown or inactive locale causes a BAD_USER_INPUT error. When available in the store configuration, the error's extensions.activeLocales field lists the active locales.

Pagination and sorting

The entries field and every typed <contentType>List field return up to 20 entries per page.

The response includes a scroll cursor:


_10
{
_10
"data": {
_10
"entries": {
_10
"entries": [],
_10
"scroll": "eyJ..."
_10
}
_10
}
_10
}

Pass the returned cursor in the next query's scroll argument. A null cursor indicates the last page. Stale or modified cursors cause an INVALID_SCROLL error.

List queries support these sorting options:

ArgumentValuesDefault
sortupdatedAt, createdAt, name, or authorupdatedAt
orderasc or descdesc

The field used for sorting doesn't need to be included in the selection set.

Discovering fields

GraphQL introspection is disabled. To inspect the available fields, append /schema.graphql to the tenant URL and send a GET request:


_10
GET https://api.vtexcommercestable.com.br/api/content-platform/graphql/{account}/{storeId}/schema.graphql
_10
Authorization: Bearer {token}

Without a schema selection, this endpoint returns the generic schema. To retrieve a typed schema, use the schema query parameter:


_10
GET https://api.vtexcommercestable.com.br/api/content-platform/graphql/{account}/{storeId}/schema.graphql?schema=vtex.faststore@4.0.1
_10
Authorization: Bearer {token}

You can also select the typed schema with the X-Content-Schema header. The header takes precedence if both the header and query parameter are present.

The endpoint returns the schema definition language (SDL) as text/plain. Responses are cached for 60 seconds and can be served stale while the schema is revalidated for 300 seconds. The typed SDL also includes the generic entry and entries fields.

Code generation

Pin the schema version so a newly published version doesn't silently change generated types:


_10
curl -s "https://api.vtexcommercestable.com.br/api/content-platform/graphql/{account}/{storeId}/schema.graphql?schema=vtex.faststore@4.0.1" \
_10
-H "Authorization: Bearer $TOKEN" \
_10
-o schema.graphql
_10
_10
graphql-codegen --schema schema.graphql

Update the pinned version deliberately when the store schema changes.

Query limits

The maximum query depth is 12. Deeper queries are rejected before execution with the QUERY_TOO_DEEP code and the extensions.maxDepth and extensions.actualDepth fields. Fragments contribute to the query depth wherever they are spread.

Errors

GraphQL errors follow the standard errors[].extensions.code format:

CodeCause
BAD_USER_INPUTNeither or both of id and slug were provided, or locale is unknown or inactive.
INVALID_SCROLLPagination cursor is malformed or stale.
QUERY_TOO_DEEPQuery exceeds the maximum depth.

Errors that occur before GraphQL query execution use HTTP status codes:

StatusCause
400Missing or malformed Authorization header, incompatible X-Content-Schema account prefix, or some invalid-token cases.
401Token is invalid or expired, or its account doesn't match {account} in the URL.

Request examples

The following examples assume that you exported a token:


_10
export TOKEN=$(vtex local token)

Generic entry by ID


_10
curl -s "https://api.vtexcommercestable.com.br/api/content-platform/graphql/{account}/{storeId}" \
_10
-H "Content-Type: application/json" \
_10
-H "Authorization: Bearer $TOKEN" \
_10
-d '{
_10
"query": "query($id: ID!, $locale: String!) { entry(contentTypeId: \"landingPage\", id: $id, locale: $locale) { id content } }",
_10
"variables": { "id": "01J...", "locale": "en-US" }
_10
}'

Generic entry by slug


_10
curl -s "https://api.vtexcommercestable.com.br/api/content-platform/graphql/{account}/{storeId}" \
_10
-H "Content-Type: application/json" \
_10
-H "Authorization: Bearer $TOKEN" \
_10
-d '{
_10
"query": "query($slug: String!, $locale: String!) { entry(contentTypeId: \"landingPage\", slug: $slug, locale: $locale) { id content } }",
_10
"variables": { "slug": "/home", "locale": "en-US" }
_10
}'

Generic paginated list

Request the first page:


_10
curl -s "https://api.vtexcommercestable.com.br/api/content-platform/graphql/{account}/{storeId}" \
_10
-H "Content-Type: application/json" \
_10
-H "Authorization: Bearer $TOKEN" \
_10
-d '{
_10
"query": "query($locale: String!) { entries(contentTypeId: \"landingPage\", locale: $locale) { entries { id name } scroll } }",
_10
"variables": { "locale": "en-US" }
_10
}'

Pass the scroll value from the response to the next request. Repeat until scroll is null:


_10
curl -s "https://api.vtexcommercestable.com.br/api/content-platform/graphql/{account}/{storeId}" \
_10
-H "Content-Type: application/json" \
_10
-H "Authorization: Bearer $TOKEN" \
_10
-d '{
_10
"query": "query($locale: String!, $scroll: String) { entries(contentTypeId: \"landingPage\", locale: $locale, scroll: $scroll) { entries { id name } scroll } }",
_10
"variables": { "locale": "en-US", "scroll": "eyJ..." }
_10
}'

The cursor in this example is a placeholder. Use the cursor returned by your store to avoid an INVALID_SCROLL error.

Typed entry by slug


_10
curl -s "https://api.vtexcommercestable.com.br/api/content-platform/graphql/{account}/{storeId}" \
_10
-H "Content-Type: application/json" \
_10
-H "Authorization: Bearer $TOKEN" \
_10
-H "X-Content-Schema: vtex.faststore@4.0.1" \
_10
-d '{
_10
"query": "query Page($slug: String!, $locale: String!) { landingPage(slug: $slug, locale: $locale) { id slug seo { title } } }",
_10
"variables": { "slug": "/home", "locale": "en-US" }
_10
}'

Typed entry list


_10
curl -s "https://api.vtexcommercestable.com.br/api/content-platform/graphql/{account}/{storeId}" \
_10
-H "Content-Type: application/json" \
_10
-H "Authorization: Bearer $TOKEN" \
_10
-H "X-Content-Schema: vtex.faststore@4.0.1" \
_10
-d '{
_10
"query": "query($locale: String!) { landingPageList(locale: $locale) { entries { id slug } scroll } }",
_10
"variables": { "locale": "en-US" }
_10
}'

Generic SDL


_10
curl -s "https://api.vtexcommercestable.com.br/api/content-platform/graphql/{account}/{storeId}/schema.graphql" \
_10
-H "Authorization: Bearer $TOKEN"

Typed SDL


_10
curl -s "https://api.vtexcommercestable.com.br/api/content-platform/graphql/{account}/{storeId}/schema.graphql?schema=vtex.faststore@4.0.1" \
_10
-H "Authorization: Bearer $TOKEN"