Vinted Demand & Sell-Through Scraper avatar

Vinted Demand & Sell-Through Scraper

Pricing

from $0.78 / 1,000 results

Go to Apify Store
Vinted Demand & Sell-Through Scraper

Vinted Demand & Sell-Through Scraper

Vinted demand intelligence: sell-through rate, realised sale prices, days-to-sell, and cross-market sourcing. No proxy, no PII.

Pricing

from $0.78 / 1,000 results

Rating

0.0

(0)

Developer

Unfenced Group

Unfenced Group

Maintained by Community

Actor stats

1

Bookmarked

4

Total users

2

Monthly active users

7 days ago

Last modified

Share

Vinted Demand & Sell-Through Scraper

Analyse demand and sell-through on Vinted at scale: per-query market reports with sold-versus-active ratios, price distributions and demand signals, ready for resale research and pricing decisions. No API key required.


Why this scraper?

🎯 Built for vinted

A dedicated European scraper tuned for vinted β€” its categories, price formats and currency (EUR), not a generic tool pointed at it.

🀝 Seller and condition

Seller details and item condition in structured fields, ready for filtering.


Input parameters

ParameterTypeDefaultDescription
searchQuerystring"nike air force 1"Product, brand, or model to scan across markets (e.g. 'nike air force 1', 'ralph lauren jumper').
countriesarray["fr", "de", "nl", "it", "es"]Vinted markets to scan. Pick 2-5 for a focused arbitrage scan. All listings are normalised to one currency for comparison.
maxResultsinteger100Total listings to return across all markets (split evenly). Vinted caps each query at ~960 per market.
baseCurrencystring"EUR"Currency all markets are converted to for price-gap comparison. Live ECB rates.
sortBystring"relevance"How listings are ordered within each market.
priceMininteger(empty)Minimum listing price in each market's local currency. Leave empty for no minimum.
priceMaxinteger(empty)Maximum listing price in each market's local currency. Leave empty for no maximum.
conditionarray(empty)Filter by item condition. Leave empty to include all conditions.
brandNamesarray(empty)Filter by brand β€” just type the names (e.g. 'Nike', 'Stone Island'). The actor looks up each brand's ID automatically. Leave empty for all brands.
brandIdsarray(empty)Advanced: filter by raw Vinted brand ID if you already know it. Most users should use Brand names above instead.
sizeIdsarray(empty)Advanced: filter by raw Vinted size ID for categories not covered by the dropdowns below. Find it in the URL after selecting a size filter. Leave empty for all sizes.
clothingSizesarray(empty)Filter by clothing size (XS–XXXL). For shoes, use the shoe size filter below. Leave empty for all sizes.
shoeSizesarray(empty)Filter by EU shoe size (38–47.5). For clothing, use the clothing size filter above. Leave empty for all sizes.
kidsSizesarray(empty)Filter by kids clothing size (age / cm, e.g. '6 yr / 116 cm'). Covers newborn to 16 years. Leave empty for all sizes.
colorsarray(empty)Filter listings by colour. Leave empty for all colours. Note: Vinted does not return a colour value per listing, so this narrows the scan but does not add a colour output field.
genderstring"all"Filter by department. Vinted sells far more than clothing β€” pick Home, Electronics, Video Games, Books & Media, Beauty, Jewellery, Sports, or Bags for non-fashion categories. Leave as 'All' to search everything. Note: if you pick a specific Category below, that takes precedence over this.
categorystring"all"Filter by product category. These are the verified cross-department categories. For any other category, use the advanced Category ID field below. If set, this overrides the Department filter.
categoryIdinteger(empty)Advanced: filter by a specific Vinted catalog ID not covered by the Category dropdown. Find it in the catalog URL (catalog[]=NUMBER). Overrides Department and Category if set.
includeListingsbooleantrueOutput every matching listing row. Disable to get only the cross-country arbitrage summary (cheaper).
dedupeAcrossMarketsbooleantrueVinted shows the same listing in every market it ships to. With this on, each item appears once, with a 'markets' field listing every market it was found in. Turn off to get one row per market.
fetchDetailsbooleanfalseFetch each listing's page for the seller's description, declared colour and full category path, and to resolve status precisely. Costs one extra request per listing. Leave off for fast, cheap catalog data.
fetchShippingbooleanfalseResolve the cheapest shipping option per listing and fill shippingCostEur and totalCostEur. Uses the same request fetchDetails makes, so turning both on costs no more than turning on either. Off leaves both fields null, never zero.
maxPhotosPerIteminteger10Cap on photo URLs emitted per listing (1-20). Only limits payload size; photoCount always reports the true number the listing holds.
sellThroughTrackingbooleanfalseMeasure real demand. Run this on a schedule (e.g. daily): the actor snapshots live listings and flags items that disappear as 'likely sold', giving you a sell-through rate and median days-to-sell for your niche. Overrides the normal scan output.
trackerNamestring(empty)A stable name for persistent state (e.g. 'nike-af1-kids'). Reuse it across scheduled runs so history accumulates: it drives sell-through tracking, and makes firstSeenAt a real first sighting rather than the current run's timestamp.
missingRunsBeforeSoldinteger2How many consecutive runs an item must be absent before it's marked likely sold. Use 2 to avoid false positives from search-ranking shuffle.
emitActiveItemsbooleantrueIn sell-through mode, also output items that are still listed (with days-listed and any price drop). Disable to output only newly-sold items and the summary (cheaper).
minSellThroughAlertPctinteger(empty)In sell-through mode, only send a webhook alert when the sell-through rate is at least this percentage. Leave empty to always alert. Pairs with the Telegram/Discord/Slack fields below.
includeArbitrageSummarybooleantrueWrite a summary comparing median/min/max prices across markets and naming the buy-low / sell-high pair. It goes to the ARBITRAGE_SUMMARY key in the key-value store, not into the dataset, so every dataset row stays a listing.
telegramBotTokenstring(empty)Optional. Send each run's arbitrage alert to Telegram. Create a bot with @BotFather and paste its token here. Also fill in the chat ID below.
telegramChatIdstring(empty)Optional. The chat or channel ID to send Telegram alerts to (e.g. your user ID, or -100... for a channel). Required if a bot token is set.
discordWebhookUrlstring(empty)Optional. Send each run's arbitrage alert to a Discord channel. Paste an Incoming Webhook URL (Server Settings β†’ Integrations β†’ Webhooks).
slackWebhookUrlstring(empty)Optional. Send each run's arbitrage alert to a Slack channel. Paste an Incoming Webhook URL (api.slack.com/messaging/webhooks).
minSpreadPctAlertinteger(empty)Only send alerts when the cross-country median spread is at least this percentage. Leave empty to always alert. Useful for scheduled runs that should only ping you on real opportunities.
alertTopListingsinteger5How many of the cheapest listings to include in each alert message.

Output schema

One row per listing. Every dataset row is a listing and nothing else. Run aggregates go to the key-value store (ARBITRAGE_SUMMARY, SELL_THROUGH_SUMMARY), so you can iterate the dataset without guarding against a summary object appearing among your results.

Every field below is present on every record. Fields the source does not publish for a given item are returned as null rather than omitted.

FieldTypeDescription
_typestringAlways LISTING.
idstringVinted listing ID.
countrystringMarket the listing was found in.
languagestringISO 639-1 language of the listing. See the note below on how far to trust it.
languageSourcestringdetected when the listing text decided the language, marketplace when it fell back to the market's primary language.
urlstringDirect listing URL.
titlestringListing title.
brandstringBrand name as Vinted classifies it.
sizestringSize label. Null for categories that have no size, such as trading cards.
conditionstringRaw condition string, in the market's own language.
conditionCodestringCondition normalised to one of new_with_tags, new_without_tags, very_good, good, satisfactory, unknown.
currencystringLocal currency of the listing.
itemPricenumberSeller's asking price (local currency).
buyerPricenumberItem price plus Buyer Protection. Shipping is not included.
serviceFeenumberBuyer Protection fee. itemPrice + serviceFee = buyerPrice.
shippingCostEurnumberCheapest shipping option, in EUR. Null unless fetchShipping is on. Never defaults to zero.
totalCostEurnumberLanded cost: buyerPrice + shippingCostEur, in EUR. Null when shipping is unknown.
statusstringactive, sold, reserved, hidden or unknown. Resolved precisely only with fetchDetails or fetchShipping on.
isAvailablebooleanWhether the listing can still be bought. Null when unknown.
favouriteCountnumberNumber of favourites. A demand signal; 0 means genuinely none.
isPromotedbooleanWhether the listing is a promoted or bumped item.
isBusinessSellerbooleanTrue for registered business sellers, false for private individuals.
photoCountnumberTrue number of photos on the listing, before maxPhotosPerItem capping.
photoUrlstringSame as photoUrls[0]. Kept for backwards compatibility.
photoUrlsarrayEvery photo URL in listing order, capped by maxPhotosPerItem.
dominantColorstringDominant colour of the main photo, as a hex string.
photoUploadedAtstringUpload timestamp of the main photo. The closest thing Vinted gives to a listing date.
contentHashstringSHA-1 of normalised id and title. Stable across every fetch mode, so it is safe as an incremental-run key.
firstSeenAtstringFirst time this actor saw the listing. Equals scrapedAt unless trackerName is set.
sourcestringAlways vinted-arbitrage-scanner.
scrapedAtstringTimestamp when this record was scraped.
itemPriceBasenumberitemPrice converted to baseCurrency.
buyerPriceBasenumberbuyerPrice converted to baseCurrency.
baseCurrencystringThe comparison currency.
marketsarrayEvery market this exact item was found in. Cross-market duplicates are merged into one row.
descriptionstringSeller's free-text description. Requires fetchDetails.
declaredColorstringColour the seller declared. Requires fetchDetails. Null for categories with no colour.
categoryPathstringVinted category path. Requires fetchDetails.

Photo URLs

photoUrls carries the full-size original of each image, recognisable by the /tc/ segment in the URL. These are the same files Vinted serves in its own gallery, typically 1200x1600. The smaller 600x800 render, if you ever see it, carries an /f800/ size token instead.

The listing search response includes every photo, so filling photoUrls costs no extra request. photoCount always reports the true number the listing holds, so comparing it to photoUrls.length tells you whether maxPhotosPerItem capped the array.

Shipping and landed cost

buyerPrice covers the item and Buyer Protection only. Vinted does not publish shipping in its search response, so resolving it means loading each listing's own page. That is the same page fetchDetails already loads, so switching both on costs no more than switching on either one.

With fetchShipping off, shippingCostEur and totalCostEur are null. They are never set to zero. If you are comparing an asking price against a reference price, use totalCostEur when it is present, because a 3 EUR shipping charge changes the answer on a 5 EUR item.

Availability

status and isAvailable are resolved from the listing's own page, so they need fetchDetails or fetchShipping switched on. From search results alone, status only records that the listing was visible when it was scraped; sold and reserved cannot be told apart at that point.

Vinted swaps between two status widgets on a listing page depending on whether the item can still be bought, and a sold listing carries no trace of the active one. Both are read, along with whether the page still offers a buy action, which is the signal that does not depend on language. Verified against 21 sold listings across four markets plus a set of active and removed ones: 19 of 19 classified correctly in a direct test.

A listing whose page has been removed entirely (HTTP 404) is reported as sold with isAvailable: false, not left at its search-time value. If a listing is off the market for a reason not seen before, status is unknown but isAvailable is still false β€” "cannot be bought" is the fact worth acting on, and it is reported even when the reason is not clear.

How far to trust language

Vinted markets carry cross-border inventory: vinted.nl serves French and Italian listings alongside Dutch. So the market domain is not the listing language, and both fields are given.

Listing titles are short and often carry no language at all, so language is set to the market's primary language by default and overridden only on decisive evidence. languageSource tells you which happened. Measured against 2,966 listings whose language was known, the override fires on about one title in six and is right about 81% of the time. It fires more often when fetchDetails is on, because a description gives it far more to work with.

If you filter on language-specific patterns, apply every pattern set when languageSource is marketplace, and narrow to one set only when it is detected.

No personal data

The dataset carries no seller identity: no usernames, no profile links, no avatars, no follower counts, no ratings. isBusinessSeller is a commercial classification, not a person, and is the only seller signal emitted.

Example record

{
"_type": "LISTING",
"id": "9887436943",
"country": "nl",
"language": "nl",
"languageSource": "marketplace",
"url": "https://www.vinted.nl/items/9887436943-winterjas-ameline-50",
"title": "Winterjas Ameline 50",
"brand": "Ameline",
"size": "5XL / 50 / 22",
"condition": "Heel goed",
"conditionCode": "very_good",
"currency": "EUR",
"itemPrice": 18,
"buyerPrice": 19.6,
"serviceFee": 1.6,
"shippingCostEur": 2.99,
"totalCostEur": 22.59,
"status": "active",
"isAvailable": true,
"favouriteCount": 9,
"isPromoted": false,
"isBusinessSeller": false,
"photoCount": 11,
"photoUrl": "https://images1.vinted.net/tc/05_018b1_Bj6B596Vqf371XciyBhXbqit/1788589081.jpeg?s=a676333608e980565c12cb150fe9a51e40375fea",
"photoUrls": [
"https://images1.vinted.net/tc/05_018b1_Bj6B596Vqf371XciyBhXbqit/1788589081.jpeg?s=a676333608e980565c12cb150fe9a51e40375fea",
"https://images1.vinted.net/tc/05_01f2b_4rL6Qpjz26sNQud4aggVckyk/1788589081.jpeg?s=a6923c76837729d0bd416d615792132de4825962"
],
"dominantColor": "#855A49",
"photoUploadedAt": "2026-09-05T06:18:01.000Z",
"contentHash": "c9ce1a11a52930d5074868f88651384fb55b6a57",
"firstSeenAt": "2026-09-07T12:08:47.884Z",
"source": "vinted-arbitrage-scanner",
"scrapedAt": "2026-09-07T12:08:47.884Z",
"itemPriceBase": 18,
"buyerPriceBase": 19.6,
"baseCurrency": "EUR",
"markets": ["nl"],
"description": "Beige winterjas polyester viscose ...",
"declaredColor": "Beige",
"categoryPath": "Dames Overjassen & Lange jassen"
}

Examples

Search for Nike hoodie:

{
"searchQuery": "Nike hoodie",
"maxResults": 100
}

Filter by category or condition:

{
"searchQuery": "Nike hoodie",
"maxResults": 100,
"condition": [
"used"
]
}

Larger run with a hard cap:

{
"searchQuery": "Nike hoodie",
"maxResults": 500
}

Daily monitoring (run on a schedule):

{
"searchQuery": "Nike hoodie",
"maxResults": 500
}

Schedule this input to run once a day in the Apify Scheduler to track prices, stock and new items over time.


πŸ’° Pricing

$0.8 per 1,000 results β€” you only pay for successfully retrieved market reports. Failed retries are never charged.

ResultsCost
100~$0.08
1,000~$0.8
10,000~$8
100,000~$80

Flat-rate alternatives typically charge $29–$49/month regardless of usage.

Subscription tiers lower the rate further, down to $0.76 per 1,000 at the highest tier. Use the Max results cap in the input to control your spend exactly.


Performance

Run sizeTypical time
100 resultsunder 1 minute
1,000 results2–4 minutes
10,000 results20–35 minutes

Known limitations

  • size and declaredColor are null for categories that have no such attribute, such as trading cards. That is a gap in the source, not a parsing failure.
  • Shipping is the cheapest option Vinted advertises for the listing. The final charge depends on the carrier and pickup point the buyer picks at checkout, so treat totalCostEur as a floor.
  • Without fetchDetails or fetchShipping, status reflects only that the listing was visible in search at scrape time. Sold and reserved are not distinguishable from search results alone.
  • reserved is supported but has not been observed on a live listing, so treat it as untested.
  • language is a best effort. Read the note under Output schema before filtering on it.
  • Vinted caps any single query at roughly 960 results per market. Narrow the query or add markets to go beyond that.
  • Prices and availability change constantly; records reflect the moment of scraping, and a share of URLs will expire shortly after.

Technical details

  • Source: vinted
  • Coverage: everything the public search exposes, filterable via the input parameters above
  • Retry: automatic retry on network errors with exponential backoff

Rate this actor

If this actor did the job, a rating on the Apify Store helps other people find it.

If something is wrong or missing, please open an Issue instead of leaving a low rating. Issues get fixed, usually quickly, and you will hear back.


Need a custom scraper?

Unfenced Group builds Apify actors for any website β€” for free.

If the site you need isn't in our portfolio yet, just ask. We scope, build, and publish it at no cost to you. You only pay for results β€” we absorb the compute and proxy costs ourselves. Same pay-per-result pricing, same quality, same standards as every actor in this portfolio.

Get in touch: www.unfencedgroup.nl