I wanted one boring, reliable number for resale items: what they actually sold for recently, not what people are asking. So I built SoldSignal, a small weekly index, and opened the data as a free API. No key, no signup, CORS open.
What the API returns
GET https://www.aiplannermail.com/api/v1/items.json gives the current week (right now 2026-W45, 52 items across sneakers, watches, collectibles, gaming and fashion). Each item has:
| Field | Meaning |
|---|---|
price |
the headline median for the week |
priceType |
SOLD or ASK, never mixed in one number |
sampleSize |
how many sales or listings the median is based on |
category, slug, name
|
what it is |
href |
a per-item JSON with more detail |
The per-item file (/api/v1/items/<slug>.json) adds the basis in plain words, retail and premium %, week-over-week change, per-platform medians, a lowSample flag, and the net payout after marketplace fees for each tracked platform.
Example (real, this week): Air Jordan 4 "Bred Reimagined", median $251 SOLD from 13 data points on GOAT, retail $215, about $215.71 net after GOAT fees.
SOLD vs ASK, never mixed
This was the main design rule. Asking prices are cheap to collect and often far from reality, so each item gets exactly one price type:
- SOLD (43 of 52 items this week): medians of recorded sales (GOAT last sales per size, Mercari sold listings).
- ASK (9 items, mostly watches): medians of live listings, labeled as asks, because there is no reliable public sold feed for them.
If a source only has a handful of sales, the item still shows, but lowSample is set and the page says so (Nintendo Switch 2 is $397.50 SOLD from only 6 sales, for example).
Weekly dated snapshots
The data refreshes once a week. Every snapshot is labeled with an ISO week ("week": "2026-W45") and an updated date, so you can store them and build your own history instead of trusting a number that silently changed.
3 ways to call it
curl, one item:
curl -s https://www.aiplannermail.com/api/v1/items/air-jordan-4-bred-reimagined.json
JavaScript (browser or Node 18+):
const { items } = await (await fetch("https://www.aiplannermail.com/api/v1/items.json")).json();
console.table(items.map(i => ({ name: i.name, price: i.price, type: i.priceType, n: i.sampleSize })));
Python:
import requests
items = requests.get("https://www.aiplannermail.com/api/v1/items.json").json()["items"]
for i in items:
print(f'{i["name"]}: ${i["price"]} {i["priceType"]} (n={i["sampleSize"]})')
There is also a CSV for spreadsheets (=IMPORTDATA("https://www.aiplannermail.com/data/latest/items.csv") works in Google Sheets) and an OpenAPI spec at /api/openapi.json.
A few numbers from this week
| Item | Median | Type | Sample |
|---|---|---|---|
| Pokemon Prismatic Evolutions Elite Trainer Box | $110 | SOLD | 17 |
| Pokemon Delta Reign Elite Trainer Box | $90 | SOLD | 25 |
| Labubu Big Into Energy blind box | $37 | SOLD | 45 |
| Stanley Quencher 40oz | $50 | SOLD | 24 |
| Nintendo Switch 2 | $397.50 | SOLD | 6 (low sample) |
Limits
- Weekly, not live. Good for trends and "is this a fair price", not for sniping.
- Sample sizes are small for new or niche items; always read
sampleSize. - Fee math uses each platform's published seller fees; shipping and promos vary.
- License is CC BY 4.0: use it, cite it.
Docs and examples: aiplannermail.com/free-resale-price-api. Feedback on the schema, and which items to add, is very welcome in the comments.
Top comments (0)