Marketplace Selling

Zalando Partner API: zDirect Auth, Endpoints and Rate Limits

Zalando Partner API (zDirect) explained: OAuth token flow, endpoint map, product onboarding, price and stock batching, order flow and real rate limits.

Long Nguyen Avatar

Long Nguyen

Fullstack Developer · AI Engineer · Researcher

• • 5 min read •

What is the Zalando Partner API?

The Zalando Partner API is the REST API that brands and retailers in the Zalando Partner Program use to list products, push prices and stock, and process orders without touching the zDirect web portal. Zalando's own documentation calls it the zDirect API, so if you search the developer portal for 'Partner API' you will find very little. They are the same thing.

Three facts shape every integration, and all three come from Zalando's zDirect API overview (checked October 2026):

  • It is partner-only. Credentials are issued per app in the Applications section of the zDirect Portal. There is no self-serve public key.
  • It is OAuth 2.0 over HTTPS only. Every call carries a Zalando-issued access token.
  • Almost every path is scoped to a Merchant ID. Zalando also calls this the BPID, and most write calls additionally need a sales channel code.
EnvironmentBase URLWho can call it
Productionhttps://api.merchants.zalando.comProduction clients only
Sandboxhttps://api-sandbox.merchants.zalando.comSandbox and production clients

How Zalando Partner API authentication works

Authentication is the OAuth 2.0 client_credentials grant. You send the app's client ID and secret as HTTP Basic auth to /auth/token with a form-encoded body, and you get back a JWT access token with expires_in: 7200, which is two hours. There is no refresh token; when the token expires you request a new one with the same credentials.

Two details save time:

  • Cache the token. Requesting a token per API call is the most common mistake in marketplace integrations. One token serves two hours of traffic, so keep it in memory or a shared cache and renew a few minutes early.
  • Ignore scope=access_token_only in old tutorials. It has been the default behaviour since . Sending it still works; it just does nothing.

Your Merchant ID does not need to be hard-coded. /auth/me returns it for the authenticated client:

import time
import requests

# Sandbox host. The same paths sit under https://api.merchants.zalando.com in production.
BASE = 'https://api-sandbox.merchants.zalando.com'


class ZalandoAuth:
    def __init__(self, client_id, client_secret):
        self.creds = (client_id, client_secret)
        self.token = None
        self.expires_at = 0

    def header(self):
        # Renew 5 minutes before the 7200-second lifetime runs out.
        if time.time() > self.expires_at - 300:
            r = requests.post(
                f'{BASE}/auth/token',
                auth=self.creds,
                data={'grant_type': 'client_credentials'},
                timeout=30,
            )
            r.raise_for_status()
            body = r.json()
            self.token = body['access_token']
            self.expires_at = time.time() + body['expires_in']
        return {'Authorization': f'Bearer {self.token}'}


auth = ZalandoAuth('YOUR_CLIENT_ID', 'YOUR_CLIENT_SECRET')
me = requests.get(f'{BASE}/auth/me', headers=auth.header(), timeout=30)
print(me.json())  # includes your Merchant ID (BPID)

Access is also limited by scopes assigned to the app in the zDirect Portal. A token that authenticates fine can still be refused on a specific API if the scope is missing:

APIScope
Product SubmissionsProducts/products/write
PricesProducts/price/write
Stocksproducts/stock/write
OrdersOrders API/orders with Read for GET, Write for PATCH

Zalando Partner API endpoints at a glance

The API is a set of separate services behind one host. Which ones you need depends on your fulfillment model: Partner Fulfillment (you ship) or Zalando Fulfillment Solutions, ZFS (Zalando ships from its warehouses).

APIPathWhat it doesUsed by
Authentication/authIssues access tokens, returns Merchant IDEveryone
Products/merchants/{merchant_id}/productsChecks if an EAN already exists in the Zalando CatalogEveryone
Product Attributes/merchants/{merchant_id}/outlines, /attribute-typesReturns the attributes required per product typeEveryone
Product Submissions/merchants/{merchant_id}/product-submissionsSubmits a new product to the catalogEveryone
Prices/merchants/{merchant_id}/pricesSubmits regular and promotional pricesEveryone
Price Reporting/merchants/{merchant_id}/price-attemptsReturns price validation results (optional)Everyone
Stocks/merchants/{merchant_id}/stocksSets stock quantitiesPartner Fulfillment
Offer Blocking/merchants/{merchant_id}/offer-blockerBlocks and unblocks offersEveryone
Orders/merchants/{merchant_id}/ordersReports order status (Partner Fulfillment) or reads it (ZFS)Everyone
Item Quantities/zfs/item-quantitiesReturns your stock held in Zalando warehousesZFS
Shipping Notices/zfs/shipping-noticesAnnounces inbound shipments to ZalandoZFS
Stock Movements/zfs/received-items/{merchant_id}Tracks receiving and returns of your stockZFS
Product Status Report/graphqlReturns product status, live price and stockEveryone

One design choice catches teams out: the Prices API is write-only. You cannot GET the current price of an offer from it. The read side lives in the Product Status Report, which is a GraphQL API, so a full integration ends up speaking both REST and GraphQL.

How a Zalando Partner API integration fits together

Zalando splits the work into two stages: Initial Integration, which ends at Go Live, and Live Business Management, which never ends. In practice the first stage is a one-off project and the second is a set of background jobs you operate.

  1. Check the catalog. Look up each EAN with the Products API. If Zalando already has the product, you map to it and skip submission entirely.
  2. Fetch required attributes. For products that are new to Zalando, the Product Attributes API tells you which attributes that product type needs.
  3. Submit the product. One JSON document per product goes to the Product Submissions API.
  4. Wait for validation. Zalando runs a three-step validation. You read the result in the Product Status Report, fix what failed, and resubmit.
  5. Set prices and stock. A price is required per sales channel. Both can be sent before onboarding finishes; Zalando holds them and applies them once the product is accepted.
  6. Go live, then operate. Orders, stock and price changes run continuously from here.

Step 5 is worth using deliberately. Because queued prices and stock are applied on acceptance, you can submit everything in one pass instead of building a second job that waits for approval before pricing.

Sending price and stock updates without getting throttled

Prices and stock are batch APIs with two limits that apply at the same time: a cap on requests per minute and a cap on individual updates per minute. Whichever you hit first blocks you until the next minute starts.

APIMax per requestRequests / minuteUpdates / minute
Stocks (production and sandbox)1,000 updates602,000
Prices (production)1,000 unique EANs308,000
Prices (sandbox)1,000 unique EANs208,000

What those numbers mean when you design the sync:

  • A full stock refresh is slow by design. 2,000 updates a minute is 120,000 an hour. A catalog of 20,000 EANs sold in 6 sales channels is 120,000 EAN and channel pairs, so a full push takes a full hour. Send only what changed and keep the full refresh for a nightly reconciliation.
  • One-item requests are the worst case. If your warehouse system fires one API call per stock change, the 60 requests limit caps you at 60 updates a minute, 3% of the available throughput. Buffer changes for a few seconds and send them as one batch.
  • Deduplicate before you send. A stock batch may not contain the same sales_channel_id and ean pair twice. If an item changed three times inside your buffer window, keep the last value only.
  • ZFS stock is not yours to set. ZFS partners cannot use the Stocks API. Stock enters through shipping notices and is read back from Item Quantities.

The helper below does the deduplication and chunking. The exact request body lives in Zalando's Stocks reference, so the rows here carry only the two key fields the batch rule is defined on:

def stock_batches(updates, size=1000):
    latest = {}
    for u in updates:
        # Last write wins per (sales_channel_id, ean) pair.
        latest[(u['sales_channel_id'], u['ean'])] = u
    rows = list(latest.values())
    for i in range(0, len(rows), size):
        yield rows[i:i + size]


# Two full batches use the whole 2,000 updates/minute allowance,
# so pace the loop: at most 2 batches of 1,000 per minute.

This throttling and reconciliation layer is most of the real work in a marketplace connector, and it is the same problem on every channel with different numbers. It is what we build in our marketplace integration service for sellers who run several channels from one inventory source.

Processing orders with the Zalando Orders API

Under Partner Fulfillment, Zalando creates the order and your system drives it to completion. There is a sequence to respect:

  1. Retrieve orders that are ready for export with a GET on the orders endpoint.
  2. Export each order with a PATCH. This is your acknowledgement that you will process it. Fulfillment starts once the order is approved.
  3. Ship and PATCH the order lines with shipping information. Zalando strongly recommends including tracking and return tracking numbers.
  4. Close each order line as shipped or canceled. Status is tracked per line, not per order, so partial shipments and partial cancellations are normal.
  5. Handle returns by marking the line as returned.

At order level the statuses you will see are Initial, Approved and Fulfilled. Under ZFS the same API becomes read-only for you: Zalando sets the line statuses and you poll to keep your own system in sync.

Things the overview page mentions briefly that matter a lot in production:

  • Your SKU arrives as external_id on the order items endpoint. Map on that, not on EAN, if one EAN can map to several internal SKUs.
  • Orders older than one year are not returned. If finance or returns need longer history, store orders on your side from day one.
  • The order list quota is counted per partner, not per app. If an ERP connector and a custom script both poll the same merchant, they share one allowance.

Zalando Partner API rate limits

There is no single global limit. Each API publishes its own policy, and the Orders API alone has seven. All figures are requests per minute unless stated otherwise:

RequestSandboxProductionCounted per
GET multiple orders1040Partner
GET orders by order_number100400Partner
GET order items402,000Partner
GET order lines402,000Partner
PATCH orders402,000Partner
GET orders deletion requests1040Partner
Other Orders API requests402,000App
Product Submissions25 / second25 / secondApp
Product Attributes1,000Client

Zalando describes the higher order_number allowance as temporary, meant for migration, so do not build a polling strategy that depends on it.

When you exceed a limit the API answers HTTP 429 with a Retry-After header in seconds and an X-Rate-Limit header showing the limit for that operation. Per Zalando's rate limiting guide, calling again before that time has passed can earn you further limiting, so a blind exponential backoff is the wrong tool. Read the header and wait:

def call(method, url, auth, **kwargs):
    for attempt in range(5):
        r = requests.request(method, url, headers=auth.header(), timeout=30, **kwargs)
        if r.status_code != 429:
            return r
        time.sleep(int(r.headers.get('Retry-After', '60')) + 1)
    raise RuntimeError('still rate limited after 5 attempts')

The limit that hurts first is rarely the one people expect. Sandbox allows only 10 order list calls a minute, so an automated test suite that lists orders in every test will fail on 429 long before it fails on logic. Throttle the test client or reuse one fetched list across tests.

What the Zalando sandbox does and does not prove

The sandbox validates the shape of your requests. It does not prove your data is right, and it does not keep what you send.

AreaSandbox behaviourWhat you still have to verify in production
Product submissionsSame automated validation as production. HTTP 200 means structure and required attributes are correct. Never persisted, never shown in the Product Status Report.Whether attribute values are actually correct. Production adds manual and automated content checks.
PricesSame pre-validation rules and response codes. New prices are not visible anywhere.Price validation outcomes, read through Price Reporting or the Product Status Report.
StockValidated on submission, then discarded.That stock arrives and offers go live.
OrdersAll GET and PATCH requests work. Customer names, addresses and prices are mock data.Real address formats, real carriers, real return flows.

Plan for this in your rollout. A sandbox pass tells you the connector is wired correctly. The first production submissions should be a small batch of products you can inspect by hand in the Product Status Report before you send the full catalog.

A build checklist before you start

  • Create the app in the zDirect Portal and assign every scope you need up front.
  • Store client ID and secret in a secrets manager. Zalando explicitly warns against keeping them in a repository or sending them over email or chat.
  • Cache the access token for its two-hour lifetime.
  • Resolve your Merchant ID from /auth/me and your sales channel codes from the Sales Channels API at startup.
  • Build stock and price sync as buffered, deduplicated batches of up to 1,000, paced to the per-minute update limits.
  • Treat order lines, not orders, as the unit of fulfillment state.
  • Honour Retry-After on every 429.
  • Persist orders yourself. The API forgets them after a year.

If Zalando is one channel among several and you would rather not maintain this yourself, request a free quote and tell us which systems hold your products, stock and orders. Integration work is priced to scope.

FAQ

Frequently asked questions

Is the Zalando Partner API the same as the zDirect API?

Yes. Zalando's developer documentation names it the zDirect API. 'Zalando Partner API' is the common search term for the same set of REST services that partners in the Zalando Partner Program use for products, prices, stock and orders.

How long does a Zalando API access token last?

The token response returns expires_in: 7200, which is two hours. No refresh token is issued, so you request a new token with the same client ID and secret when the current one is about to expire.

What are the Zalando Partner API rate limits?

Limits are set per API. Stocks allows 60 requests and 2,000 stock updates per minute. Prices allows 30 requests and 8,000 price updates per minute in production. Listing orders allows 40 requests per minute in production and 10 in sandbox. Product Submissions allows 25 requests per second. Exceeding a limit returns HTTP 429 with a Retry-After header.

Can I read current prices from the Zalando Prices API?

No. The Prices API is write-only. To read the live price, stock and status of a product you use the Product Status Report, available in the zDirect interface and as a GraphQL API.

Does the Zalando Partner API work with Zalando Fulfillment Solutions (ZFS)?

Yes, with a different set of endpoints. ZFS partners cannot set stock through the Stocks API. They announce inbound shipments with Shipping Notices, read warehouse stock from Item Quantities, track receiving and returns with Stock Movements, and use the Orders API to read order status that Zalando sets.

Does a successful sandbox test mean my products will be accepted?

No. A 200 response in sandbox confirms the JSON structure and required attributes are valid. Sandbox submissions are never persisted, and production adds manual and automated checks on whether the attribute values themselves are correct.

Stay updated with Netalith

Get coding resources, product updates, and special offers directly in your inbox.