Best Buy is North America's largest consumer electronics retailer, with roughly 1,000 stores in the United States and around 160 in Canada. For a connector there are two entirely separate things called "the Best Buy API" and conflating them wastes a sprint. The public developer portal at developer.bestbuy.com serves a read-only product catalogue for affiliates and price comparison sites: it will never show you an order. Selling on Best Buy means Best Buy Marketplace, which runs on Mirakl, and the interface is the standard Mirakl seller API mounted on Best Buy's own marketplace host. Best Buy Canada has operated its Mirakl marketplace for years and Best Buy launched its US marketplace on 19 August 2025.
At a glance
What it is
Best Buy Co., Inc. sells consumer electronics, appliances, computing and entertainment through stores, bestbuy.com and bestbuy.ca. Best Buy Canada has run a third-party marketplace on Mirakl for years, at marketplace.bestbuy.ca. Best Buy's US marketplace is newer: the company's own announcement describes it as "Best Buy Marketplace, powered by Mirakl" and calls it "the largest expansion ever of Best Buy's product assortment", launched on 19 August 2025 into categories Best Buy had not carried before, including seasonal decor, automotive tech, office and home goods, movies and music, licensed sports merchandise, small appliances, furniture, musical instruments and toys. Named launch sellers include Beach Camera, World Wide Stereo, Antonline and Fanatics.
The commercially important detail is that marketplace purchases can be returned at a Best Buy store. That makes the returns object real operational traffic rather than an edge case, and it means your returns handling has to cope with a return you did not originate.
The Best Buy Developer API is a product catalogue API, not a seller API. It exposes Best Buy's own assortment: SKUs, prices, specifications, store locations, categories, open box availability and recommendations. It has no orders endpoint for a seller, no offers endpoint, no inventory write and no concept of your shop. If your goal is to sell on Best Buy, close that tab and open the Mirakl documentation.
API access
The catalogue API
Self-serve. Register at developer.bestbuy.com, receive a key, and call https://api.bestbuy.com/v1/. The documentation is published on GitHub Pages at bestbuyapis.github.io/api-documentation and covers six API families:
Two terms of use constraints have direct engineering consequences. First, you agree "not to cache any content except on a temporary basis", and Best Buy enforces this partly by expiring response links after seven days. A commerce database that mirrors Best Buy catalogue data indefinitely is outside the terms. Second, "Music and movie data may be used only where an ability to purchase the related music or movies from BESTBUY.COM is provided to end users".
The seller API
There is no self-serve route. You apply to Best Buy Marketplace, Best Buy approves the shop, and the shop is provisioned on Best Buy's Mirakl instance with an API key visible in the seller panel. Best Buy's Canadian seller pages return HTTP 403 to non-browser clients, so the commercial terms, commission rates and category eligibility could not be read directly for this page and should be taken from the seller agreement.
The instance itself is verifiable without credentials, and was on 2026-09-21:
Confirms the marketplace host is a Mirakl instance
curl -i https://marketplace.bestbuy.ca/
HTTP/1.1 302 Found
Location: https://redirect.mirakl.net/?error=login_required...
Health check, unauthenticated, returns the Mirakl platform version
curl https://marketplace.bestbuy.ca/api/version
{"version":"3.1307"}
Any seller endpoint without a key
curl https://marketplace.bestbuy.ca/api/orders
{"message":"Unauthorized","status":401}
That /api/version response is operationally useful beyond proving the point: Mirakl instances differ by version, and endpoints introduced after an operator's version are simply absent. Read it before assuming an endpoint exists on Best Buy specifically.
Mirakl publishes the full seller OpenAPI 3 specification without login at https://developer.mirakl.com/specs/content/product/mmp/rest/seller/openapi3-download.json?download, along with a Postman collection at the sibling postman-mmp-seller.json path. The specification read for this page declares "title": "Mirakl Marketplace APIs", "version": "latest-release", and contains 95 paths. Everything below is taken from it. Note Mirakl's own caveat: the OpenAPI file excludes multipart APIs that take both a file and an object, and APIs with dynamically constructed URLs, so the published spec is a subset of the real surface.
Authentication
Catalogue API
A single query parameter. There is no token, no expiry and no refresh.
curl "https://api.bestbuy.com/v1/products/8880044.json?show=sku,name,salePrice&apiKey=YourAPIKey"
{
"sku": 8880044,
"name": "Batman Begins (Blu-ray Disc)",
"salePrice": 7.99
}
Documented status codes: 200 "It is all good"; 400 "The request is missing key information or is malformed"; 403 "The API key is not valid, or the allocated call limit has been exceeded"; 404 "The requested item cannot be found"; 405 for disallowed methods such as POST; 500, 501 and 503 for Best Buy side errors.
Mirakl seller API
The specification declares exactly one security scheme:
{
"Shop-API-Key": { "type": "apiKey", "in": "header", "name": "Authorization" }
}
That means the shop API key is the entire value of the Authorization header. There is no scheme prefix, no Bearer, no signature and no expiry. The sequence is:
- Sign in to the Best Buy Marketplace seller panel at
marketplace.bestbuy.ca(or the US equivalent for your shop). - Copy the shop API key from the user or shop settings area. It is bound to a user, and that user's roles determine what the key can do.
GET /api/users/shops/roles(RO02) lists the roles available on your shop. - Send it on every request.
curl -H "Authorization: YOUR_SHOP_API_KEY" \
"https://marketplace.bestbuy.ca/api/orders?max=100&order_state_codes=WAITING_ACCEPTANCE"
Multi-account works through a shop_id query parameter rather than through separate credentials. The specification's own wording on OR11 is that you "use this parameter when your user has access to several shops. If not specified, the shop_id from your default shop is used". For a connector holding many sellers, store one key per seller anyway: keys are per user and operator instances differ.
Because the key never expires, there is no refresh job to write, but there is also no rotation signal. Treat the key as a long-lived secret, store it encrypted, and assume revocation happens silently when a seller regenerates it in the panel. The failure mode you will see is a sudden 401 with {"message":"Unauthorized","status":401} on every call for one seller.
Mirakl also documents an OAuth 2 based "Mirakl Authentication System" for external partners, distributed as a PDF at /static/doc/mirakl-authentication-system/mas-oauth2-documentation.pdf on the developer portal. That is the connector-developer route for published Mirakl connectors, not the per-seller route. For a single seller integration the shop API key is the whole story.
Objects we can read
All paths below are relative to the marketplace host, for example https://marketplace.bestbuy.ca. The bracketed code is Mirakl's own operation id, which is what the seller panel, the support team and every Mirakl forum post will call the endpoint.
Orders
GET /api/orders (OR11). The central read.
Filters: order_ids, order_references_for_customer, order_references_for_seller, order_state_codes, channel_codes, only_null_channel, start_date, end_date, start_update_date, end_update_date, customer_debited, payment_workflow, has_incident, fulfillment_center_code, order_tax_mode, shop_id, locale.
Pagination is offset based: max for page size, offset for the index of the first item, plus sort and order. The response carries total_count.
For incremental sync use start_update_date, not start_date. The specification notes that "Mirakl will subtract a time delta to ensure no orders are missed due to network latency", so the endpoint is deliberately built for high water mark polling and will re-serve a small overlap. Make your loader idempotent on order_id.
The response shape, with arrays trimmed to one element and names exactly as the specification gives them:
{
"orders": [
{
"order_id": "BBY-1234-A",
"commercial_id": "BBY-1234",
"order_state": "SHIPPING",
"created_date": "2026-09-14T09:12:44Z",
"last_updated_date": "2026-09-15T11:02:10Z",
"acceptance_decision_date": "2026-09-14T10:01:00Z",
"currency_iso_code": "CAD",
"price": 249.98,
"shipping_price": 12.5,
"total_price": 262.48,
"total_commission": 24.99,
"order_tax_mode": "TAX_EXCLUDED",
"order_taxes": [ { "code": "GST", "rate": 5.0, "total_amount": 12.49 } ],
"payment_type": "CREDIT_CARD",
"payment_workflow": "PAY_ON_ACCEPTANCE",
"customer_debited_date": "2026-09-14T10:05:00Z",
"has_incident": false,
"fully_refunded": false,
"shipping_carrier_code": "CANADA_POST",
"shipping_carrier_standard_code": "…",
"shipping_tracking": "1234567890",
"shipping_tracking_url": "https://…",
"shipping_deadline": "2026-09-17T23:59:59Z",
"shipping_type_code": "STD",
"shipping_zone_code": "CA",
"channel": { "code": "WEB", "label": "Web" },
"customer": {
"customer_id": "c_8812",
"firstname": "…", "lastname": "…", "locale": "en_CA",
"shipping_address": { "…": "PII" },
"billing_address": { "…": "PII" }
},
"customer_notification_email": "…@marketplace.bestbuy.ca",
"order_lines": [
{
"order_line_id": "BBY-1234-A-1",
"order_line_state": "SHIPPING",
"offer_id": 55512345,
"offer_sku": "SELLER-SKU-001",
"product_shop_sku": "SELLER-SKU-001",
"product_title": "…",
"quantity": 2,
"price_unit": 124.99,
"price": 249.98,
"origin_unit_price": 139.99,
"commission_fee": 24.99,
"shipping_price": 12.5,
"taxes": [], "refunds": [], "cancelations": [],
"shipped_date": "2026-09-15T11:02:10Z",
"shipping_from": { "…": "warehouse or address" }
}
]
}
],
"total_count": 1
}
The specification types order_state as a plain string with the description "Order's state" and does not enumerate the values, so do not hard code a state machine from a blog post. Read the states your shop actually produces, and use GET /api/reasons (RE01) for reason codes.
A second, cheaper route exists for backfills: POST /api/orders/async-export (OR13) starts an asynchronous export and GET /api/orders/async-export/status/{tracking_id} (OR14) polls it. Use this for the initial load and OR11 for the incremental one.
Order items
Not a separate resource. Order items are the order_lines array inside OR11, each with its own order_line_id, order_line_state, money breakdown, taxes, refunds and cancellations. Both per-line commission (commission_fee) and per-line shipping (shipping_price) are present, which is unusually complete and means you can reconcile margin at line level without a settlement file.
Products and listings
Mirakl separates the catalogue product from your offer on it, and the distinction matters for the unified model.
GET /api/offers (OF21) lists your offers. Filters: offer_state_codes, sku, product_id, favorite, pricing_channel_code, pricing_customer_organization_id, shop_id, locale, with the same max/offset/sort/order pagination and a total_count.
Offer fields, from the specification: offer_id, shop_sku, product_sku, product_title, product_brand, category_code, price, total_price, msrp, all_prices, retail_prices, applicable_pricing, currency_iso_code, quantity, min_quantity_alert, state_code, active, inactivity_reasons, available_start_date, available_end_date, leadtime_to_ship, shipping_deadline, logistic_class, min_shipping_price, min_shipping_type, min_shipping_zone, min_order_quantity, max_order_quantity, package_quantity, description, internal_description, product_tax_code, eco_contributions, channels, warehouses, fulfillment, offer_additional_fields.
GET /api/offers/{offer} (OF22) fetches one. GET /api/offers/export (OF51) and the asynchronous pair OF52 and OF53 produce a CSV or JSON export, which is the right tool for a full reconciliation.
GET /api/products (P31) resolves catalogue products for a list of references, GET /api/products/offers (P11) lists all offers on given products including competitors', and GET /api/products/attributes (PM11) returns the attribute configuration you must satisfy to create a product. GET /api/hierarchies (H11) walks the category tree.
Inventory
Quantity is a field on the offer, not a separate object. Read it from OF21 as quantity and min_quantity_alert. Where the operator enables multi-warehouse, the offer carries a warehouses array and stock is per warehouse code.
Shipments and tracking
GET /api/shipments (ST11). Unlike orders, shipments paginate by token: the response is { "data": [...], "next_page_token": "...", "previous_page_token": "..." }. Shipment fields: id, order_id, status, status_customer_debit, created_date, last_updated_date, shipped_date, invoice_reference, tracking, shipping_from, shipment_lines, shipment_additional_information, payment_details.
GET /api/shipments/items_to_ship (ST12) is the work queue.
Returns and cancellations
GET /api/returns (RT11), also token paginated. Return fields: id, rma, state, order_id, order_commercial_id, reason_code, rejection_reason_code, method_code, description, date_created, last_updated, return_address, return_lines, tracking, documents, label_url. GET /api/returns/items_to_return (RT12) is the corresponding queue.
Cancellations appear both as a cancelations array on the order line in OR11 and as write endpoints OR29, OR30 and OR33.
Payments and settlements
GET /api/sellerpayment/transactions_logs (TL02) returns transaction lines, token paginated, with POST /api/sellerpayment/transactions_logs/async (TL03) and its status poll TL04 for bulk export. GET /api/seller-billing-cycles (SBC11) lists billing cycles, which give the period boundaries. GET /api/invoices (IV01) lists accounting documents and GET /api/invoices/{accounting_document_id} (IV02) downloads one.
Customers
Embedded in the order as the customer object: customer_id, civility, firstname, lastname, locale, shipping_address, billing_address, delivery_contact, accounting_contact, organization. All PII. The customer_notification_email is typically an operator-relayed alias rather than the shopper's real address, so do not treat it as a marketing contact and check what Best Buy actually sends before storing it as one.
Locations
GET /api/shipping/zones (SH11), GET /api/shipping/types (SH12), GET /api/shipping/carriers (SH21), GET /api/shipping/logistic_classes (SH31), GET /api/shipping/configuration/shop (SH42) and GET /api/shipping/charges/shop (SH52). Warehouses appear on offers and on shipping_from.
Writing back: listings, price and stock
Fully supported, by two different mechanisms, and the choice matters at volume.
Single call JSON
POST /api/offers (OF24), "Create, update, or delete offers". The request body is {"offers": [...]} where each entry carries shop_sku, product_id, product_id_type, price, quantity, state_code, logistic_class, leadtime_to_ship, min_order_quantity, max_order_quantity, package_quantity, min_quantity_alert, msrp, all_prices, retail_prices, discount, description, product_tax_code, eco_contributions, available_started, available_ended, and crucially update_delete, which selects create, update or delete per row. This is a single request that carries many offers, and it is the right default for a connector reacting to price or stock changes.
CSV imports
Three narrower, higher-throughput lanes, each multipart/form-data with a field named file:
The stock file format is given verbatim in the specification as "offer-sku";"quantity";"warehouse-code";"update-delete", one line per offer quantity, with quantities for the same offer grouped together, and with a missing warehouse-code meaning a global quantity. Use the price and stock imports rather than the full offer import for routine updates: they touch one field each and their error reports are correspondingly easy to act on.
Product creation is asynchronous and report driven: submit a product import, then poll GET /api/products/imports/{import} (P42) and collect the error report (P44), the new product report (P45), the transformed file (P46) and the transformation error report (P47). Nothing is confirmed synchronously.
Promotions are first class: GET /api/promotions (PR01), POST /api/promotions (PR03), PUT /api/promotions/{promotion_internal_id} (PR04).
Order side writes: PUT /api/orders/{order_id}/accept (OR21) to accept or refuse lines, PUT /api/orders/{order_id}/tracking (OR23) then PUT /api/orders/{order_id}/ship (OR24), or the shipment-centric POST /api/shipments (ST01), POST /api/shipments/tracking (ST23) and PUT /api/shipments/ship (ST24). Refunds via PUT /api/orders/refund (OR28), cancellations via OR29, OR30 and OR33.
Accepting orders is time bound. OR11 returns a shipping_deadline on the order and an acceptance_decision_date once decided, and Mirakl operators typically auto-refuse lines that are not accepted within a window. Whatever your poll interval, the acceptance path must be the fastest thing in the connector.
Webhooks and notifications
Mirakl's MMP developer documentation lists webhook and Cloud Events documentation alongside the OpenAPI files, but the seller specification read for this page contains no subscription, registration or secret rotation endpoint. Best Buy publishes nothing about seller webhooks. Plan for polling and treat any webhook capability as a bonus to confirm with Best Buy's marketplace support.
A workable cadence, given no published rate limit:
The start_update_date delta subtraction that Mirakl applies means consecutive polls overlap on purpose. Do not try to eliminate the overlap by advancing the high water mark past the last seen timestamp.
Rate limits and pagination
Catalogue API. No numeric limit appears in the published documentation. The 403 status is documented as "The API key is not valid, or the allocated call limit has been exceeded", so a quota exists and is enforced, but its value is not stated in the reference. Assume it is modest, back off on 403, and remember the terms of service restriction on caching. Pagination: page size defaults to 10 and maxes at 100, with response metadata total, totalPages, currentPage, from and to. Past roughly ten pages the documentation directs you to cursorMark instead of page numbers, which is the efficient path for a full sweep.
Mirakl seller API. No rate limit headers, quota fields or numeric limits appear anywhere in the OpenAPI specification, and limits on Mirakl are configured per operator instance rather than published. Two pagination models coexist and you must implement both:
- Offset based, on the older endpoints including orders (OR11) and offers (OF21):
max,offset,sort,order, withtotal_countin the body. - Token based, on the newer ones including shipments (ST11), returns (RT11) and transaction lines (TL02):
next_page_tokenandprevious_page_tokenin the body, with no total.
Deep offset paging degrades on any large dataset. For anything above a few thousand rows, prefer the asynchronous exports: OR13 and OR14 for orders, OF52 and OF53 for offers, TL03 and TL04 for transactions.
Mapping to the unified model
Gaps and open questions
- Order state values. The specification types
order_stateas an unenumerated string. The set Best Buy actually emits, and its transitions, must be observed on a live shop. - Best Buy's own seller terms.
bestbuy.careturns HTTP 403 to non-browser clients, so commission rates, category eligibility, onboarding steps, performance targets and the acceptance window could not be read from Best Buy directly. All of those are in the seller agreement. - Whether the US marketplace exposes the same Mirakl surface. Best Buy's announcement confirms Mirakl powers it, but the US seller host was not identified during this research, and the two instances may be on different Mirakl versions. Run
/api/versionagainst each before assuming parity. - Webhooks. Mirakl documents them at platform level; nothing says whether Best Buy enables them for sellers. Worth one support ticket, because it would remove most of the polling above.
- Rate limits. Published nowhere for either API. Instrument from the first day rather than guessing.
- Multi-warehouse. Whether Best Buy enables warehouse level stock, which decides whether
inventory.location_idis meaningful or always null. - The Commerce API. Restricted and obtained by direct contact, so its scope is unverified. It is a Best Buy first-party ordering API and is almost certainly irrelevant to a marketplace seller, but that has not been confirmed.
Sources
- Best Buy developer portal: developer.bestbuy.com
- Best Buy API documentation, the source for the base URL, the
apiKeyparameter, status codes, pagination,cursorMarkand the caching terms: bestbuyapis.github.io/api-documentation - Best Buy corporate announcement of Best Buy Marketplace, "powered by Mirakl", launched 19 August 2025: corporate.bestbuy.com/2025/marketplace
- Mirakl seller OpenAPI 3 specification, downloaded in full on 2026-09-21, 95 paths, the source for every endpoint, operation id, parameter, field name and the
Shop-API-Keysecurity scheme: developer.mirakl.com seller openapi3 - Mirakl seller Postman collection, the same surface in collection form: developer.mirakl.com postman-mmp-seller
- Mirakl Marketplace Platform developer documentation, for the Front, Operator and Seller API split, the OpenAPI exclusions and the webhook and Cloud Events mention: developer.mirakl.com/content/product/mmp
- Mirakl Authentication System OAuth 2 documentation PDF: developer.mirakl.com mas-oauth2-documentation.pdf
- Live probes against
marketplace.bestbuy.caon 2026-09-21: the root returns HTTP 302 toredirect.mirakl.net,/api/versionreturns{"version":"3.1307"}unauthenticated, and/api/ordersand/api/docreturn{"message":"Unauthorized","status":401} - Best Buy Canada seller pages at
bestbuy.ca/en-ca/about/marketplaceand/marketplace-seller, which returned HTTP 403 to non-browser clients on 2026-09-21 and could not be read