Use shelf data from your own code or agent
Shelf data is the Amazon page as a shopper at a given location sees it: the search results page for a keyword, the Buy Box for an ASIN, the whole offer list, the product page, a seller's storefront. The MixShift plugin's Amazon Shelf skill is one way in; this article is the other two: the REST endpoints for your own code and automations, and the shelf_* tools for AI clients over MCP. Beta, on for every organization. Full reference: mcp.mixshift.io/developers#shelf.
Where the data comes from, and what it is not
Public shelf observations from a licensed third-party shelf-data provider, through MixShift's service on MixShift's own key. No call uses your Amazon authorization, and nothing of yours is sent: an ASIN, a keyword or a seller id is the only input. It is not your account's data (that is the retail surface), not sales estimates ("bought in past month" is the label Amazon puts on its own page), and nothing here writes anywhere.
Access
- Scope:
shelf:read. Interactive sign-ins hold it, and so does a sign-in from a ChatGPT custom app or a Claude, Cursor or Codex connector. A service credential minted at/admingets it by default; a tenant admin can deny it per person. - Organization: your account must belong to a MixShift organization (every customer does). If a call answers
shelf_not_enrolled, ask MixShift. - Allowance: every data answer counts one against your organization's monthly shelf allowance (a larger discovery allowance in the first month, a monthly one after; no rollover).
GET /api/shelf/usageand the zip list are free. See "When you run out" below.
The calls
| REST | MCP tool | Body | What comes back in data |
|---|---|---|---|
POST /api/shelf/search |
shelf_search |
{ "query", "surface": "desktop" or "mobile", "page": 1 to 5, "zip", "country", "fresh" } |
every position with rank, absolute_rank, is_sponsored, price, rating, reviews, monthly_sales, badges, coupon, stock text, delivery promise; banners (sponsored brands with brand and headline); related_keywords, total_results, has_more. Desktop is 60 positions a page, mobile 15. One page is one answer. |
POST /api/shelf/buybox |
shelf_get_buybox |
{ "asin", "zip", "country", "fresh" } |
price, seller and seller_id, ships-from, Prime, stock, free_delivery and fastest_delivery as text and as promise_days_* |
POST /api/shelf/offers |
shelf_get_offers |
{ "asin", "zip", "country", "fresh" } |
the featured offer plus offers[]: price and price_num, fba, condition, seller_id, promise days |
POST /api/shelf/product |
shelf_get_product |
{ "asin", "zip", "country", "fresh" } |
title, bullets, details, media, best-seller ranks, rating, badge, the Buy Box block, "customers say"; the answer carries beta: true (a field can be missing) |
POST /api/shelf/seller |
shelf_get_seller |
{ "seller_id", "country", "fresh" } |
storefront name, rating, description, business details |
GET /api/shelf/zips?country=us |
shelf_list_zips |
the shopper locations per country (us default; ca uk de fr it es in). Free. |
|
GET /api/shelf/usage |
shelf_get_usage |
period, allowance, used, remaining, resets_at. Free. |
Only query, asin or seller_id is required. zip is a location from the zip list (omit for the default location; a zip off the list is refused for free). country defaults to us. fresh: true asks for the shelf as of now instead of a recent observation, at the same cost.
A keyword rank call from your own code:
POST https://mcp.mixshift.io/api/shelf/search
Authorization: Bearer <token>
Content-Type: application/json
{ "query": "backpacking meals", "surface": "mobile", "zip": "10001" }
Every data answer has one shape:
{ "ok": true, "kind": "search_mobile", "retailer": "amazon", "country": "us", "zip": "10001",
"subject": "backpacking meals", "observed_at": "2026-10-20T14:41:13.187Z", "source": "shelf",
"data": { "surface": "mobile", "page": 1, "page_size": 15, "products": [ ... ], ... } }
observed_at is when the shelf looked like this. MixShift may answer from a recent observation (hours for rank, the Buy Box and offers; a day for the product page; a week for a seller); nothing in the answer says how it was produced. Rank on page n is absolute_rank = (n - 1) * page_size + rank.
From ChatGPT, Claude, Cursor or Codex
Connect the MixShift MCP server as described in Connect AI tools over MCP; the sign-in grants shelf:read with the rest. The seven tools then appear next to the warehouse and Amazon tools, and a question such as "where do we rank for backpacking meals in New York on mobile?" maps to shelf_search. The tool descriptions say what each call costs; the allowance is per organization, so a connector and the plugin draw on the same monthly number.
Tracking rank over time
Every observation MixShift takes is kept and dated, so calling the same keyword at the same zip on a schedule (your own cron, n8n, or a MixShift scheduled task) builds a dated series. A MixShift-run tracked keyword list, with the history on a page and feeding the daily health check and the monthly report, is the next thing being built on this data; to be first on it, send MixShift your keywords, locations and cadence (from the plugin, "track these keywords for me"; or email MixShift). Daily observations count against the allowance like any other answer until history is priced.
When you run out
The call answers 402 shelf_allowance_exhausted with period, allowance, used, resets_at and request_more, which says how to ask for more: from the plugin, "request more shelf requests" sends the request with your organization and period; or reply to any MixShift message. MixShift raises the allowance from your organization's settings, with effect this month.
Errors
The usual envelope, { "ok": false, "kind", "friendly", "source", "retryable", ... }; branch on kind:
| kind | status | meaning |
|---|---|---|
shelf_not_enabled |
403 | shelf data is switched off for your organization; ask MixShift |
shelf_not_enrolled |
403 | your account has no organization yet; ask MixShift |
shelf_allowance_exhausted |
402 | the month's allowance is used; see above |
zip_unavailable |
400 | not a location on the list (available_count says how many are) |
bad_request |
400 | refused as sent, for example no such ASIN or seller; do not resend unchanged |
invalid_request |
400 | the body did not pass; nothing was sent |
throttled |
429 | retry after retryAfterMs |
upstream_unavailable |
502 | shelf data is temporarily unavailable; retry in a minute |
For the general failure contract (who acts, whether to retry), see When a call fails.
Shifty is the MixShift docs assistant. Pick a question or open the chat for anything else.