E-commerce & Marketplaces

How do you track StockX sneaker prices via API?

Call ReefAPI's stockx product_detail action with a url_key and read lowest ask, highest bid, last sale, sales counts, volatility and per-size variant pricing back as JSON. The trap is not fetching the numbers - it is that the headline numbers are cross-size aggregates and the market parameter changes some of them and not others.

StockX engineLive JSON5 steps1,000 free credits

This guide demonstrates the real StockX API engine with a captured response from . The example is only published because the engine passed the SEO snapshot gate.

Use case

Resale price tracking, sneaker arbitrage, portfolio valuation and market analytics.

Step by step

Call the live endpoint

  1. 1

    Find the url_key with search

    stockx/v1/search takes a query and returns url_key, sku, brand and the same three headline prices per result. url_key is the slug in the product URL and is what every other action accepts.

  2. 2

    Call stockx/v1/product_detail

    Pass url_key plus currency and market. Read variants[] rather than the top-level market block whenever the answer has to be per-size.

  3. 3

    Pick market and currency deliberately

    market selects which order book you see; currency selects the units. Sales counts and bid counts came back identical across markets in our test - only prices and ask counts moved.

  4. 4

    Scope price_history to a size

    price_history with a size sampled fewer sales but covered the full window. Without a size on a fast seller, 400 sampled sales can all fall inside one month and window_complete comes back false.

  5. 5

    Recompute price_premium if you compare markets

    retail_price is not converted, so the built-in premium is only apples-to-apples inside the currency the product was priced in.

Code

Copy the request

These snippets use the captured request params for stockx/v1/search.

curl -X POST https://api.reefapi.com/stockx/v1/search \
  -H "x-api-key: $REEF_KEY" \
  -H "content-type: application/json" \
  -d '{"query":"jordan 1"}'
MCP one-liner
Ask your MCP-connected assistant: call reefapi.stockx.search with {"query":"jordan 1"}.
Real response

Captured output from ReefAPI

Captured on UTC. The response below is the committed snapshot, including the API envelope and metadata.

Captured request
{
  "method": "POST",
  "url": "https://api.reefapi.com/stockx/v1/search",
  "headers": {
    "x-api-key": "$REEF_KEY",
    "content-type": "application/json"
  },
  "body": {
    "query": "jordan 1"
  }
}
Captured response
{
  "ok": true,
  "meta": {
    "api": "stockx",
    "endpoint": "search",
    "mode": "live",
    "latency_ms": 1555.8,
    "record_count": 40,
    "bytes": 32434,
    "cache_hit": false,
    "currency": "USD",
    "market": "US",
    "page": 1,
    "total": 1000,
    "pagination": {
      "page": 1,
      "has_more": true
    },
    "charged_credits": 1,
    "version": "0.1.0"
  },
  "data": {
    "query": "jordan 1",
    "results": [
      {
        "id": "019cfdb4-3545-7b24-8773-11f7411145fb",
        "url_key": "air-jordan-1-retro-low-og-sp-travis-scott-sail-tropical-pink",
        "url": "https://stockx.com/air-jordan-1-retro-low-og-sp-travis-scott-sail-tropical-pink",
        "title": "Jordan 1 Retro Low OG SP Travis Scott Sail Tropical Pink",
        "brand": "Jordan",
        "sku": "IQ7604-101",
        "category": "sneakers",
        "gender": "men",
        "image": "https://images.stockx.com/images/Air-Jordan-1-Retro-Low-OG-SP-Travis-Scott-Sail-Tropical-Pink-Product.jpg?fit=fill&bg=FFFFFF&w=700&h=500&fm=webp&auto=compress&q=90&dpr=2&trim=color&updated_at=1779381546",
        "lowest_ask": 262,
        "highest_bid": 640,
        "last_sale": 262,
        "number_of_asks": 1388,
        "number_of_bids": 894,
        "in_stock": true
      },
      {
        "id": "c21909e2-c5ca-48b4-a1db-66d6f6c5b55c",
        "url_key": "air-jordan-1-retro-high-virgil-abloh-archive-alaska",
        "url": "https://stockx.com/air-jordan-1-retro-high-virgil-abloh-archive-alaska",
        "title": "Jordan 1 Retro High Virgil Abloh Archive Alaska",
        "brand": "Jordan",
        "sku": "AA3834-100",
        "category": "sneakers",
        "gender": "men",
        "image": "https://images.stockx.com/images/Air-Jordan-1-Retro-High-Off-White-Alaska-Product.jpg?fit=fill&bg=FFFFFF&w=700&h=500&fm=webp&auto=compress&q=90&dpr=2&trim=color&updated_at=1784258612",
        "lowest_ask": 236,
        "highest_bid": 624,
        "last_sale": 400,
        "number_of_asks": 1064,
        "number_of_bids": 589,
        "in_stock": true
      },
      {
        "id": "9a4d44f9-4b16-4abc-ba58-c0db340ee791",
        "url_key": "air-jordan-1-retro-high-og-chicago-reimagined-lost-and-found",
        "url": "https://stockx.com/air-jordan-1-retro-high-og-chicago-reimagined-lost-and-found",
        "title": "Jordan 1 Retro High OG Chicago Lost and Found",
        "brand": "Jordan",
        "sku": "DZ5485-612",
        "category": "sneakers",
        "gender": "men",
        "image": "https://images.stockx.com/images/Air-Jordan-1-Retro-High-OG-Chicago-Reimagined-Product.jpg?fit=fill&bg=FFFFFF&w=700&h=500&fm=webp&auto=compress&q=90&dpr=2&trim=color&updated_at=1738193358",
        "lowest_ask": 167,
        "highest_bid": 286,
        "last_sale": 267,
        "number_of_asks": 1015,
        "number_of_bids": 416,
        "in_stock": true
      }
    ],
    "page_info": {
      "page": 1,
      "limit": 40,
      "total": 1000
    }
  }
}
Manual way

Why this is hard manually

The obvious plan is to read the big numbers off the product page and compute a spread. On the Jordan 4 Retro Bred Reimagined we measured lowest_ask 204 and highest_bid 247 in the US market on the same request. A bid above an ask is impossible inside one order book - it would have matched. Those two figures are the best ask and the best bid taken across all 26 sizes, and they belong to different sizes. Any spread, liquidity or arbitrage number built on the product-level pair is measuring nothing.

The second trap is currency. StockX will happily price the same product in EUR, and it looks like a straight conversion until you check the depth. Switching from US/USD to DE/EUR moved lowest_ask from 204 to 167 and the ask count from 1,499 to 1,404 - a different book, not a converted one. But sales_last_72h stayed at 62 and sales_count_90d stayed at 1,976 in both, because the volume figures are global. Half the market block localises and half does not, and nothing in the response labels which half is which.

The third only shows up on fast-moving products. Ask for a 90-day price history on a shoe that sells 1,976 pairs a quarter and the sales feed runs out of room before it runs out of days.

ReefAPI way

Why ReefAPI solves it

Use variants[] for anything that has to be right. Each entry carries variant_id, size, lowest_ask, highest_bid and last_sale for that size alone, and inside a size the book behaves: size 3.5 came back with ask 220 against bid 145 in USD. The variant_id is stable across markets - the same UUID appeared in both the US and the German response - so it is a safe primary key for a price-history table.

market and currency are two separate levers and it is worth knowing which fields each one moves. In our side-by-side, market/currency changed lowest_ask (204 to 167), highest_bid (247 to 181), last_sale (213 to 183), number_of_asks (1,499 to 1,404), avg_price_90d (222 to 190) and avg_price_annual (237 to 204). They did not change number_of_bids (262), sales_last_72h (62), sales_count_90d (1,976) or volatility (0.1228 against 0.1224, the same series measured in different units).

One field to treat carefully: retail_price does not convert. It came back as 215 in the USD response and 215 in the EUR response. price_premium is computed from it, which is why the same shoe reads price_premium -0.0093 in the US and -0.1488 in Germany - the German figure is a EUR last sale divided by a USD retail. If you compare premium across markets, recompute it yourself against a converted retail. We would rather tell you that than let you find it in a dashboard.

price_history exposes the sampling limit instead of hiding it, in a field called window_complete. We asked for window '90d', bucket 'month' on the whole product: it sampled 400 sales, every one of them landed in the current month, and it returned one series point with window_complete false. The identical request scoped to size 10 sampled 310 sales, covered four months (2026-05 through 2026-08) and returned window_complete true. On a high-volume product, asking for one size gives you more history than asking for the whole product. Check window_complete before you draw a trend line.

null is used where a figure genuinely does not exist rather than being collapsed to zero. avg_price_72h came back null while sales_count_72h read 62, and in the German market express_next_day returned count 0 with lowest_ask null. A zero there would read as a free next-day ask.

Invalid enum values are rejected before any work happens and are not charged. Sending window '1y' returned INVALID_PARAM in 1.2 ms with detail.allowed listing ['30d','90d','180d','365d','all']. The same discipline applies to currency (19 values from USD to THB) and market. Failed calls cost nothing.

Everything else on the product is what you would expect and is populated: sku/style_id (FV5029-006), colorway, release_date, retail_price, brand, model, plus a market block with number_of_asks, number_of_bids, sales_last_72h, avg_price_90d, avg_price_annual, price_premium, volatility as a coefficient of variation, and ask_service_levels broken into standard, express_standard, express_expedited and express_next_day with their own counts, lowest asks and expected delivery dates. Product detail answered in about 750 ms in our runs.

FAQ

Questions developers ask

Why is highest_bid higher than lowest_ask on the product?

Because those two numbers come from different sizes. They are the best bid and best ask across all variants, not a matched book. We measured ask 204 against bid 247 on one Jordan 4. Inside a single variant the relationship is normal - size 3.5 was ask 220, bid 145.

Does changing market give me converted prices or a different market?

A different market. US to DE moved the ask count from 1,499 to 1,404 and the express next-day ask count from 125 to 0. Prices moved by more than an FX rate would explain.

Which fields stay the same across markets?

In our measurement: number_of_bids, sales_last_72h, sales_count_90d and sales_count_annual were identical in the US and German responses. Volume is global; pricing and ask depth are local.

Why does price_premium look so different in EUR?

Because retail_price is not converted - it stayed 215 in both responses. The EUR premium divides a EUR last sale by a USD retail. Compute your own premium against a converted retail if you compare markets.

I asked for 90 days of history and got one data point. Is that a bug?

No, it is the sampling limit surfacing. The feed sampled 400 sales and all 400 landed in the current month, so the series has one bucket and window_complete comes back false. Scope the request to a single size: the same product with size 10 covered four months with window_complete true.

What does volatility actually measure?

It is the coefficient of variation of recent sale prices - 0.1228 on our test product. It is unit-free, which is why the US and German responses agreed to within 0.0004 despite pricing in different currencies.

Do rejected parameters cost a request?

No. window '1y' came back INVALID_PARAM in 1.2 ms with the allowed values in detail.allowed, and nothing was charged. Failed calls are never billed.