Marketplace Selling

Mirakl API Integration: Endpoints, Order Flow and Rate Limits

Mirakl API integration for sellers: authentication, product and offer imports, stock and price sync, the order workflow and the official rate limits.

Photo de profil de Long Nguyen

Long Nguyen

Développeur fullstack · Ingénieur IA · Chercheur

• • 6 min de lecture •

What a Mirakl API integration connects

A Mirakl API integration is the code that links your own systems (ERP, online store, warehouse or PIM) to a marketplace that runs on Mirakl, so that products, offers, stock, prices and orders move without anyone exporting spreadsheets. Retailers such as B&Q, La Redoute, Catch and PcComponentes run their seller marketplaces on Mirakl, and each one exposes the same seller API.

In practice the integration does three jobs: it publishes your catalog, it keeps stock and prices current, and it pulls orders in and pushes acceptance and tracking back. Everything below was checked against Mirakl's seller API reference on .

The first thing to get right is which API you are reading about, because "Mirakl API" covers three different surfaces:

API surfaceWho calls itAuthenticationBase URL
Seller APIs (shop role)A seller, or the agency or connector acting for the sellerAPI key sent in the Authorization headerhttps://your-instance.mirakl.net/api
Operator APIsThe retailer that owns the marketplaceOperator credentials, separate referenceThe operator's own instance
Connect Channel Platform APIsA sales channel plugging itself into Mirakl ConnectOAuth 2.0 bearer token (JWT) from a client ID and secret issued by Mirakl's Partner teamhttps://miraklconnect.com/api/channel-platform

If you sell on a Mirakl marketplace, you want the first row. The rest of this guide covers the seller APIs only.

Mirakl API authentication: API key, shop_id and one instance per marketplace

Seller authentication is deliberately plain. You generate an API key in the marketplace's seller back office and send it, as is, in the Authorization header. There is no Bearer prefix and no token exchange, and every request must use HTTPS.

curl -i 'https://your-instance.mirakl.net/api/orders?max=100' \
  -H 'Authorization: YOUR_API_KEY' \
  -H 'Accept: application/json'

Three details cause most of the early failures:

  • One instance per marketplace. The host is the operator's Mirakl instance, so three marketplaces means three base URLs and three API keys. The endpoints are identical; the credentials and the catalog rules are not.
  • shop_id matters for multi-shop users. When a user is attached to several shops, the API uses the default shop unless you pass shop_id. Agencies should always pass it explicitly, otherwise a stock file can land in the wrong shop.
  • Roles are per endpoint. Each operation in the reference lists the roles that may call it. Updating offers with OF24, for example, lists ROLE_SHOP_ADMIN and ROLE_PARTNER_OFFER_WRITE. When a key works on one endpoint and returns 403 on another, check the role before you check your code.

Mirakl publishes an official PHP SDK for the seller role. From Python, Node or Go the API is plain HTTPS and JSON, which is what the examples in this guide use.

Products vs offers: the split that shapes the whole integration

Mirakl separates the product (the catalog record: title, category, attributes, images) from the offer (your price, quantity, condition and lead time on that product). They travel through different endpoints, at different speeds, with different validation.

StepAPIEndpointWhat you get back
Read the category treeH11GET /api/hierarchiesThe operator's categories
Read required attributesPM11Product attribute configurationWhich fields each category needs
Read allowed valuesVL11GET /api/values_listsThe operator's value lists (colors, sizes, brands)
Send productsP41POST /api/products/importsAn import ID (CSV, XML or XLSX file)
Track the importP42GET /api/products/imports/{import}Import status
Read what failedP44, P47.../error_report and the transformation error reportRejected products and source-file errors
Read what was addedP45.../new_product_reportProducts the operator integrated

The reference explains each call well. What it does not spell out is where the effort goes: H11, PM11 and VL11 return a different answer on every marketplace, because each operator defines its own categories, mandatory attributes and value lists. Your code is reusable across Mirakl marketplaces; your catalog mapping is not.

That mapping is the same job we do for Amazon SP-API, eBay and Walmart in our marketplace API integration service, and it is usually the largest line in a Mirakl project too.

Two practical rules follow from the split:

  • Offers depend on products. An offer points at a product through product_id and product_id_type (an EAN in Mirakl's own example). If the product is not in the operator's catalog yet, the offer has nothing to attach to, so sequence product imports first and treat offer creation as a later step.
  • Cache the catalog configuration. H11, PM11 and VL11 are each capped at one call per hour. Pull them on a schedule and store them; do not call them inside a per-product loop.

Syncing offers, stock and prices: OF01, OF24, STO01 and PRI01

There are four ways to write offer data, and choosing the wrong one is the most common source of silent data loss.

APIEndpointUse it forThe trap
OF24POST /api/offers (JSON)Creating, updating or deleting offers from codeYou must send all offer fields. Fields you leave out are reset to their default value.
OF01POST /api/offers/imports (CSV, XML, XLSX)Bulk offer files, optionally with product data (with_products)import_mode is required. The current values are NORMAL and REPLACE; REPLACE replaces the shop's whole offer set.
STO01POST /api/offers/stock/imports (CSV)Quantity-only updates, global or per warehouseA row without warehouse-code is treated as the global quantity for that SKU.
PRI01POST /api/offers/pricing/imports (CSV)Price-only updates, including channel and volume pricingThe mode is delete and replace: any existing price you do not resend is deleted. Up to 50 prices per offer.

The practitioner rule: use STO01 for stock and PRI01 for prices, and keep OF24 for genuine offer changes. Teams that push a stock-only payload through OF24 wipe descriptions, lead times and discounts, because every unsent field falls back to its default.

All four are asynchronous. The call returns an import ID immediately, and the real result arrives later through a status endpoint and an error report. A 201 response means "file received", not "stock updated".

import csv, io, time
import requests

BASE = 'https://your-instance.mirakl.net/api'   # one base URL per marketplace
session = requests.Session()
session.headers.update({'Authorization': 'YOUR_API_KEY', 'Accept': 'application/json'})

def call(method, path, **kwargs):
    for _ in range(5):
        r = session.request(method, BASE + path, timeout=30, **kwargs)
        if r.status_code == 429:
            # Mirakl tells you how many seconds to wait
            time.sleep(int(r.headers.get('Retry-After', '60')))
            continue
        r.raise_for_status()
        return r
    raise RuntimeError('Still rate limited after 5 attempts')

def push_stock(rows):
    # rows: [('SKU-001', 12), ('SKU-002', 0)]  -> one batch, not one call per SKU
    buf = io.StringIO()
    writer = csv.writer(buf, delimiter=';', quoting=csv.QUOTE_ALL)
    writer.writerow(['offer-sku', 'quantity', 'warehouse-code', 'update-delete'])
    for sku, qty in rows:
        writer.writerow([sku, qty, '', ''])
    files = {'file': ('stock.csv', buf.getvalue(), 'text/csv')}
    return call('POST', '/offers/stock/imports', files=files).json()['import_id']

def stock_import_result(import_id):
    # STO02: poll this until the import finishes, then read the error report (STO03)
    status = call('GET', f'/offers/stock/imports/{import_id}/status').json()
    if status.get('has_error_report'):
        report = call('GET', f'/offers/stock/imports/{import_id}/error_report',
                      headers={'Accept': '*/*'})
        return status, report.text
    return status, None

Store every import ID with the batch that produced it. When a seller asks why a SKU oversold, the error report for that import is the only place the answer lives.

The Mirakl order API workflow: OR11, OR21, OR23, OR24

Orders on Mirakl are not "paid, then ship". The seller accepts first, the customer is debited second, and only then can the order be shipped. Your integration has to follow the order state, not assume it.

Order stateMeaningYour call
WAITING_ACCEPTANCEThe order is waiting for the shop's answerOR21 PUT /api/orders/{order_id}/accept, accepting or refusing each line
WAITING_DEBITThe shop accepted the orderNone, wait
WAITING_DEBIT_PAYMENTThe operator's system was asked to debit the customerNone, wait
SHIPPINGPaid, waiting for the shop to shipOR23 PUT .../tracking, then OR24 PUT .../ship
SHIPPEDSent by the shopNone
RECEIVED, CLOSEDDelivered, then finalizedNone
REFUSED, CANCELEDStopped before fulfillmentRelease the reserved stock

Poll OR11 (GET /api/orders) with start_update_date so you only fetch what changed. The reference notes that Mirakl subtracts a time delta from that date so no order is missed. The consequence is that you will receive the same order more than once, so make processing idempotent on order ID plus state.

def sync_orders(since_iso):
    # since_iso example: '2026-10-10T01:00:00Z' (dates are UTC, ISO 8601)
    offset = 0
    while True:
        page = call('GET', '/orders', params={
            'start_update_date': since_iso, 'max': 100, 'offset': offset,
        }).json()
        for order in page['orders']:
            handle(order)
        offset += 100
        if offset >= page['total_count']:
            break

def handle(order):
    order_id, state = order['order_id'], order['order_state']
    if state == 'WAITING_ACCEPTANCE':
        lines = [{'id': line['order_line_id'], 'accepted': in_stock(line)}
                 for line in order['order_lines']]
        call('PUT', f'/orders/{order_id}/accept', json={'order_lines': lines})
    elif state == 'SHIPPING':
        parcel = tracking_for(order_id)          # from your warehouse or carrier
        if parcel:
            call('PUT', f'/orders/{order_id}/tracking', json={
                'carrier_code': parcel['carrier_code'],
                'tracking_number': parcel['tracking_number'],
            })
            call('PUT', f'/orders/{order_id}/ship')   # no body

Notes from the reference that are easy to miss:

  • OR24 needs Content-Length: 0. The ship call has no body, and Mirakl requires the header anyway. Most HTTP libraries add it for a bodiless PUT, but hand-built requests and some proxies do not, and the call fails.
  • Unregistered carriers are allowed. If the carrier is not in the operator's list, OR23 accepts a complete tracking URL instead of a carrier code.
  • Several parcels need a different group. Orders that ship in more than one parcel go through the Multiple Shipments APIs: ST01 (POST /api/shipments), ST23 for tracking and ST24 to ship.
  • Large backlogs should not use OR11. Mirakl points high-volume callers to the asynchronous export: OR13 to request it, OR14 to poll, OR15 to download.

Mirakl API rate limits and call frequency

Mirakl does not publish one global quota. Every operation has its own "Call Frequency" block with a recommended and a maximum usage, and exceeding it returns 429 Too Many Requests with a Retry-After header giving the number of seconds to wait.

APIPurposeRecommendedMaximum
OF24Create, update, delete offersEvery 5 minutesOnce per minute
OF01Offer file importEvery 5 minutes (offers only); every hour with productsOnce per minute (offers only); once every 15 minutes with products
STO01Stock file importEvery 5 minutes when stock needs updatingOnce per minute
STO02Stock import statusAfter each STO01 call, every 5 minutesOnce every 15 seconds
PRI01Price file importEvery 5 minutesOnce per minute
P41Product importEvery hour, per sellerEvery 15 minutes, per seller
P42Product import statusOnce per minute until the final statusOnce per minute
H11, PM11, VL11Categories, attributes, value listsEvery hourEvery hour
OR11List orders (background sync)Every 5 minutesOnce per minute
OR11 as "External Partner"List orders (background sync)Every 15 minutesEvery 15 minutes
OR13Asynchronous order exportDifferential every 15 minutesDifferential every 5 minutes; full every 24 hours
OF52Asynchronous offer exportDifferential every 5 minutes; full once per dayDifferential once per minute; full once per day

These numbers decide the architecture more than any framework choice does:

  • Stock sync is batch, not event-per-SKU. With STO01 capped at once per minute, you collect every stock change into a queue and flush one file per interval. A webhook from your store that fires one Mirakl call per sale will be throttled on a busy day.
  • Your oversell window is your flush interval plus Mirakl's import processing time. At the recommended five minutes, keep a safety buffer on fast-moving SKUs instead of publishing your last unit.
  • Limits apply per seller. A connector serving many shops needs one queue per shop, not one global queue.
  • Order sync is polling. The seller reference lists no webhook endpoints as of October 2026, so new orders reach you on your OR11 cycle. Agencies calling as an external partner get the slower 15-minute limit, which is worth knowing before you promise a client "instant" order import.

Pagination, dates and compatibility rules that break integrations later

These rarely show up in a demo and regularly show up in production:

  • Two pagination styles. Offset pagination uses max (default 10, maximum 100) and offset, with next and previous URLs in the Link header; OR11 works this way. Seek pagination uses limit and a next_page_token you pass back as page_token; shipments (ST11) work this way. With a page_token, every other parameter is ignored.
  • The default page is 10 items. An integration that forgets max=100 and never paginates looks fine in testing and drops orders the first day volume passes ten.
  • Dates are UTC. Responses use ISO 8601 with a trailing Z. In GET parameters the date must be URL-encoded, which matters when you send an offset such as +02:00.
  • New fields and new enum values can appear at any time. Mirakl ships continuously and guarantees backward compatibility only if your code tolerates added fields, reordered fields and unknown enumeration values. Deserialize order states as strings, not as a closed enum that throws on a new value.
  • Prefer JSON. Mirakl advises JSON because its newest APIs are JSON-only.
  • Skip deprecated endpoints. OR12, OR26, OR41, OF26 and OF51 are marked deprecated in the reference. Older tutorials and plugins still use them; a new build should start on OR11, OR28 and the asynchronous exports.
  • Read status codes, not reason phrases. Mirakl says the text after the code is optional and may be missing.

Sources for everything above: Mirakl's seller REST API reference and the official Mirakl PHP SDK for sellers, whose changelog (v1.27.0, ) is a quick way to see which operations were added recently.

Custom Mirakl integration or a ready-made connector?

Both are valid. The decision depends on where your complexity lives, not on which is "better".

Your situationBetter fitWhy
One or two Mirakl marketplaces, a standard store platform, a small catalogConnector or feed toolThe mapping is light and a subscription costs less than maintaining code
Custom ERP or in-house order systemCustom integrationConnectors expect a known platform on your side; an ERP needs its own adapter either way
Multi-warehouse stock, channel pricing, volume pricing, pricing rulesCustom integrationSTO01 per warehouse and PRI01 channel prices need logic that generic tools flatten
Many Mirakl marketplaces with strict category attributesCustom, or a connector plus custom mappingThe API code is shared, but the per-operator mapping has to live somewhere you control
You are an agency or SaaS acting for many sellersCustom integrationPer-seller queues, shop_id handling and partner rate limits are your core product

Whichever route you choose, scope it in this order: catalog mapping first, then stock and price sync, then the order workflow, then returns and messaging. The order endpoints are the smallest part of the work; teams that estimate from the order flow alone underestimate the project.

If you want that scope written down against your own ERP or store, send your marketplace list and SKU count through our free quote form. It costs nothing, needs no account, and the build itself is priced to scope.

FAQ

Questions fréquentes

What is a Mirakl API integration?

It is a software connection between your own systems (ERP, online store, warehouse or PIM) and a marketplace that runs on Mirakl. It uses Mirakl's seller REST APIs to import products, keep offers, stock and prices up to date, retrieve orders, and send back order acceptance and tracking.

How does Mirakl API authentication work for sellers?

Sellers generate an API key in the marketplace's seller back office and send it directly in the Authorization header of every HTTPS request, with no Bearer prefix. If the user belongs to several shops, add the shop_id parameter to choose the shop. Mirakl Connect's Channel Platform APIs are different: they use an OAuth 2.0 bearer token.

What are the Mirakl API rate limits?

There is no single global quota. Each operation documents its own call frequency. For example, OF24 (update offers), STO01 (stock import) and PRI01 (price import) are recommended every 5 minutes with a maximum of once per minute, P41 (product import) is recommended hourly with a maximum of every 15 minutes per seller, and OR11 (list orders) is recommended every 5 minutes with a maximum of once per minute. Exceeding a limit returns HTTP 429 with a Retry-After header.

What is the difference between OF01 and OF24 in the Mirakl API?

OF01 imports an offer file (CSV, XML or XLSX) and requires an import_mode, while OF24 creates, updates or deletes offers from a JSON body. Both are asynchronous and return an import ID. With OF24 you must send every offer field, because fields that are not sent are reset to their default value. For stock-only or price-only changes, STO01 and PRI01 are the safer endpoints.

Does the Mirakl seller API send webhooks for new orders?

The seller REST API reference lists no webhook endpoints as of October 2026. Order sync is done by polling OR11 with the start_update_date filter, or by using the asynchronous order export (OR13, OR14, OR15) for large volumes.

Can one integration work with every Mirakl marketplace?

The code can, because every Mirakl marketplace exposes the same seller API. The configuration cannot: each marketplace has its own instance URL, its own API key, and its own categories, required attributes and value lists. Plan for shared API code and a separate catalog mapping per marketplace.

Restez informé avec Netalith

Recevez des ressources de développement, des mises à jour produit et des offres spéciales directement dans votre boîte mail.