# Vinted Scraper: New Listing & Price Drop Alerts (`accountable_eel/vinted-listing-lookup`) Actor

Vinted scraper for 22 countries: up to 2,000 listings per search, past the 96-per-page cap. Monitoring returns new listings, price drops and gone ones, a Vinted sold items tracker with sell-through rate and cross country price gap in euros. Brand, size, condition, link. No login. Pay per listing.

- **URL**: https://apify.com/accountable\_eel/vinted-listing-lookup.md
- **Developed by:** [Adrian Voss](https://apify.com/accountable_eel) (community)
- **Categories:** E-commerce, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.28 / 1,000 listing returneds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Vinted Scraper: New Listing & Price Drop Alerts

**Watch a search, get only what's new.** New listings and price drops since your last run, charged
per new row, free on quiet days. Schedule it hourly and send it to Discord, Slack, Google Sheets or
n8n.

This actor searches Vinted the way you do in the app: type keywords (or paste a search link with
your filters), pick one or more of Vinted's 22 European country sites, and get one clean row per
listing with the title, price, the total a buyer pays with Vinted's buyer protection fee, a euro
conversion, brand, size, condition, favourites, the main photo and the link. Turn on monitoring
and each run returns only the listings that appeared, or got cheaper, since the previous run, and,
if you ask, the ones that disappeared: a Vinted sold items tracker with a running sell-through rate
and a cross country price gap on every row.

### Who it's for

- **Resellers and vintage flippers** who lose good pieces to whoever refreshes the app fastest. A
  watchlist on "arcteryx beta" or a pasted search with your size and price cap, run every hour,
  puts new listings in your Discord or Telegram without keeping a phone open.
- **Sniper-bot and alert-bot builders** who are tired of maintaining their own scraper each time
  Vinted changes its site. You get a stable JSON row per listing across every country and keep
  your bot logic.
- **Price researchers and brand teams** who want to know what a model really sells for on the
  second-hand market in France versus Poland versus the UK, in one currency.
- **Marketplace analysts and trust teams** tracking business (Vinted Pro) sellers listing a brand,
  without collecting who those sellers are.

### Why this one

- **Past Vinted's 96-per-page and 960-per-search ceiling:** price-band fan-out returns up to 2,000
  listings per search per country.
- **Every Vinted country in one run.** One keyword line runs on vinted.fr, .de, .pl, .co.uk and the
  rest of the sites you pick. Rows carry the country, the local currency and `priceEur` at the
  European Central Bank's daily reference rate, so a cross-border price comparison is one sort.
- **Monitoring built in, not bolted on.** `deltaMode` remembers what each watchlist has seen per
  country. New listings and price drops (item price, with your own minimum drop %) come back;
  everything else is removed before billing. A quiet run returns one summary row and costs only
  the start fee.
- **A sold signal, not just asking prices.** Listings that disappear from a watched search come
  back as `gone`, flagged `gonePresumedSold` when they were cheap and stayed up for more than one
  run, and every row carries the watchlist's sell-through rate and the search's median price in
  euros. No extra requests: it is worked out from the listings each run already reads.
- **Seller privacy by default.** You get `private` or `business` and an anonymous seller hash to
  group listings by seller. No usernames, no profile links, no contact details.
- **Listing details when you want them.** Description (with emails and phone numbers removed),
  colour, upload time, reserved flag, seller rating and review count, billed separately and only
  when they arrive.

### Countries

Keywords run once per country you list in `countries`. A pasted Vinted link always runs on its own
site.

| Code | Site | Currency | Code | Site | Currency |
|---|---|---|---|---|---|
| `fr` | vinted.fr | EUR | `pt` | vinted.pt | EUR |
| `de` | vinted.de | EUR | `uk` | vinted.co.uk | GBP |
| `it` | vinted.it | EUR | `ie` | vinted.ie | EUR |
| `es` | vinted.es | EUR | `se` | vinted.se | SEK |
| `nl` | vinted.nl | EUR | `fi` | vinted.fi | EUR |
| `be` | vinted.be | EUR | `dk` | vinted.dk | DKK |
| `at` | vinted.at | EUR | `hu` | vinted.hu | HUF |
| `pl` | vinted.pl | PLN | `ro` | vinted.ro | RON |
| `cz` | vinted.cz | CZK | `hr` | vinted.hr | EUR |
| `sk` | vinted.sk | EUR | `gr` | vinted.gr | EUR |
| `lt` | vinted.lt | EUR | `lu` | vinted.lu | EUR |

Minimum and maximum price are applied in each site's own currency (euros on vinted.fr, zloty on
vinted.pl). Brand, size and condition come back in the site's language, exactly as Vinted shows
them ("Très bon état", "Bardzo dobry", "Very good").

### What you get

By default each listing is its own row. Every row also repeats the search it came from.

| Field | What it is |
|---|---|
| `listingId`, `url`, `title` | Vinted's listing ID, the listing link and its title |
| `price`, `currency` | The seller's price in the site's currency |
| `totalPrice` | What a buyer pays including Vinted's buyer protection fee (shipping not included) |
| `priceEur` | `price` in euros at the ECB daily reference rate (same number on euro sites) |
| `brand`, `size`, `condition` | As shown on Vinted; `size` is empty for items without one, such as bags (about 4% of a clothing search) |
| `favouriteCount`, `isPromoted` | Favourites so far, and whether the listing is a paid promotion |
| `sellerType`, `sellerHash` | `private` or `business`, and an anonymous 16-character seller hash |
| `imageUrl` | Link to the main photo (links only, nothing is downloaded) |
| `country` | Which Vinted site the listing is on |
| `isNew`, `changeType`, `firstSeenAt` | Monitoring: `new`, `price-drop`, `gone` or `seen`, and when this watchlist first saw it |
| `lastSeenAt`, `daysListed` | Monitoring: the last run that saw the listing, and the days between first and last sighting |
| `previousPrice`, `priceDropPct` | Monitoring: the price last seen and the drop in %, on price-drop rows |
| `lastPrice`, `gonePresumedSold` | Monitoring, on `gone` rows only: the last price seen, and the presumed-sold flag (see below) |
| `medianPriceEur`, `countryMedianPriceEur` | Monitoring: median euro price of everything the run read for this search, across all countries and in this row's country |
| `priceGapPct` | Monitoring, 2+ countries: this row's euro price against the cross-country median, in % (negative = cheaper) |
| `sellThroughRate` | Monitoring: listings that went gone / listings ever tracked by this watchlist, 0 to 1 |
| `searchQuery`, `searchCountries`, `listingCount`, `totalAvailable`, `truncated` | The search summary |
| `newCount`, `priceDropCount`, `goneCount`, `monitorStatus` | Monitoring summary: `NEW_ROWS`, `NO_NEW_ROWS` or `SEEDED` |
| `description`, `color`, `postedText`, `postedAt`, `sellerRating`, `sellerReviewCount`, `isReserved` | Only with "Fetch listing details" on |

Three dataset views are ready in the Console: **Overview**, **Monitoring** (the change type,
previous price and drop % next to each listing) and **Sold signals** (gone listings, presumed-sold
flag, days listed, sell-through rate and price gap).

Not available anywhere on Vinted's public pages, so not in this actor: the seller's city, an exact
upload timestamp (Vinted only says "2 hours ago", which `postedAt` converts approximately for
English, French, German, Italian, Spanish, Dutch, Portuguese and Polish), and confirmed sales or
sold prices. The sold signal below is inferred, never confirmed.

### Monitoring: new listings and price drops

The recipe most buyers use:

1. Put your search in `searches` (keywords, or paste the link of a Vinted search with your brand,
   size and price filters), pick `countries`, and turn on **Only return listings that are new, or
   cheaper, since the last run** (`deltaMode`).
2. Optionally turn on **Seed silently** so the first run remembers today's listings without sending
   them all to your webhook.
3. Save it as a Task and add an hourly **Schedule** in Apify.
4. Add a webhook or integration on the Task for **Run succeeded**: Discord or Slack webhook,
   Telegram bot via n8n or Make, a Google Sheets append, or an HTTP call to your own bot. Each run's
   dataset holds only the new and price-drop rows.

```json
{
  "searches": ["carhartt detroit jacket"],
  "countries": ["fr", "de", "nl"],
  "maxPrice": 120,
  "deltaMode": true,
  "skipFirstRun": true,
  "alertOnNew": true,
  "alertOnPriceDrop": true,
  "minPriceDropPct": 10
}
```

How it decides:

- **New** means this watchlist has never returned that listing ID before, on that country site.
- **Price drop** means the item price (not the total with fee) is at least `minPriceDropPct` below
  the price last seen. The remembered price updates every run, so a second cut is measured from
  the latest price.
- Unchanged listings are removed before you are billed. A run with nothing new returns one summary
  row with `monitorStatus: NO_NEW_ROWS`, `listingCount: 0`, and costs only the start fee.
- **Gone** (only with "Alert on gone listings" on) means a listing seen on the previous run has
  disappeared from the part of the search this run covered. See "Sold signal and sell-through".
- Each watchlist remembers up to 5,000 listings per country and search, oldest forgotten first,
  and forgets any listing it has not seen for 30 days.
- The watchlist name is derived from your price, seller and keyword filters, so two schedules with
  different filters never share a memory. Set `deltaName` to choose your own.

**The window limitation.** A run only sees the newest `maxListingsPerSearch` listings (96 by
default). A listing that drops in price after it has slid out of that window is not seen again, so
its price drop is missed. For price-drop alerts keep searches narrow (brand plus model, a size, a
price cap) or raise `maxListingsPerSearch`. New-listing alerts are not affected as long as fewer
than `maxListingsPerSearch` listings appear between two runs; an hourly schedule on a busy keyword
may need 192 or more.

### Sold signal and sell-through

Turn on **Alert on gone listings** (`alertOnGone`) next to `deltaMode` and a watchlist also returns
the listings it saw on its previous run that have since disappeared from the search.

**What "gone" means, honestly.** Vinted never confirms a sale on its public pages, and a sold item
simply stops showing in search. So does an item the seller deleted, reserved for a buyer, hid while
on holiday, or edited so it no longer matches your filters (a price raised above your
`maxPrice`, for example). `gone` means delisted: most often sold, sometimes withdrawn. Treat it as
a strong signal, not a receipt.

- **`gonePresumedSold: true`** when the listing's last price was at or under the search's median
  price in its country and it was seen on at least 2 runs in a row. A cheap listing that stayed up
  and then vanished is the pattern of a sale; one that appeared once and vanished, or was priced
  above the market, is only `gone`.
- **`firstSeenAt`, `lastSeenAt`, `daysListed`, `lastPrice`** tell you how long it was up (at least,
  since your runs only see it between those two times) and what it was asking.
- **`sellThroughRate`** is listings gone / listings ever tracked, over the lifetime of the
  watchlist, summed across its countries. It is on every row (and on the summary row of a quiet
  run), and counts gone listings even when `alertOnGone` is off.
- **`medianPriceEur`**, **`countryMedianPriceEur`** and **`priceGapPct`** are the run's price
  picture: medians of every listing the run read (not just the ones returned), converted to euros
  at the ECB rate so zloty and euro listings compare like for like, and each row's gap to the
  cross-country median when you search 2 or more countries. A cross country price gap of -30% is
  the arbitrage shortlist.

**How a run decides a listing is gone, not just out of view.** A run only reads the newest
`maxListingsPerSearch` listings, and a listing pushed past that window by newer ones is still for
sale. The actor remembers each listing's position in Vinted's newest-first feed, counts the new
arrivals above it, and calls it gone only if it should still sit inside the window (with a margin
of 2 places or 5%, whichever is larger). It does not use listing IDs for this: Vinted's feed is in
publish order and old IDs regularly appear near the top. Listings near the bottom edge of the
window are therefore never called gone, so give a sold-signal watchlist some headroom (for example
`maxListingsPerSearch` 192 for a search that gets 50 new listings between runs). No gone check is
made on a watchlist's first run, on a run a Vinted site refused partway, or on a run above 960
listings per search (price-band fan-out reads the catalogue out of feed order).

A gone listing is reported once. If it shows up again later (a reservation that fell through), it
is quietly taken back off the sell-through count. Gone rows are billed as ordinary `listing` rows,
at the same price, only when `alertOnGone` is on; no item page is ever opened for them. The
watchlist forgets a listing it has not seen for 30 days.

```json
{
  "searches": ["carhartt detroit jacket", "arcteryx beta"],
  "countries": ["fr", "de"],
  "maxListingsPerSearch": 192,
  "deltaMode": true,
  "deltaName": "sell-through-outerwear",
  "alertOnNew": false,
  "alertOnPriceDrop": false,
  "alertOnGone": true
}
```

Scheduled daily, that returns only the listings that went, with `gonePresumedSold`, `daysListed`
and the running `sellThroughRate`: a Vinted sold items tracker for a brand list, in a Google Sheet.

### More than 960 listings per search

Vinted shows 96 listings per page and stops every search at 960 (10 pages of 96); many Vinted
scrapers stop at the first 96. This one pages through all 10, and past 960 returns up to 2,000
listings per search per country. `totalAvailable` shows 960 when there are 960 or more.

Set `maxListingsPerSearch` above 960 (up to 2,000, sort newest first) and the actor:

1. reads page 1, takes the median price, and splits the search into two price bands at that point,
   using Vinted's own price filters (decimal bounds, no gaps between bands);
2. repeats on every band still at the cap, up to 16 bands;
3. reads the bands newest first, merges them by listing ID and removes duplicates.

In testing against a simulated 50,000-listing search it returned exactly the newest 2,000 in 45 page
requests. `truncated` becomes `true` only when a band that was still needed hit the 960 cap (for
example thousands of listings at one identical price), or the run had to stop early.

### Price

- **Listing returned**: $3 per 1,000 listings
- **Listing details added**: $10 per 1,000 listings

Plus a $0.00005 start fee per run. Each event above is billed independently, only when it actually returns data — misses (`found:false`) are never charged.

- **Listings:** $3 per 1,000 listings returned, plus a $0.00005 start fee per run.
- **Listing details** (optional): $10 per 1,000 listings whose details arrived.
- **Gone listings** (with "Alert on gone listings" on) are ordinary `listing` rows at the same
  price. No separate event, no extra request.
- A monitoring run with nothing new costs the start fee only. A search that returns nothing, or a
  country that fails, is never billed.

An hourly watchlist on one search that finds 5 new listings an hour costs about 5 × 24 × 30 = 3,600
listings, **about $11 a month**. The same watchlist with details on adds about $36.

### How to use

1. **In the Apify Console.** Open the actor page and click **Start** — the `searches` field is already pre-filled with a working example. Results land in the run's dataset as soon as each item is found.
2. **Via the API.** Call it directly with a POST request — no Console needed once you have an API token:
   ```bash
   curl "https://api.apify.com/v2/acts/accountable_eel~vinted-listing-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \
     -X POST \
     -H "Content-Type: application/json" \
     -d '{"searches":["carhartt"]}'
   ```
3. **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.

Tips:

- Paste a link from Vinted when you need filters the input doesn't list (brand, size, colour,
  category, material). Everything in the link is kept; monitoring only forces newest first.
- Use `sellerType: business` to see only Vinted Pro sellers; the actor keeps reading pages until it
  has enough matching listings (within the 960 cap).
- `includeKeywords` and `excludeKeywords` match the title and brand before billing, so "kids" or
  "replica" in `excludeKeywords` never costs you a row.

### Input

```json
{
  "searches": [
    "carhartt"
  ]
}
```

One search per line: plain keywords such as "carhartt jacket", or a search link copied from your browser on any Vinted country site (its own filters such as brand, size and category are kept). Keywords run once in every country you pick below; a pasted link runs on its own country site. No login required. Accepted formats: carhartt, levis 501, https://www.vinted.fr/catalog?search\_text=stone%20island\&order=newest\_first.

### Sample output

| query | found | status | searchQuery | searchCountries | listingCount | totalAvailable | truncated | newCount | priceDropCount | monitorStatus | goneCount | sellThroughRate | medianPriceEur | listings | listingId | title | price | totalPrice | currency | priceEur | countryMedianPriceEur | priceGapPct | brand | size | condition | country | favouriteCount | isPromoted | sellerType | sellerHash | imageUrl | url | isNew | changeType | firstSeenAt | lastSeenAt | daysListed | description | color | postedText | postedAt | sellerRating | sellerReviewCount | isReserved | scrapedAt |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| carhartt | true | OK | carhartt | fr | 96 | 960 | false |  |  |  | \<gone since last run (delisted, most often sold)> | \<sell-through rate, watchlist lifetime (0-1)> | \<median price of the search, eur> | \<all listings found (full list)> | 10103016508 | Cord Hemd | 45 | 47.95 | EUR | 45 | \<median price in this country, eur> | \<cross-country price gap % (vs median of all countries)> | Carhartt | M | Très bon état | fr | 0 | false | private | e07fdc8f8fb25230 | https://images1.vinted.net/t/02\_014f0\_QzU51Lq1rYmhRu5ToEaRCGSs/f800/be0fc870.webp?s=2f16bc49cc6da33d05603c73f65b54dc5b9bfeb9 | https://www.vinted.fr/items/10103016508-cord-hemd |  |  |  | <last seen on a run> | \<days listed (first to last seen)> |  |  |  |  |  |  |  | 2026-09-23T06:20:13.605Z |

A real row from a `levis 501` run on vinted.pl (2026-09-17), trimmed:

```json
{
  "searchQuery": "levis 501",
  "searchCountries": "de, pl",
  "listingCount": 192,
  "totalAvailable": 1920,
  "truncated": false,
  "listingId": "10033153698",
  "url": "https://www.vinted.pl/items/10033153698-levis-501",
  "title": "Levis 501",
  "price": 39.13,
  "totalPrice": 43.99,
  "currency": "PLN",
  "priceEur": 9,
  "brand": "Levi's",
  "size": "46 | W29",
  "condition": "Nowy z metką",
  "favouriteCount": 0,
  "isPromoted": false,
  "sellerType": "private",
  "sellerHash": "44a85cb9d8d5464a",
  "country": "pl"
}
```

### 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.

```bash
curl "https://api.apify.com/v2/acts/accountable_eel~vinted-listing-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{"searches":["carhartt"]}'
```

**n8n.** Add an HTTP Request node: Method `POST`, URL `https://api.apify.com/v2/acts/accountable_eel~vinted-listing-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>`, Body Content Type `JSON`, JSON Body `{"searches":["carhartt"]}` (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~vinted-listing-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>`, Body `{"searches":["{{search}}"]}`, mapping the row's search into the `searches` array.

**MCP.** In Claude, Cursor, or any MCP client with the Apify MCP server, ask for "Vinted Scraper: New Listing and Price Drop Alerts" — the agent will find and run this actor.

**Discord or Slack alerts without code.** Schedule the monitoring Task hourly, then add an
integration on the Task: in n8n or Make, trigger on "Apify: run succeeded", read the run's dataset,
skip rows where `listingCount` is 0, and post `title`, `price`, `currency`, `size` and `url` to a
Discord or Slack webhook (or a Telegram bot node). Google Sheets users can append the same rows to a
sheet for a running price log.

### vs. alternatives

| | What it costs | What you get | Trade-off |
|---|---|---|---|
| **This actor** | $3 per 1,000 listings, details $10 per 1,000, quiet monitoring runs free | 22 Vinted countries in one run, euro prices, built-in new-listing, price-drop and gone (sold signal) monitoring, sell-through rate, cross country price gap, up to 2,000 listings per search | Search results only; no seller city or exact upload time, because Vinted doesn't show them publicly |
| Popular Vinted scrapers on the Apify Store | $0.50 to $20 per 1,000 | A dump of search results, usually one country per run | The most-used one is rated 2.92 stars with 3 active users last week; none does multi-country plus monitoring, so you rebuild "what's new" yourself |
| Paid Discord alert bots | Monthly subscription | Alerts in their server, their filters | You don't own the data or the filters, and can't feed Sheets or your own bot |
| Self-hosted sniper bots (GitHub) | Free, plus your server and time | Full control | Break whenever Vinted changes its site; you maintain proxies, parsing and dedupe |

Store figures as of September 2026.

### Data & privacy

**Data & privacy.** This actor reads public search results that anyone can see without logging in. It
doesn't log in, solve CAPTCHAs or reveal hidden contact details. Seller identity is off by default: you
get a private/business flag and an anonymous seller hash so you can group listings by seller without
names. Turning on seller info makes you responsible for having a lawful reason to process it. Not
affiliated with Vinted.

Photos on Vinted often show the seller wearing the item, so only the main photo link is returned by
default and nothing is ever downloaded. Descriptions have emails, phone numbers, @handles and chat
links replaced with `[redacted]`.

### FAQ

**Is this allowed?**
It collects the same public listing data your browser shows, for the searches you
choose. It's built for monitoring a search, not for copying the marketplace. Check that your use fits
Vinted's terms and your local law.

**Why did my monitoring run return nothing?**
Nothing new or cheaper appeared since the last run. You get one row with `monitorStatus:
NO_NEW_ROWS` and are charged only the start fee. On the very first run with "Seed silently" on, the
status is `SEEDED`.

**Why is `truncated` true?**
Either a price band the run still needed hit Vinted's 960 cap, or a country site refused the run
partway (the listings collected up to then are delivered and billed; nothing else is).

**Does it find sold listings?**
It finds listings that stopped being for sale. Vinted's search only shows listings on sale and
never confirms a sale, so with monitoring and "Alert on gone listings" on, a listing that
disappears from your watched search comes back as `gone`, with `gonePresumedSold` when it looks
like a sale (cheap, up for 2+ runs). That is delisted, most often sold or withdrawn; see "Sold
signal and sell-through" above. Sold prices are never shown by Vinted, so `lastPrice` is the last
asking price.

**Does `totalPrice` include shipping?**
No. It is the item price plus Vinted's buyer protection fee, as Vinted shows it in search.
Shipping depends on the buyer's address and carrier.

**Can I get the seller's name or city?**
No. Names are deliberately never returned, and the city isn't on Vinted's public listing pages. Turn
on "Include the raw Vinted seller ID" only if you have a lawful reason to process it.

**What do I need to set up?**
Nothing. No Vinted account, no cookies, no proxy settings. The actor runs on Apify's standard
proxy; when a Vinted country site refuses a page, it retries on fresh connections and then, for that
page only, on a residential connection. That fallback is included in the listing price; turn off
"Retry refused pages on a residential connection" if you never want it used.

**Why did one country come back empty?**
If a Vinted site kept refusing the run even after the retries, that country is skipped, the other
countries are still delivered, and `truncated` is `true`. Nothing is billed for the skipped country.

**Can an AI agent call this?**
Yes, through the Apify MCP server or the API call shown above. Ask for "Vinted Scraper: New
Listing and Price Drop Alerts".

### Related actors

- [OLX Listing Lookup](https://apify.com/accountable_eel/olx-listing-lookup): the same one row per
  listing shape for olx.pl classifieds.
- [Kleinanzeigen Listing Lookup](https://apify.com/accountable_eel/kleinanzeigen-listing-lookup):
  Germany's largest classifieds site.

# Actor input Schema

## `searches` (type: `array`):

One search per line: plain keywords such as "carhartt jacket", or a search link copied from your browser on any Vinted country site (its own filters such as brand, size and category are kept). Keywords run once in every country you pick below; a pasted link runs on its own country site. No login required. Accepted formats: carhartt, levis 501, https://www.vinted.fr/catalog?search\_text=stone%20island\&order=newest\_first. You're only charged for the ones we actually find — a miss costs nothing.

## `countries` (type: `array`):

One country code per line. Keywords run once in each: fr, de, it, es, nl, be, at, pl, cz, sk, lt, lu, pt, uk, ie, se, fi, dk, hu, ro, hr, gr. Prices come back in each site's own currency plus a euro conversion. Ignored for pasted Vinted links, which use their own country. Leave empty for France.

## `testRun` (type: `boolean`):

Turn this on to test your input on a small sample before running the full list. Turn it off to process everything.

## `onlyFound` (type: `boolean`):

Only keep rows where something was actually found. Misses are always free, whether or not you show them here.

## `includeKeywords` (type: `array`):

Optional. Only keep results that mention at least one of these words (e.g. a job title, a city, a product name). Leave empty to keep everything.

## `excludeKeywords` (type: `array`):

Optional. Drop any result that mentions one of these words. Leave empty to skip nothing.

## `maxResults` (type: `integer`):

Optional. Stop the run once this many results have been found — useful for a quick, cheap sample. Leave blank for no limit.

## `sort` (type: `string`):

Forced to newest first whenever monitoring is on, so every run compares the same ordering. Collecting more than 960 listings per search also needs newest first.

## `maxListingsPerSearch` (type: `integer`):

Vinted shows 96 listings per page and stops any single search at 960. Above 960 (up to 2,000) this actor splits the search into price bands and merges them newest first. You pay per listing returned, so this is also your budget control.

## `minPrice` (type: `integer`):

Optional. Sent to Vinted as its own price filter, on the item price before the buyer protection fee. On a multi-country run it applies in each site's currency (euros on vinted.fr, zloty on vinted.pl).

## `maxPrice` (type: `integer`):

Optional. Sent to Vinted as its own price filter, on the item price before the buyer protection fee. Leave empty for no maximum.

## `sellerType` (type: `string`):

Keep only private or only business sellers. Applied while paging, so the actor keeps reading pages (up to Vinted's 960) until it has enough matching listings.

## `deltaMode` (type: `boolean`):

Turns a search into a watchlist. A listing is new when its Vinted listing ID was not returned by a previous run of the same watchlist, and a price drop when its price fell by at least the percentage below since it was last seen. Turn on "Alert on gone listings" below to also get listings that disappeared (a sold signal). Every row then carries the watchlist's sell-through rate and the search's median euro price. Unchanged listings are removed before you are billed, so a quiet run only costs the start fee and returns one summary row. Schedule it hourly and send the results to Discord, Slack, Telegram, Google Sheets or n8n.

## `deltaName` (type: `string`):

Leave empty and one is derived from this run's price and seller filters, so two schedules with different filters keep separate memories. Type your own name to keep one memory across a filter change. Naming a watchlist with the checkbox above off returns every listing but still marks each one new / price-drop / seen.

## `alertOnNew` (type: `boolean`):

With monitoring on, return listings this watchlist has never seen before.

## `alertOnPriceDrop` (type: `boolean`):

With monitoring on, return listings already seen whose price has dropped since the last sighting. The drop is measured on the item price, not the total with the fee.

## `alertOnGone` (type: `boolean`):

Off by default. With monitoring on, also return listings this watchlist saw on its previous run that have since disappeared from the part of the search the run covered: delisted, most often sold, sometimes withdrawn. Vinted never confirms a sale. gonePresumedSold is true when the last price was at or under the search's median and the listing was seen on at least 2 runs in a row. Billed as ordinary listing rows, no extra request.

## `minPriceDropPct` (type: `integer`):

A listing must be at least this much cheaper than when it was last seen to count as a price drop.

## `skipFirstRun` (type: `boolean`):

The first run of a watchlist has nothing to compare with. Turn this on to remember everything it finds without returning or billing it, so a webhook does not get a hundred messages on day one. Alerts start from the second run.

## `includeDetails` (type: `boolean`):

Off by default. When on, the actor opens each returned listing's page for its description (emails and phone numbers removed), colour, upload time, reserved flag and the seller's rating and review count. Billed as a separate listing-details charge, only for listings whose details arrived. With monitoring on, only new and price-drop listings are opened.

## `includeAllPhotos` (type: `boolean`):

Off by default: you get the main photo only, because Vinted photos often show the seller wearing the item. When on, imageUrls lists the photo links; all photos need listing details turned on (search results carry only the main photo). Links only, nothing is downloaded.

## `includeSellerInfo` (type: `boolean`):

Off by default. Every row already carries an anonymous seller hash, so you can group listings by seller without knowing who they are. Turning this on adds Vinted's own numeric seller ID (sellerId); you then need a lawful reason to process it. No names are ever returned.

## `columns` (type: `array`):

Choose which pieces of information to include in each result row. All are included by default.

## `expandRows` (type: `boolean`):

When on, each listing found gets its own row instead of being grouped under its search. You're still only charged once per search, no matter how many rows it produces.

## `residentialFallback` (type: `boolean`):

On by default. Vinted sometimes refuses Apify's shared datacenter connections on a country site (seen on vinted.de). The actor first retries on fresh datacenter connections, then on a residential one, for that page only. It costs you nothing extra: you still pay per listing returned. Turn it off to never use residential connections.

## `maxConcurrency` (type: `integer`):

Parallel requests. Keep conservative — this target has no browser fallback, so getting blocked costs more than slow-and-steady.

## `proxyConfiguration` (type: `object`):

Apify Proxy config. Residential recommended for anti-bot-sensitive targets.

## Actor input object example

```json
{
  "searches": [
    "carhartt"
  ],
  "countries": [
    "fr"
  ],
  "testRun": false,
  "onlyFound": false,
  "includeKeywords": [],
  "excludeKeywords": [],
  "sort": "newest",
  "maxListingsPerSearch": 96,
  "sellerType": "any",
  "deltaMode": false,
  "deltaName": "",
  "alertOnNew": true,
  "alertOnPriceDrop": true,
  "alertOnGone": false,
  "minPriceDropPct": 5,
  "skipFirstRun": false,
  "includeDetails": false,
  "includeAllPhotos": false,
  "includeSellerInfo": false,
  "columns": [
    "searchQuery",
    "searchCountries",
    "listingCount",
    "totalAvailable",
    "truncated",
    "newCount",
    "priceDropCount",
    "monitorStatus",
    "goneCount",
    "sellThroughRate",
    "medianPriceEur",
    "listings",
    "listingId",
    "title",
    "price",
    "totalPrice",
    "currency",
    "priceEur",
    "countryMedianPriceEur",
    "priceGapPct",
    "brand",
    "size",
    "condition",
    "country",
    "favouriteCount",
    "isPromoted",
    "sellerType",
    "sellerHash",
    "imageUrl",
    "url",
    "isNew",
    "changeType",
    "firstSeenAt",
    "lastSeenAt",
    "daysListed",
    "description",
    "color",
    "postedText",
    "postedAt",
    "sellerRating",
    "sellerReviewCount",
    "isReserved"
  ],
  "expandRows": true,
  "residentialFallback": true,
  "maxConcurrency": 3,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `results` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "searches": [
        "carhartt"
    ],
    "countries": [
        "fr"
    ],
    "includeKeywords": [],
    "excludeKeywords": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("accountable_eel/vinted-listing-lookup").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "searches": ["carhartt"],
    "countries": ["fr"],
    "includeKeywords": [],
    "excludeKeywords": [],
}

# Run the Actor and wait for it to finish
run = client.actor("accountable_eel/vinted-listing-lookup").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "searches": [
    "carhartt"
  ],
  "countries": [
    "fr"
  ],
  "includeKeywords": [],
  "excludeKeywords": []
}' |
apify call accountable_eel/vinted-listing-lookup --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,accountable_eel/vinted-listing-lookup"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/r3jbg16BDXj06m3dZ/builds/JzMWmLzBhsLhiweIz/openapi.json
