Getting Price Update Reports

Overview

The Price Reporting API allows you to retrieve the status of the last 7 days of price updates you submitted, either over the Prices API or the Manage Prices UI in zDirect.

Note

Depending on your requirements you can also use the zDirect Price Management UI to get the validation results of last the price updates submitted.

You can query by EANs, sales_channel_ids and specific time ranges.

The Price Reporting API is cursor-based. This means that it returns a list of results. If the complete result set does not fit into a single response, the cursor can be used to iterate through the result set.

Price Reporting API specification

  • Price Reporting API reference: Information on scopes, rate limiting, and sandbox behavior.

  • OpenAPI reference for the Price Reporting API.

Price update lifecycle

The purpose of the Price Reporting API is to allow you to understand how your price updates have been processed, whether they are live, still waiting to be validated or have been rejected due to errors. To that effect, the Price Reporting API will provide a state for each price update in addition to the transitions which have led it to the current state.

The initial state for a price update will always be RECEIVED. Once processed by the Price API, it will either go to REJECTED (if it failed the Price API pre-validation), ACCEPTED (if accepted by the Price API but still needs to be processed by the validation system) or AWAITING_ONBOARDING if the price update's EAN has not been onboarded yet.

From the states ACCEPTED or AWAITING_ONBOARDING it can go to REJECTED if rejected by the asynchronous validation system or SUBMITTED if accepted. Both REJECTED and SUBMITTED are final states, meaning there will be no more state changes for that price update. Once a price update reaches the SUBMITTED state, it will be live in the store shortly after.

Note

Solid arrows represent synchronous updates, dashed arrows represent asynchronous updates

Scheduled price updates have a similar lifecycle. Howeverm, there is also an additional state SCHEDULED between ACCEPTED and SUBMITTED. Scheduled price updates that have gone through the validation system successfully will go to the SCHEDULED state and will then transition to the SUBMITTED state once ready to be applied.

Validation system

Price Updates will pass through further validation after being initially accepted. The validation can result in status messages with one of 3 different severity levels:

Severity Description
INFO This message is for your information. The price update will be applied as expected.
WARNING The price update looks suspicious. It may be rejected depending on the value of the ignore_warnings flag. See section Enforcing Warnings for more details.
ERROR The price update is invalid and will be rejected.

Below are the possible status messages along with their codes and severity levels:

Warning

The set of validation messages is continuously changing as we improve the validation system to better catch potential errors before they reach the customer.

Code Message Severity
REJECTED_CURRENCY_DOES_NOT_MATCH_SALES_CHANNEL Error - You need to set the price in local currency. ERROR
REJECTED_REGULAR_PRICE_LOWER_EQUAL_THAN_EUR_PRICE Error - The regular price in PLN, SEK, DKK, NOK, CZK, HRK, RON, HUF is the same as or less than the regular price in EUR. Please check that you have calculated the currency conversion correctly. ERROR
REJECTED_REGULAR_PRICE_TOO_HIGH Error - The regular price is equivalent to more than 6000 EUR. If this is a mistake, please change the price. If this isn’t a mistake, please contact Zalando for help. ERROR
REJECTED_PRICE_TOO_LOW Error - The regular price is equal to or less than the equivalent of 1 EUR. ERROR
REJECTED_CZK_INVALID_SUBUNIT_PRICE Error - Prices in CZK should not contain subunits below 1 CZK. Please check that you have set prices correctly. ERROR
REJECTED_HUF_INVALID_PRICE Error - Prices in HUF should be in incremental steps of 5 and not contain subunits below 1 HUF. Please check that you have set prices correctly. ERROR
DISCOUNT_RATE_TOO_HIGH Warning - The promotional price has been reduced by more than 80% of the regular price. If this is a mistake, please change the promotional price. If this isn’t a mistake, you can ignore this warning. WARNING
REGULAR_PRICE_CHANGE_TOO_LOW Warning - The regular price has been reduced by more than 60%. If this is a mistake, please change the price. If this isn’t a mistake, you can ignore this warning. WARNING
REGULAR_PRICE_CHANGE_TOO_HIGH Warning - The regular price has increased by more than 330% of the current regular price. If this is a mistake, please change the price. If this isn’t a mistake, you can ignore this warning. WARNING
NEW_REGULAR_PRICE_TOO_LOW Warning - The regular price is 60% lower than the equivalent price in EUR. If this is a mistake, please check that you have calculated the currency conversion correctly. If this isn’t a mistake, you can ignore this warning. WARNING
INITIAL_REGULAR_PRICE_TOO_LOW Warning - The regular price is 60% lower than the equivalent price in EUR set by other partners on zDirect. If this is a mistake, please check that you have calculated the currency conversion correctly. If this isn’t a mistake, you can ignore this warning. WARNING

Getting the status of a Price Update

Keep in mind that you can only query the history of price updates for the last 7 days! Follow these two steps to retrieve the status of your price update attempts:

  1. Prepare a JSON payload specifying the criteria of articles you wish to query the results for
  2. Submit the HTTP Request with the JSON payload to the Price Reporting API.

Prepare a JSON payload for the Price Reporting API

You can query by EANs, sales_channel_ids, specific time ranges when price updates were requested or time ranges when price updates statuses were changed. The query filter criteria are cumulative. It means that by providing a list of EANs and e.g. Sales Channel for Germany, you will only see the price this article has in Germany. You may use multiple sales channels in your query. (If no query is provided, all articles for all Sales Channels will be returned.)

However you can query only either by time ranges when price updates were requested (start and end criteria), or by time ranges when price updates statuses were changed (modified_since and modified_until). Querying by both is not supported and would result in an error.

All timestamp fields must be in ISO-8601 extended date-time format with an offset from UTC, as defined by RFC 3339, section 5.6, with a resolution up to a microsecond. Examples: "2021-02-02T16:30:15.000Z", "2021-02-02T17:30:15.000+01:00".

By default, this API returns 100 price updates per page. You can change it by providing a page_size between 1 and 1000 in the query. page_size > 1000 will be capped at 1000 and page_size < 1 will be set to 100.

This is an example of a JSON payload to be used in an HTTP Request to be made to the /price-attempts endpoint:

{
  "sales_channels": ["$SALES_CHANNEL"],
  "eans": ["$EAN1", "$EAN2", "$EAN3"],
  "modified_since": "$MODIFIED_SINCE",
  "modified_until": "$MODIFIED_UNTIL",
  "page_size": 200
}

Price Reporting JSON Requirements

General Requirements

  • All fields and values are optional. If no query is provided, all articles will be returned for all Sales Channels.
  • A query can not be changed while iterating. It must be resubmitted for every follow-up request.

Follow-up Requests

After submitting a request with a query, the response will contain the results in the items field (if there are price updates that match the provided query).

If the result set does not fit in a single response, there will be a cursor field, with a next field provided in the response body. The value of the next field is the URL that you can use to make a follow-up request to get the next batch of results.

Warning

The follow-up request should contain the same query used in the original request to provide you with the next set of results

Note

This process should be repeated until there is no cursor field provided in the response, which will mean that this response contained the last set of results.

Price Reporting API - JSON Payload Request Example

This example illustrates a JSON that would be used to query the price update results of an EAN in one sales channel, which had its status updated between 2020-05-12T08:00:00Z and 2020-05-12T09:00:00Z:

{
  "sales_channels": ["01924c48-49bb-40c2-9c32-ab582e6db6f4"],
  "eans": ["5901234123457"],
  "modified_since": "2020-05-12T08:00:00Z",
  "modified_until": "2020-05-12T09:00:00Z",
  "page_size": 200
}

Alternatively, the following example illustrates a JSON that could be used to query the price update results of an EAN in one sales channel, submitted after 2020-05-12T08:00:00Z and before 2020-05-18T08:00:00Z:

{
  "sales_channels": ["01924c48-49bb-40c2-9c32-ab582e6db6f4"],
  "eans": ["5901234123457"],
  "start": "2020-05-12T08:00:00Z",
  "end": "2020-05-18T08:00:00Z",
  "page_size": 200
}

Querying by modified_since and/or modified_until is the preferred way as it allows client to reliably receive all status updates.

Use the Price Reporting API to Send the Request with the JSON Payload

Authentication

The zDirect API requires OAuth 2.0 authentication for all API calls. Use the Authentication API to generate access tokens as described in the Authentication section.

Submit the JSON

Use the Price Reporting API to submit the JSON:

POST /merchants/{$MERCHANT_ID}/price-attempts

The following command will POST the file price_report.json to zDirect:

http POST \
https://api-sandbox.merchants.zalando.com\
/merchants/e18e458a-de38-40ee-8119-4130eed7486a/price-attempts \
"Authorization:Bearer $YOUR_ACCESS_TOKEN" \
< price_report.json

Here is an example response:

{
  "cursors": {
    "next": "https://api-sandbox.merchants.com/merchants/e2ad171a-6b52-4db0-8ae3-54709720458b/price-attempts?cursor=ewpsYXN0OiAyNTkKb3JkZXI6IGFzYwp9"
  },
  "query": {
    "sales_channels": [
      "01924c48-49bb-40c2-9c32-ab582e6db6f4"
    ],
    "eans": [
      "5901234123457"
    ],
    "modified_since": "2020-05-12T08:00:00Z",
    "modified_until": "2020-05-12T09:00:00Z",
    "page_size": 200
  },
  "items": [
    {
      "ean": "5901234123457",
      "sales_channel_id": "01924c48-49bb-40c2-9c32-ab582e6db6f4",
      "base_price": {
        "regular_price": {
          "amount": 99.95,
          "currency": "EUR"
        },
        "promotional_price": {
          "amount": 80.95,
          "currency": "EUR"
        },
        "status": "ACCEPTED",
        "status_transitions": [
          {
            "from": "RECEIVED",
            "to": "ACCEPTED",
            "timestamp": "2020-05-18T08:00:00Z",
            "messages": []
          }
        ]
      },
      "scheduled_prices": [
        {
          "regular_price": {
            "amount": 99.95,
            "currency": "EUR"
          },
          "promotional_price": {
            "amount": 25.95,
            "currency": "EUR"
          },
          "start": "2020-05-20T08:00:00Z",
          "end": "2020-05-22T08:00:00Z",
          "status": "SCHEDULED",
          "status_transitions": [
            {
              "from": "RECEIVED",
              "to": "SCHEDULED",
              "timestamp": "2020-05-18T08:00:00Z",
              "messages": [
                {
                  "severity": "WARNING",
                  "message": "Promotional Price '25.95' is more than 70% below Regular Price '99.95'",
                  "code": "REGULAR_PRICE_CHANGE_TOO_LOW"
                }
              ]
            }
          ]
        }
      ],
      "ignore_warnings": true
    }
  ]
}

Understand the Response

After successful execution of the POST, you should receive an HTTP 200 response code. This response provides the items field, and optional query and cursors fields.

The items field includes all the results that matched the query provided in the original request. If there were no results matching the provided query, then this list will be empty.

If no query was provided in the original request, then the query field will be null.

Cursor

If the complete result set does not fit in a single response, there will be a cursor field, with a nested next field provided in the response body. Use the cursor to retrieve the next list of results.

Since there was a next cursor provided, this means you will have to submit a follow-up request to the URL https://api-sandbox.merchants.com/merchants/e2ad171a-6b52-4db0-8ae3-54709720458b/price-attempts?cursor=ewpsYXN0OiAyNTkKb3JkZXI6IGFzYwp9 to go to the next payload.

Status

The field status reflects the current status of a price update attempt. It can be one of the following:

Status Description
RECEIVED The price has been received.
ACCEPTED The price has been accepted & passed on for further validation.
REJECTED The price did not pass validation. See the status messages for further information.
SCHEDULED The price has been successfully validated & is waiting for submission.
SUBMITTED The price has been submitted and will be displayed in the fashion store shortly after.
AWAITING_ONBOARDING The price has been accepted, but the article has not yet been onboarded. The price will be applied when onboarding is completed.

Status Caveats

If successive price updates are submitted for a product and sales channel containing an identical price, they will be ACCEPTED by the system, but effectively ignored and will have no effect. This can be seen by inspecting the messages field in the response from the reporting API.

Status Transitions

The status transitions can contain messages providing you with further information about the outcome of your price update. Each Message has a code, a severity and an optional messsage.

The severity of a status message can be one of the following:

Severity Description
INFO For your information only. This message does not affect the outcome of your price update.
WARNING We observed something suspicious about your price update, e.g. a surprisingly high discount rate. A warning message can lead to rejection of the price update, depending on the value of the ignore_warnings flag. See Enforcing Warnings for additional information.
ERROR We observed something that indicates a potentially dangerous price update, e.g. a wrong currency. An error message causes immediate rejection of the price update.

Querying patterns

The Price Reporting API can be queried in many different ways which allows to construct queries that fit your specific use cases. Here are two common patterns that we recommend that could fit your use cases:

  • If you update prices infrequently and wish to check specific EANs that you have updated recently, you can use the eans parameter. If you want to avoid getting information about older, stale price updates you can use the start and end parameters to scope the results to a specific timeframe. Please note that it can take up to 60 minutes for submitted prices to appear in the Price Reporting API.

  • On the other hand, if you wish to batch process price update reports, it would be advised to use the modified_since field to get reports for price updates which have changed since your last call to the API. You will have to use the cursor system if the number of results exceeds the page size.

Contact Support