Vinted Scraper - Listings with Favourite Counts
Pricing
from $1.00 / 1,000 listings
Vinted Scraper - Listings with Favourite Counts
Collect verified Vinted listings from 26 marketplaces with favourite counts, search, category, brand, size, condition and price filters, and monitor new listings and favourite changes.
Pricing
from $1.00 / 1,000 listings
Rating
0.0
(0)
Developer
Amadeusz
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
a day ago
Last modified
Categories
Share
Vinted Scraper
Collect listings from Vinted with the number of favourites (favouriteCount) of every listing. Pick a marketplace (26 countries), give a phrase, a category, brands, sizes, condition and a price range, or paste a search URL copied from Vinted. Every listing is checked against your filters, de-duplicated and returned as clean JSON, ready for CSV, Excel, the API or AI agents.
Run Vinted Scraper · Input schema · Output schema
What you get
- Favourite counts:
favouriteCountin every record, the same number the listing page shows. - 26 marketplaces:
pl,fr,de,it,es,nl,be,lt,cz,sk,at,pt,hu,ro,hr,se,dk,fi,lu,gr,co.uk,com,ie,lv,ee,si. - Names instead of IDs:
categorytakes a path (Mężczyźni > Obuwie > Obuwie sportowe),brandstakes brand names,conditionstakes names (new_with_tags,new_without_tags,very_good,good,satisfactory). - Filters you can discover: a free run with
listFilterslists every filter of a category with its allowed values and the input field to put them in. - Price range, four sort orders, search URLs copied from Vinted (
startUrls), several searches in one run with shared de-duplication. - Verified results: filters are checked against what Vinted reports as applied. A filter that Vinted would silently ignore stops the run with an explanation instead of returning wrong data.
- No duplicates: the same listing is returned once per run, and you are charged once.
- Optional details (
enrichDetails): description, full photo list, category path, attributes, color, how long ago the listing was posted, seller profile and reputation. - Monitoring (
mode: monitor): each run returns only listings not reported by earlier runs; withtrackFavouritesit also returns achangerecord when the favourite count of a listing moved. - Free estimate (
countOnly): how many listings match your search, without scraping or charges. - More than 960 results per search: one Vinted search exposes at most 960 listings, so the Actor splits large searches by price automatically.
- One date format (UTC,
YYYY-MM-DDTHH:mm:ssZ) in every field.
Quick start
- Choose a Marketplace and type a Search phrase, or choose a Category.
- Add brands, condition, price range or other filters if you need them.
- Set Max items (the limit of listings returned and charged).
- Click Start. The default input (
country: pl,maxItems: 100) is a working example. - Open the Overview table, export JSON, CSV or Excel, or read the data through the API.
Not sure how many listings match? Start with Count only: it is free.
Pricing
Pay per event, no start fee:
| Event | Price | When |
|---|---|---|
listing | $1.00 / 1000 | One per listing returned, with favouriteCount |
listing-details | $4.00 / 1000 | One per listing when enrichDetails is on and the details were fetched |
listing-change | $0.50 / 1000 | One per change record (trackFavourites) |
Duplicates, rejected cards, empty runs, countOnly and listFilters runs and runs that end with an input or filter error are not charged. maxItems and your maximum cost per run are hard limits: the run stops when either is reached.
Cost of a run = listings returned × $0.001 (plus $0.004 per enriched listing and $0.0005 per change record). Run countOnly first to see how many listings match.
Ready-to-use recipes
1. Search by phrase on one marketplace
{"country": "pl","query": "nike air max","priceTo": 200,"sortBy": "newest","maxItems": 100}
2. Category, brand and condition
{"country": "fr","category": "Hommes > Chaussures > Baskets","brands": ["Nike", "Adidas"],"conditions": ["new_with_tags", "very_good"],"maxItems": 300}
Run listCategories to find the exact category name or ID, and listFilters with the same country and category to see every filter and value of the category.
3. Search URL copied from Vinted
{"startUrls": ["https://www.vinted.de/catalog?search_text=levis&order=newest_first"],"maxItems": 200}
The marketplace comes from the URL. A parameter the Actor does not know is rejected instead of ignored.
4. Monitor new listings and favourite changes
{"country": "pl","query": "iphone 13","mode": "monitor","trackFavourites": true,"seenStoreName": "vinted-iphone","maxItems": 200}
Schedule it in Apify. The first run returns what is there; later runs return only new listings and change records.
Use it from the API
$curl -X POST "https://api.apify.com/v2/acts/ziomixshot~vinted-scraper/run-sync-get-dataset-items?token=$APIFY_TOKEN" -H "Content-Type: application/json" -d '{"country":"pl","query":"nike air max","maxItems":50}'
The same Actor is available to AI agents through the Apify MCP server: add ziomixshot/vinted-scraper to the tools of the server.
Input
The full description of every field is in the input form. A field the Actor does not know (for example a typo) is rejected when the run starts.
| Field | Description |
|---|---|
country | Marketplace code, default pl. Ignored for startUrls (the URL decides). |
query | Search phrase. |
category | Category name or path in the language of the marketplace (Kobiety > Ubrania > Sukienki), a category code or a category page URL. A name used by several categories is refused with the list to choose from. |
brands | Brand names, matched to the Vinted brand with the same name (or the best match); the run log shows what was chosen. |
conditions | new_with_tags, new_without_tags, very_good, good, satisfactory. |
priceFrom, priceTo | Price range in the currency of the marketplace, inclusive. |
sortBy | relevance, newest, priceAsc or priceDesc. A price order starts from the cheapest (or most expensive) listing of the whole result, so combine priceAsc with priceFrom to skip very cheap ones. |
maxItems | Hard limit of returned listings and charges, shared by all startUrls (default 100). |
includePromoted | Vinted mixes promoted cards into results; false skips them (default true). |
enrichDetails | Fetch the listing page of each listing; each one also triggers the listing-details event. |
includeRawData | Add raw, the unchanged Vinted card of the listing. |
mode, seenStoreName, seenIdsKey, stopMonitorOnAllSeenPages, trackFavourites | Monitoring, see below. |
countOnly | Return only the number of matching listings (free). |
listFilters | Return the filters of the category instead of listings (free). |
listCategories | Return the category tree of the marketplace (ID, path, code, URL) instead of listings (free); category narrows it to paths containing that text. |
startUrls | Search URLs copied from Vinted, as strings or {url} objects. They replace all search fields above. |
categoryId, brandIds, sizeIds, colorIds, materialIds | Vinted numeric IDs, for when you already know them. listFilters shows them. |
otherFilters | Filters of the category without a field of their own, as filter code to a list of IDs, for example {"video_game_platform":[7]}. listFilters lists them; a filter the category does not have fails the run before any listing is charged. |
category and categoryId cannot be combined, and neither can brands and brandIds; the run stops with an explanation instead of guessing.
Output
Every listing is one dataset item. The dataset has an Overview view, and the Overview table also shows status, estimate, filter, category and change records.
Fields of every listing item: recordType, id, url, title, country, price, currency, priceWithDiscount, serviceFee (buyer protection), totalPrice, favouriteCount, brand (as shown on the card), size and condition (text in the language of the marketplace), isPromoted, thumbnailUrl, photos (card photos), seller (id, isBusiness), scrapedAt. A value Vinted does not provide is null.
Added by enrichDetails: detailsFetched, description, originalPrice, categoryId, categoryPath, brandId, sizeId, color, attributes, uploadedAgo, isReserved, isHidden, canBuy, all photos, and in seller: login, feedbackCount, feedbackReputation (0 to 1), lastLoggedIn (relative text, for example 3 godz.), badges. The favouriteCount of a detailed record is read from the listing page, so it is the freshest value. A listing Vinted no longer serves is skipped and not charged; the count is detailsUnavailable in the run status.
Other record types, always free except change:
status: the run ended because of your input and returned no listings. Fields:status,message(what is wrong and how to fix it),details,scrapedAt.estimate: result ofcountOnly:estimatedTotal,isLowerBound,sources,note.filter: result oflistFilters:code,title,selectionType,inputField,values,note.category: result oflistCategories:id,path,title,code,url,hasSubcategories,country. PutidintocategoryId, orpathintocategory.change: see monitoring.
Run status
The OUTPUT record of the key-value store describes the run: status (OK, PARTIAL, INVALID_INPUT, URL_NOT_FULLY_RESOLVED, FILTER_VERIFICATION_FAILED, SEEN_STATE_INVALID, UPSTREAM_ERROR), counters and jobs[] per search (criteria as sent to Vinted, expectedTotal, coverage, partitions, truncatedPartitions, failedPages). Input and filter errors end the run without listings and without charges. PARTIAL means some pages or details failed after all retries; the returned records are still valid.
Monitoring new listings and favourite changes
mode: monitor returns only listings whose ID was not reported by earlier runs with the same seenStoreName and seenIdsKey (a named key-value store in your account). Without seenStoreName the Actor uses a store named after the search (vinted-monitor-..., shown in the run log), so repeating the same search keeps its history; set a name to share one history between different runs. The first run returns everything up to maxItems; later runs return only new IDs.
- It always reads newest first and ignores
sortBy, does not split the search and skips promoted cards. - It reads pages until
stopMonitorOnAllSeenPagesconsecutive pages hold nothing new. - When
maxItemsstops it before that, the listings it did not return stay unseen and the next run returns them (the run log says so). RaisemaxItemsto catch up in one run. - State is saved only for listings that were actually returned. A corrupted state record ends the run with
SEEN_STATE_INVALIDinstead of starting over.
Tracking favourites (trackFavourites)
With trackFavourites: true the run also returns a change record for every listing whose favouriteCount changed since the previous run: recordType: "change", metric: "favourites", id, url, title, country, previous, current, delta and changedAt. New listings are still returned as listing items.
- It compares the cards the run reads anyway (the newest listings of the search, up to the first page without new IDs), with no extra requests. A listing that falls off those pages is not checked.
- The first time a listing is seen only a baseline counter is stored; a change needs a stored value to compare with.
- Counters are stored next to the seen IDs, in the record named like
seenIdsKeyplus_FAVOURITES(up to 200000 newest listings). maxItemslimits listings and change records together. A change beyond the limit is kept for the next run.
Estimate (countOnly)
Returns one item with recordType: "estimate" and estimatedTotal. One Vinted search reports at most 960 matches, so a result of 960 is a lower bound (isLowerBound: true, the run message says "at least 960"): narrow the filters to see the real number. Counting beyond 960 would mean reading the search page by page, which is what a run does. With several startUrls the result is the sum of the searches. It is not charged.
Limits and notes
- One search exposes at most 960 listings (10 pages of 96). For
maxItemsabove that the Actor splits by price. Sorting then applies only inside each part.truncatedPartitionsabove 0 means more than 960 listings with the same price that cannot be separated. - Vinted shows no absolute posting date on the listing page:
enrichDetailsreturns the relative text asuploadedAgo(for example2 dni). Vinted shows no public view counter, so there is none in the output; the favourite count is the only public counter. priceis in the currency of the marketplace; Vinted converts listings from other countries.originalPrice(withenrichDetails) is the price as the seller listed it.- Names of categories and conditions are in the language of the marketplace. A category name that is not found is answered with close names;
listCategoriesshows the whole tree. - A filter code in
otherFiltersthat the category does not offer stops the run before anything is charged, with the codes the category has. maxItemsis one budget for the whole run: with severalstartUrlsthe first URLs use it up first.- Vinted protects its site with anti-bot systems. Runs use Apify residential proxies in the country of the marketplace (included in the platform usage cost). A page that cannot be read after all retries is counted in
failedPagesand the run endsPARTIAL. - The Actor uses the web interface of Vinted, which is unofficial and can change without notice.
- The Actor reads public pages anonymously. It never logs in, never favourites, messages or buys, and never changes anything on an account.
Legal notice
This Actor collects data that Vinted shows publicly. Vinted's terms of service restrict automated access to the site and the use of its content; you are responsible for using the data lawfully and in line with the terms of the marketplace you read. Listings contain personal data of sellers (login, ID, reputation), so GDPR rules apply to how you store and use them. The Actor is not affiliated with Vinted.
Development
npm test, npm run typecheck, npm run lint. The mapping of the Vinted network requests lives in docs/openapi/ (openapi.yaml, ENDPOINTS.md, captures/); npm run openapi regenerates it from the Playwright captures in tmp/har/ (npm run openapi:capture). Backlog: docs/backlog.md, architecture: docs/diagram.md, competitors and pricing: docs/pricing.md.