This document gives an overview of the characteristics and common features of the API.

The HTTP API of the commercetools platform provides an interface for programmatic access to the data stored on the commercetools platform and the associated functionality. While it can be used directly, for experimentation or custom integration solutions, we recommend taking a look at our available client libraries and SDKs, which usually provide a more comfortable development experience.

In order to provide the best possible SLA and minimal latency, the commercetools platform API is being provided in two different regions:
Europe and United States.
These regions are completely isolated from each other and no data is transferred between them.

Platform accounts created for one region are not valid for the other one; so a signup is required for each region individually:

In case you need advice in which region your project should be located, please get in touch with us.


The HTTP API for projects on the commercetools platform is currently provided at the following URLs that is assumed to be prepended to all endpoints in the specific APIs:

Illegal request handling

Requests get blocked if any of the following conditions are true:

It is strongly advised to use the host and domain names in your applications instead of hardcoding any IP address of the API hosts. IP addresses can change any time without prior notice.


In addition to the alternative scopes listed for each API operation, the scope manage_project:{projectKey} is always an implied valid scope. It grants full API access for all operations within that specific project.


The majority of the services consume and produce standard application/json payloads for the time being. Please use Content-type: application/json in your HTTP header when you are sending POST requests to our APIs so that the payload is encoded correctly. Consumers of JSON responses should ignore any unrecognized fields encountered in JSON objects of responses, or deal with them in a way that does not cause the client application to crash. The services will assume clients behave properly in that manner and thus consider the addition of new fields to be backwards-compatible with respect to existing clients. Clients should also not rely on the presence of undocumented fields. Fields that are returned by a service but are not documented can be renamed or disappear at any time without prior notice.


Many resources contain a string field id which uniquely identifies a resource. The ID is generated server-side on resource creation.

Some resources have a string field key which also uniquely identifies a resource. The key identifier is always assigned to a resource by the client.

Update Guarantees

Depending on the API request the platform provides different guarantees for which the update will be applied.

Strong Consistency

Each resource provides read-after-write consistency. It means that when you update an entity, wait for the successful response, and then read the same entity, you will see the changes you just made.

Eventual Consistency

Some updates will not be visible right after the update. For example, if you change a discount, the update on the discount itself provides read-after-write consistency. But the prices on all affected products are updated with a delay. When you read a price just after having changed the discount, you may read the old discount value. If you wait a bit, and retrieve the price again, you will read the new value.

After updating a product, the Product Projection Search index will be updated with a delay. A search result right after an update may not reflect this last update. When trying later, the search index will be updated.

These delayed updates provide an Eventual Consistency guarantee. It means that after a while, what you can read reflect all past updates.

These delays can vary depending on the amount of data to update. We provide our best efforts to keep these latencies as small as possible.

Optimistic Concurrency Control

Many resources exposed on the API employ optimistic concurrency control to prevent lost updates when making changes to existing data concurrently. Such resources carry around a version. When sending (partial) updates to such resources, the expected version of a resource needs to be a part in the representation sent with the request. The HTTP response body contains the new version of the resource.

A client must wait for the new version before sending follow-up requests, trying to guess the next version is considered bad practice.

Note that the current API deliberately does not use ETag + If-Match headers for the purpose of optimistic concurrency control. Future versions of the API may support ETags together with If-None-Match headers for the purpose of reducing network traffic.

In case of version mismatch, the API delivers a 409 HTTP status (Conflict).


All API access is over HTTPS. API authorization uses ↗ OAuth2 bearer tokens. See the section on authorization for details.

Query Features

Common query features include filtering, sorting, paging and reference expansion. Certain aspects of these features are standardized across all parts of the API as described in the following sections.

Single Resource Query Response

Query responses for single resources only return a single, top-level JSON object.


For query responses of requests supporting paging via limit and offset, the following common top-level fields are returned:


If queries support ad-hoc filtering of resources through flexible predicates, they do so via the where query parameter that accepts a single (possibly compound) predicate to determine whether a specific resource representation should be included in the result.

The structure of predicates and the names of the fields follow the structure and naming of the fields in the documented response representation of the query results.

Examples of predicates:

// Compare a field's value to a given value
name = "Peter"
name != "Peter"
age < 42
age > 42
age <= 42
age >= 42
age <> 42

// Combine any two conditional expressions in a logical conjunction / disjunction
name = "Peter" and age < 42
name = "Peter" or age < 42

// Negate any other conditional expression
not (name = "Peter" and age < 42)

// Check whether a field's value is or is not contained in
// a specified set of values.
age in (42, 43, 44)
age not in (42, 43, 44)

// to be noted: 'in' is much more efficient than several '='
// prefer:
name in ("Peter", "Barbara")
// to:
name = "Peter" or name = "Barbara"

// Check whether an array contains all or any of a set of values
tags contains all ("a", "b", "c")
tags contains any ("a", "b", "c")

// Check whether an array is empty
tags is empty

// Check whether a field exists & has a non-null value
name is defined
name is not defined

// Descend into nested objects
dog(age < 7 and name = "Beethoven")

// Descend into nested arrays of objects
cities(zip > 10000 and zip < 20000)

// Query GeoJSON field within a circle
// The two first parameters are the longitude and latitude of the circle's center.
// The third parameter is the radius of the circle in meter.
geoLocation within circle(13.37770, 52.51627, 1000)

// Query for ProductProjections with attribute values
// - to get all results add a predicate of the same form, but starting with 'masterVariant' instead of 'variants'
// - to query for Products instead enclose the examples with 'masterData(current(<example>))' or 'masterData(staged(<example>))'
// ---------------------------
// for missing attribute

// for single attribute value of TextType
variants(attributes(name="attribute-name" and value="attribute-value"))
// for multiple attribute values of TextType with same name
variants(attributes(name="attribute-name" and value in ("attribute-value-1", "attribute-value-2")))

// for single attribute value of LTextType
variants(attributes(name="attribute-name" and value(en="attribute-value")))
// for multiple attribute values of LTextType with same name
variants(attributes(name="attribute-name" and value(en="english-value" or de="german-value")))

// for EnumType or LocalizableEnumType
variants(attributes(name="attribute-name" and value(key="enum-key")))

// for MoneyType (currencyCode is required)
variants(attributes(name="attribute-name" and value(centAmount=999 and currencyCode="EUR")))
// for MoneyType with centAmount within a specific range (currencyCode is required)
variants(attributes(name="attribute-name" and value(centAmount > 999 and centAmount < 1001 and currencyCode="EUR")))

// for NumberType
variants(attributes(name="attribute-name" and value=999))
// for NumberType with value within a specific range
variants(attributes(name="attribute-name" and value > 999 and value < 1001 ))

// for DateType, TimeType or DateTimeType
variants(attributes(name="attribute-name" and value="attribute-value"))
// for DateType, TimeType or DateTimeType with a value within a specific range
variants(attributes(name="attribute-name" and value > "value-start" and value < "value-end"))

// for ReferenceType
variants(attributes(name="attribute-name" and value(typeId="reference-type-id" and id="reference-id")))

A query endpoint usually restricts predicates to only be allowed on a specified subset of a resource representation’s fields. The documentation of the endpoint will therefore list fields that can be used for constructing predicates.

If multiple predicates are specified via multiple where query parameters, the individual predicates are combined in a logical conjunction, just as if they had been specified in a single where query parameter and combined with and.

Note: The encoding of the predicates is UTF-8 and the predicate must be URL-encoded in the HTTP request.

Example predicate for a Product

# decoded predicate
masterData(current(slug(en="peter-42") and name(en="Peter")))

# URL-encoded predicate


A query endpoint that supports sorting does so through the sort query parameter. The provided value must be a valid sort expression.
The default sort direction is ASC. The allowed sort paths are typically listed on the specific query endpoints.

Here are some examples of sort expressions:

  name desc
  dog.age asc

If multiple sort expressions are specified via multiple sort parameters, they are combined into a composed sort where the results are first sorted by the first expression, followed by equal values being sorted according to the second expression, and so on.

Note: The sorting is case sensitive.


Queries on collections of resources can optionally provide paging functionality through limit and offset query parameters, thereby allowing clients to request a certain page of the complete query result.


The maximum number of results to return from a query can be set using the limit query parameter. Allowed is a value between 1 and 500. The default limit on most query endpoints is 20.


The offset into the results matching the query can be set using the offset query parameter. An offset of 0 is the default value indicating that no results should be skipped.

Note: When iterating on all elements from an endpoint (for example to import/export all products), using offset for that is not appropriate:

In cases where you want to iterate over all elements while avoiding offset you can follow this alternative approach instead:

The following pseudo-code demonstrates how to iterate on all elements returned by an endpoint, using chunks from 100 elements at each iteration:

lastId = null
continue = true
while (continue) {
  if (lastId == null)
    response = client.get(endpoint + "?limit=100&sort=id")
    response = client.get(endpoint + "?limit=100&sort=id&query=id%3E" + lastId) // "id%3E" is "id >" encoded
  results = response.results
  continue = (results.size == 100)
  lastId =
  // do something with result

Cart Predicates

The Cart predicate offers a flexible way to define the conditions under which e.g. a discount or a shipping method can be applied to a cart. Cart predicates are structured as generic predicates by using specific fields listed below.

The following field identifiers can be used in a CartPredicate. They reference fields in a Cart.

The following identifiers referencing fields in an Address can be used in a CartPredicate.

The following identifiers referencing fields in a TaxRate can be used in a CartPredicate.

CartPredicate Functions

The following functions can be used in a cart predicate. All functions accept a LineItemPredicate or CustomLineItemPredicate as an argument, which allows you to limit function to the subset of matching (custom) line items.

Please note, that you can provide an empty predicate like 1 = 1 or true = true if you want to sum or count all (custom) line items.

CartPredicate Examples

// matches a cart with total line item cost bigger or equal to 10 USD (which excludes other costs, like shipping)
lineItemTotal(1 = 1) >  "10.00 USD"

// matches a cart only when it has exactly 2 like items that have product with size "xxl" or "xl"
lineItemCount(attributes.size in ("xxl", "xl")) = 2

// matches a cart by customer information = "" and = "f6a19a23-14e3-40d0-aee2-3e612fcb1bc7"

// matches a cart with a minimum total price and at least one lineItem that satisfies a price, a productType, a size attribute or a specific product
totalPrice > "800.00 EUR" and lineItemCount(price > "10.50 EUR" and = "f6a19a23-14e3-40d0-aee2-3e612fcb1bc7" and attributes.size in ("xl", "xxl") or = "abcd9a23-14e3-40d0-aee2-3e612fcbefgh") > 0

// matches a cart with custom.bookingStart = 24.11.2016 and custom.bookingEnd = 04.12.2016
custom.bookingStart = "2016-11-24" and custom.bookingEnd = "2016-12-04"

// matches a cart for a family (at least 2 adults and at least one youth)
lineItemCount(custom.age = "adult") >=2 and lineItemCount(custom.age = "youth") >=1


The LineItemPredicate offers a flexible way to specify the line items that should be used in the predicate. LineItemPredicates are structured as generic Predicates by using specific fields listed below.

LineItemPredicate Fields

The following field identifiers can be used in a predicate. They reference fields in a LineItem.

LineItemPredicate Examples

// matches all line items
1 = 1

// matches line item with SKU "SKU-123" only if the price is a net price
sku = "SKU-123" and taxRate.includedInPrice = false

// matches a line item by product type, a specific product and at least 3 'rating' attributes = "f6a19a23-14e3-40d0-aee2-3e612fcb1bc7" and attributes.rating > 3 and ( = "abcd9a23-14e3-40d0-aee2-3e612fcbefgh" or = "ba3e4ee7-30fa-400b-8155-46ebf423d793")

// matches a line item that has the custom field "gender" to be "alien"
custom.gender = "alien"

//matches a line item that is not in a given category != ("f6a19a23-14e3-40d0-aee2-3e612fcb1bc7")


The CustomLineItemPredicate offers a flexible way to define which CustomLineItem should be used in the predicate. Custom line item predicates are structured as generic Predicates by using the specific fields listed below.

CustomLineItemPredicate Fields

The following field identifiers can be used in a predicate. They reference fields in a CustomLineItem.

CustomLineItemPredicate Examples

// matches all custom line items
1 = 1

// matches custom line items with price of individual items bigger than 10.50 EUR only if the price is a net price
money > "10.50 EUR" and taxRate.includedInPrice = false

// matches a custom line item by slug
slug = "adidas-superstar-2"

// matches a custom line item that has the custom field "gender" to be "alien"
custom.gender = "alien"

Predicate on custom fields

A predicate on a custom field has the following form:

This predicate matches a custom field by its name.

The following field types are supported: Boolean, String, Number, DateTime, Date, Time, Enum, LocalizedEnum and Reference. Those types are also supported for SetType of them.

Predicates on reference matches on the ID.

Furthermore the field type Money is supported, but not for SetType of it.
Predicates on Money can match:

If a field name contains a dash (-) or starts with a digit, it needs to be escaped with backticks (`), e.g., custom.`1stYear`.

Predicate Operators

See ProductDiscount operators.

Resource Timestamps

Many resources exposed on the API contain the createdAt and the lastModifiedAt timestamps. Those timestamps are automatically set by the server on creation or on modification of the resource. They can be used as predicates and for sorting in queries.

Reference Expansion

Reference expansion is a feature of the resources listed in the table below that enables clients to request server-side expansion of Reference resources, thereby reducing the number of required client-server roundtrips to obtain the data that a client needs for a specific use-case.

Reference expansion can be used when creating, updating, querying, and deleting these resources.

Endpoints supporting Reference Expansion on creation, update, query, and deletion
Carts CartDiscounts Categories Channels
Customers CustomerGroups DiscountCodes State
Inventory Messages Orders Products
ProductDiscounts ProductProjections ProductTypes Reviews
ShippingMethods TaxCategories Types Zone

The JSON snippet below is taken from an Example-Product representation that contains two references: one to an Example-Product-Type and one to an Example-TaxCategory:

  "id": "<example-product-id>",
  "version": 4,
  "productType": {
    "typeId": "product-type",
    "id": "<example-product-type-id>"
  "taxCategory": {
    "typeId": "tax-category",
    "id": "<example-tax-category-id>"

Without Reference Expansion you’ll get such a representation as response from the query endpoints.
With Reference Expansion you’ll get the expanded objects embedded into the product by an additional obj field as shown in the following JSON snippet:

  "id": "<example-product-id>",
  "version": 4,
  "productType": {
    "typeId": "product-type",
    "id": "<example-product-type-id>"
    "obj": {
        "id": "<example-product-type-id>",
        "version": 4,
        "name": "Example Product Type",
        "description": "example Product Type to showcase Reference Expansion",
        "classifier": "Complex",
        "attributes": [
  "taxCategory": {
    "typeId": "tax-category",
    "id": "<tax-category-id>"
    "obj": {
        "id": "<tax-category-id>",
        "version": 2,
        "name": "Standard tax category",
        "rates": [
            "name": "5% US",
            "amount": 0.05,
            "includedInPrice": false,
            "country": "US",
            "id": "zJJ5KIGH"
            "name": "19% MwSt",
            "amount": 0.19,
            "includedInPrice": true,
            "country": "DE",
            "id": "893f_nXJ"

All query endpoints that support reference expansion provide an expand query parameter which can be used to specify one or more expansion paths.

Expansion paths define the full path to the reference in a resource. Expansion path format:

The query for the Example Product introduced before - including both expansion paths - would look like this:


A more complex example is to expand all categories within a found product. The expansion path would be: masterData.current.categories[*].

The query would thus look like this:


Please find another example for expansion paths in our Product Bundle Tutorial.

The expansion path can also point to references in expanded objects.

Note that expansion of references does not cause requests to fail if not all of the references can be expanded (e.g. because the referenced resource cannot be found). The server will try to expand as much as possible but never beyond what the client requested. It is up to the client to decide which referenced resources it deems critical for completing a specific use-case, e.g. by either treating it as an error or by simply not rendering / returning the missing information.

Partial Updates

For partial updates to existing resources, a common approach used by the HTTP API is to accept a custom patch format in the form of a list of resource-specific actions that need to be processed. The request method for partial update requests is usually POST, but PATCH might also be supported in the future.

The general request representation (patch format) of a partial update request is as follows:

Example Request

  "id": "a136fd9e-bc29-4d05-813f-350b248aefd8",
  "version": 1,
  "actions": [
      "action": "setTitle",
      "title": "My title"
      "action": "setText",
      "text": "My text"

The concrete actions that can be used on a specific resource are documented together with the resource.

All actions provided in a specific resource’s patch format are generally freely combinable in a single request. Furthermore, such update requests only succeed or fail as a whole, i.e. unless explicitly stated otherwise, the result of all actions sent in the request is applied to a resource or none at all, if any of the actions fail.

Not all updates or state transitions of a resource may be freely composable with one another in a single HTTP request. For those updates, other endpoints may be provided to handle only a specific update or state transition in a single HTTP request.