Resale Price Comps: Sold and Asking Prices, 5 Marketplaces
Pricing
from $9.12 / 1,000 vinted comps
Resale Price Comps: Sold and Asking Prices, 5 Marketplaces
Resale price comps across marketplaces: what is it worth on Vinted and Grailed? One query in, one row out with count, median, cheapest and dearest per marketplace, plus sold price vs asking price and a cross marketplace arbitrage check. Only marketplaces that return comps are billed.
Pricing
from $9.12 / 1,000 vinted comps
Rating
0.0
(0)
Developer
Adrian Voss
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
a day ago
Last modified
Categories
Share
One search query in, one row out: how many listings each of five resale marketplaces has for it, what the median, cheapest and dearest price is on each, and — this is the part nobody else sells — what the thing has actually sold for, next to what people are currently asking for it.
Who it's for
- Resellers and arbitrage bots. "A listing just appeared. Is it cheap?" needs a price to compare
against, and the only honest comparison is the price things really sell at.
askingVsSoldGapPctanswers it in one number: negative means the cheapest asking price is below the going sold price. - Anyone deciding what to list something at. Five markets' medians side by side, in your own currency, shows where a piece sells dearest and where it is being given away.
- Cross-border buyers. The same jacket is not the same price in Paris, New York and Tokyo.
crossMarketGapPctis how far the cheapest listing sits below the market as a whole.
It is not a listings feed. If you want every listing, with new-listing and price-drop alerts, run the single-marketplace actors instead (see Related actors at the end). This one collapses a whole search into one statistical row per query, which is what makes it cheap enough to run across a watchlist of hundreds of queries.
What "sold" means here
This matters enough to be the second section on the page.
Only Yahoo! Auctions Japan returns genuine final prices. They come from Yahoo's own closed-auction search, where every row has at least one bid and carries the price the auction actually ended at. Those are real transactions.
The other four are asking prices. Vinted, Grailed, Poshmark and Vestiaire Collective search results are what sellers currently want, which is not what anyone paid:
- Vinted, Grailed and Vestiaire results are live, unsold listings.
- Poshmark does mark listings
sold_out, but it never reveals the negotiated final amount. A "sold" Poshmark listing only carries its own last asking price at the moment it went out of stock, so it is counted here as an asking price, because that is what it is.
Everywhere in the output the distinction is visible rather than implied:
| Column | What it is |
|---|---|
soldMedianPrice, soldSampleSize, yahoo* | Real final prices, from Yahoo's closed auctions |
bestAskingPrice, vinted*, grailed*, poshmark*, vestiaire* | Asking prices |
askingVsSoldGapPct | The cheapest asking price against the real sold median |
soldSampleSize travels with soldMedianPrice on purpose: a median of 3 auctions and a median of
50 are not the same evidence, and you should be able to see which one you got.
The five marketplaces
| Marketplace | Prices | Covers | Route |
|---|---|---|---|
| Vinted | asking | EU second-hand fashion, EUR | The vinted.fr catalog search, relevance order |
| Grailed | asking | Used menswear and streetwear, USD | Grailed's own first-party search index |
| Poshmark | asking | US resale fashion, bags, shoes, USD | A poshmark.com brand page narrowed by keywords |
| Vestiaire Collective | asking | Luxury resale, USD (US locale) | Vestiaire's own search API |
| Yahoo! Auctions Japan | SOLD | Japan's largest auction market, JPY | Yahoo's own closed-auction search |
One thing to know about Poshmark. poshmark.com/robots.txt disallows keyword search, so this
actor never requests it. Poshmark is read through brand pages instead, which robots.txt does allow.
That is why a query should start with the brand: Carhartt Detroit jacket becomes the Carhartt
brand page narrowed to "Detroit jacket". For a two-word brand, use the explicit form,
Louis Vuitton: neverfull. A query whose first word is not a Poshmark brand gets
poshmarkStatus: NO_RESULTS with a message naming the form that would have worked, and since
Poshmark returned nothing, it costs you nothing.
What you get
Per marketplace, seven columns. For Vinted, for example:
| Column | Meaning |
|---|---|
vintedCount | How many listings were matched and priced |
vintedMedianPrice | Median price, in your output currency |
vintedMinPrice / vintedMaxPrice | Cheapest and dearest, in your output currency |
vintedCurrency | What Vinted itself quoted in, before conversion (MIXED if more than one) |
vintedSampleUrl | Link to the single cheapest listing, the one to open first |
vintedStatus | OK, NO_RESULTS, NOT_CHECKED, BLOCKED, REQUEST_FAILED, NO_COMPARABLE_PRICES or BAD_INPUT |
Plus, once per row:
| Column | Meaning |
|---|---|
bestAskingPrice / bestAskingSource | The cheapest single asking price anywhere, and where it is |
soldMedianPrice / soldSampleSize | The real sold median, and how many auctions it is built from |
askingVsSoldGapPct | Cheapest asking vs sold median. Negative = the arbitrage case |
crossMarketGapPct | Cheapest asking vs the median of the marketplace medians |
sourcesChecked / sourcesFound | How many marketplaces were asked, and how many had comps |
currency / fxRateDate | The currency every price is in, and the FX snapshot used |
crossMarketGapPct is deliberately the median of the marketplaces, not of all the listings
pooled together. Pooling would let a marketplace that returned 20 listings outvote one that returned
2, which is a sampling artefact rather than a market signal.
Currency, and the fixed FX table
Pick one output currency (default EUR) and every price column is expressed in it. Two rules make
the numbers trustworthy:
- Prices are converted before any median is taken. A median is never computed across mixed
currencies. A listing in a currency the table does not cover is dropped from the statistics
rather than counted at face value, and if that leaves a marketplace with nothing comparable you
get
NO_COMPARABLE_PRICESinstead of a wrong number. - The rates are a frozen snapshot, not a live lookup. Every row is stamped with
fxRateDate, the date of the ECB reference rates used. That means two runs a week apart are comparable: a price change is a price change, not a currency wobble. It also costs you nothing, because no extra request is made to fetch rates.
Covered currencies: EUR, USD, GBP, JPY, PLN, CZK, SEK, DKK, HUF, RON, CHF, NOK, CAD, AUD, KRW. Ask for anything else and the run warns you and uses EUR rather than failing.
What a run costs, and how to control it
- "Which marketplaces to check (and pay for)" is the real control. Only the marketplaces you select are requested at all, and you are billed only for the ones that actually return comps. Drop Yahoo if you don't need sold prices and that whole leg disappears from the bill.
- "Pages to fetch per marketplace" defaults to 1, and one page per marketplace is what the published price is based on. Each extra page is another real request. Raise it only for a query so rare that one page doesn't fill your "listings to price" target.
- "Listings to price per marketplace" (default 20) does not change the cost. The cost is the page requests; reading 20 rows or 96 rows off the same page costs the same.
- "Sold prices only" makes a run cheaper as a side effect: only Yahoo can honour it, so the other four marketplaces are skipped entirely instead of having their asking prices relabelled.
A query that finds nothing anywhere is free. So is every individual marketplace that fails, is unreachable, or comes back empty, and one failing marketplace never affects the row or the other four.
How to use
- In the Apify Console. Open the actor page and click Start — the
queriesfield is already pre-filled with a working example. Results land in the run's dataset as soon as each item is found. - Via the API. Call it directly with a POST request — no Console needed once you have an API token:
curl "https://api.apify.com/v2/acts/accountable_eel~resale-comps-unified-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \-X POST \-H "Content-Type: application/json" \-d '{"queries":["Carhartt Detroit jacket","Nike Air Max 90","Levi's 501"]}'
- On a schedule. Save this actor as an Apify Task with the input you want, then add a Schedule (hourly, daily, weekly) so it runs on its own — no server of your own required.
Input
{"queries": ["Carhartt Detroit jacket","Nike Air Max 90","Levi's 501"]}
One product, brand or model per line. Each line is searched on every marketplace you select and comes back as ONE row: count, median, cheapest, dearest and a link per marketplace, plus the asking-vs-sold and cross-marketplace gaps. Put the BRAND FIRST — Poshmark's robots.txt disallows keyword search, so that source reads brand pages ("Carhartt Detroit jacket", or the explicit "Carhartt: Detroit jacket"). Accepted formats: Carhartt Detroit jacket, Nike Air Max 90, Louis Vuitton: neverfull.
Output
One row per query. Every column, for one query:
| query | found | status | vintedCount | vintedMedianPrice | vintedMinPrice | vintedMaxPrice | vintedCurrency | vintedSampleUrl | vintedStatus | grailedCount | grailedMedianPrice | grailedMinPrice | grailedMaxPrice | grailedCurrency | grailedSampleUrl | grailedStatus | poshmarkCount | poshmarkMedianPrice | poshmarkMinPrice | poshmarkMaxPrice | poshmarkCurrency | poshmarkSampleUrl | poshmarkStatus | vestiaireCount | vestiaireMedianPrice | vestiaireMinPrice | vestiaireMaxPrice | vestiaireCurrency | vestiaireSampleUrl | vestiaireStatus | yahooCount | yahooMedianPrice | yahooMinPrice | yahooMaxPrice | yahooCurrency | yahooSampleUrl | yahooStatus | bestAskingPrice | bestAskingSource | soldMedianPrice | soldSampleSize | askingVsSoldGapPct | crossMarketGapPct | sourcesChecked | sourcesFound | currency | fxRateDate | scrapedAt |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Carhartt Detroit jacket | true | OK | <vinted: listings found> | <vinted: median price> | <vinted: cheapest> | <vinted: dearest> | <vinted: native currency> | <vinted: cheapest listing> | <vinted: status> | <grailed: listings found> | <grailed: median price> | <grailed: cheapest> | <grailed: dearest> | <grailed: native currency> | <grailed: cheapest listing> | <grailed: status> | <poshmark: listings found> | <poshmark: median price> | <poshmark: cheapest> | <poshmark: dearest> | <poshmark: native currency> | <poshmark: cheapest listing> | <poshmark: status> | <vestiaire: listings found> | <vestiaire: median price> | <vestiaire: cheapest> | <vestiaire: dearest> | <vestiaire: native currency> | <vestiaire: cheapest listing> | <vestiaire: status> | <yahoo auctions jp (sold): listings found> | <yahoo auctions jp (sold): median price> | <yahoo auctions jp (sold): cheapest> | <yahoo auctions jp (sold): dearest> | <yahoo auctions jp (sold): native currency> | <yahoo auctions jp (sold): cheapest listing> | <yahoo auctions jp (sold): status> | <asking vs sold gap %> | <cross-marketplace gap %> | 1970-01-01T00:00:00.000Z |
A query nothing was found for comes back as a row with "found": false, a message saying why each
marketplace was empty, and no charge.
Example runs
Three inputs that need no credentials of any kind and return rows as they stand. Paste one into the Input tab and press Start.
1. What is a Carhartt Detroit jacket worth, everywhere?
{"queries": ["Carhartt Detroit jacket", "Carhartt: Detroit jacket blanket lined"],"currency": "EUR"}
2. Sold price vs asking price, before you list something
{"queries": ["Nike Air Max 90", "Nike: Air Max 95", "New Balance 990"],"currency": "USD","maxPerSource": 30}
3. A cross-marketplace arbitrage check on luxury bags
{"queries": ["Louis Vuitton: neverfull", "Gucci: marmont", "Prada: re-edition"],"currency": "EUR","sources": ["vinted", "vestiaire", "poshmark", "yahoo"]}
Pricing
Pay-per-event. A flat per-run fee covers session/proxy warmup; each independent
source is billed under its own event, only when that source actually returns
data — see .actor/pay_per_event.json for exact prices. A miss is never
charged.
Concretely: a query that hits three of five marketplaces is billed three events, not five and not one flat fee. A query that hits none is billed nothing at all.
Use it from Clay, n8n, Make, or an AI agent
This actor runs synchronously over plain HTTP — call it directly from a script, a workflow tool, or an AI agent, no Apify Console needed once you have an API token.
curl "https://api.apify.com/v2/acts/accountable_eel~resale-comps-unified-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \-X POST \-H "Content-Type: application/json" \-d '{"queries":["Carhartt Detroit jacket","Nike Air Max 90","Levi's 501"]}'
n8n. Add an HTTP Request node: Method POST, URL https://api.apify.com/v2/acts/accountable_eel~resale-comps-unified-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>, Body Content Type JSON, JSON Body {"queries":["Carhartt Detroit jacket","Nike Air Max 90","Levi's 501"]} (swap in an expression from an earlier node for a real value).
Clay. Add an "HTTP API" column: Method POST, URL https://api.apify.com/v2/acts/accountable_eel~resale-comps-unified-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>, Body {"queries":["{{search query}}"]}, mapping the row's search query into the queries array.
MCP. In Claude, Cursor, or any MCP client with the Apify MCP server, ask for "Resale price comps: Vinted, Grailed, Vestiaire, Poshmark" — the agent will find and run this actor.
vs. the alternatives
- Single-marketplace comps actors answer "what is it worth on Vinted?" — one market, asking prices only. This one answers "what is it worth, and where is it cheapest", across five, with a real sold price on the same row.
- Cross-country arbitrage checks compare one marketplace against itself in two countries. That finds shipping arbitrage; it does not tell you the same piece sells for a third more on a different platform.
- eBay sold comps are the usual answer to "sold, not asking", and eBay's sold-listings pages are behind a bot wall that does not open to an HTTP client. Yahoo! Auctions Japan's closed-auction search is genuinely open and genuinely final-price, which is why the sold column on this row exists at all.
Limits, stated plainly
- One country per marketplace. Vinted is read on
vinted.fr(EUR), Poshmark onposhmark.com(US), Vestiaire on its US locale; Grailed is USD-only and Yahoo is JPY-only. For a specific other country, run that marketplace's own actor, which exposes all of them. - Poshmark needs the brand first (see above). That is a robots.txt constraint, not a shortcut.
- A median is only as good as its sample. Every
*CountandsoldSampleSizeis on the row so you can see when a median rests on two listings. - Sold comps are Japanese auctions. They are real final prices, and they are the Japanese market's prices. For a Western-market sold price, treat them as a floor and a sanity check, not a local comp.
- Asking prices carry no shipping or fees. Every price is the item price as the marketplace states it.
Data & privacy
Only public search results are read, and only the fields needed to price them: an id, a title, a price, a currency and a link. No seller names, no seller ids, no seller profiles, no photos and no descriptions are requested or returned by this actor — a comps median does not need them, so they are never collected. No account, cookie or credential of yours is used; this actor signs into nothing.
FAQ
Is soldMedianPrice a real sold price?
Yes, and it is the only one on the row. It comes from Yahoo! Auctions Japan's own closed-auction
search, which lists auctions that ended with at least one bid, at the price they ended at. Every
other price column is an asking price and is labelled as one.
Why is askingVsSoldGapPct negative?
Because the cheapest asking price is below what things actually sell for. That is usually the
interesting case.
Why did one marketplace come back empty?
Read its *Status column. NO_RESULTS means it genuinely had nothing, NOT_CHECKED means you
didn't select it (or "Sold prices only" skipped it), BLOCKED means it refused the request this
time. In all of those, that marketplace cost you nothing.
Can I get the individual listings?
Not from this actor — it returns one statistical row per query by design, which is what keeps it
cheap. *SampleUrl gives you the cheapest listing on each marketplace; for the full list, run the
per-marketplace actors below.
Why is the FX rate fixed instead of live?
So that two runs a week apart are comparable, and so a run costs no extra request. The date is
stamped on every row as fxRateDate.
How many queries can one run take? As many as you like, each line is independent. Start with "Try it first" on to check the shape on five of them before running the whole list.
Related actors
For the full listing feed, with new-listing and price-drop alerts, on one marketplace:
vinted-listing-lookup, grailed-listing-lookup, poshmark-listing-lookup,
vestiaire-listing-lookup, yahoo-auctions-jp-lookup.