# Vinted Scraper - New Listings Monitor & Price Drop Alerts (`neverempty/vinted-new-listings-monitor`) Actor

For resellers, deal alerts and n8n or Telegram bots: Vinted listings from 26 countries by keyword, brand, size, condition and price, with title, price plus buyer fee, photo and seller type. Monitor mode returns only new listings and price drops, never old ones bumped to the top. JSON, CSV or Excel.

- **URL**: https://apify.com/neverempty/vinted-new-listings-monitor.md
- **Developed by:** [NeverEmpty](https://apify.com/neverempty) (community)
- **Categories:** E-commerce, Automation, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.45 / 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 Listings Monitor & Price Drop Alerts

Get **Vinted listings** as clean JSON from **26 Vinted countries** (vinted.fr, vinted.de, vinted.co.uk, vinted.com, vinted.it, vinted.es, vinted.nl, vinted.pl and more): title, **brand, size, condition**, price and currency, **buyer protection fee and total price**, photo, favourites, Boosted flag, seller ID and whether the seller is a business, and the link. Search by **search term**, **country**, **category ID**, **brand ID**, **size ID**, **condition** and **price range**, or paste Vinted search URLs. Turn on **Monitor mode** and scheduled runs return **only new listings and price drops** since the last check - for resellers, deal alerts, n8n / Make / Zapier flows and Telegram or Discord bots that should not pay for the same listings again.

- **A new listings monitor that does not sell old listings as new.** Vinted's "newest first" order puts a listing back at the top when it is bumped (a paid Boost), relisted, or published late by Vinted's review, so old listings keep appearing above the new ones. Vinted item IDs grow with every new listing across all countries, so this Actor can tell them apart: a listing that sits above listings created after it is marked `isResurfaced: true`. In monitor mode only listings that are really new since the last check come back as `changeType: new`; older listings that moved back to the top, or moved up from further down the list, are not sold as new (you can still ask for them as `changeType: resurfaced`).
- **Price drops.** A listing this watch has seen before whose price is now lower comes back as `changeType: price-drop` with `previousPrice` and `priceDrop`.
- **Honest search matching.** Vinted matches search terms loosely: a production search for the nonsense term `qqzxwvkjh plokm` returned 58 listings on 2026-09-25, none containing either word. Every row has `matchesSearchWords`, and `requireAllSearchWords` leaves the non-matching ones out (and does not charge for them).
- **Unknown IDs are not "no results".** Vinted answers a brand, size or condition ID it does not know with an empty list. This Actor checks the filter names Vinted sends back and returns a free `not-found` row naming the unknown ID instead of pretending the search is empty.
- **Free rows when there is nothing to deliver.** A search with no results, an unknown ID, a page that could not be read, and a check page come back as free rows that say why, and such runs are not charged at all. A monitor-mode run that read the list and found nothing new pays only the run start fee for that check (no listing rows).
- **26 countries in one run.** Search terms times countries, plus search URLs, up to 10 searches per run. Prices, the price filter and the currency follow each site (EUR, GBP on vinted.co.uk, USD on vinted.com, PLN on vinted.pl, CZK, SEK, DKK, RON, HUF ...).
- **Fast.** One Vinted page is 96 listings. Production runs on 2026-09-25 took about 3-4 s per search (one page), 512 MB.

Unofficial. Reads the public Vinted search result pages (`/catalog`), the same pages a person sees without logging in, and follows each site's robots.txt (checked on every run). It does not log in, does not open item or member pages, does not use Vinted's internal API, and does not solve or bypass check pages. It returns no seller names, photos or locations: only the numeric seller ID Vinted shows on the listing and whether the seller is a business. E-mail addresses and phone numbers written into a title are replaced with `[email removed]` / `[phone removed]`. Vinted's robots.txt asks that its content is not used to train AI models; please respect that.

### What you get

One row per listing. Example (a production monitor-mode run on 2026-09-25, search term `nike` on vinted.fr):

```json
{
  "status": "ok",
  "changeType": "new",
  "itemId": "10123641799",
  "title": "Nike Dunk High White/Red (Picante Red)| Taglia 46",
  "brand": "Nike",
  "size": "46",
  "condition": "Très bon état",
  "price": 54.49,
  "currency": "EUR",
  "buyerProtectionFee": 3.42,
  "totalPrice": 57.91,
  "previousPrice": null,
  "priceDrop": null,
  "isResurfaced": false,
  "matchesSearchWords": true,
  "isBoosted": false,
  "favouriteCount": 0,
  "photoUrl": "https://images1.vinted.net/t/02_00481_Cf4dfL7kDR6PBAwbwcEePBBZ/f800/80f0e616.webp?s=96c7bf1d51f4f6b4a0978866a6604c016df66a32",
  "thumbnailUrl": "https://images1.vinted.net/t/02_00481_Cf4dfL7kDR6PBAwbwcEePBBZ/310x430/80f0e616.webp?s=dff9c29eee3602e2e27b7588a95d6dcbcf2bfb49",
  "photoCount": 1,
  "sellerId": "107109061",
  "sellerIsBusiness": false,
  "url": "https://www.vinted.fr/items/10123641799-nike-dunk-high-whitered-picante-red-taglia-46",
  "country": "fr",
  "positionInSearch": 4,
  "listingsInSearch": 960,
  "listingsInSearchCapped": true,
  "searchQuery": "nike",
  "searchFilters": null,
  "searchUrl": "https://www.vinted.fr/catalog?search_text=nike&order=newest_first",
  "watchName": "t2",
  "checkedAt": "2026-09-24T20:30:33.337Z"
}
```

| Field | What it is |
|---|---|
| `status` | `ok` for a listing. Free rows: `no-results`, `none-matching`, `not-found`, `no-new-listings`, `blocked`, `unreadable`, `incomplete`, `not-allowed-by-robots-txt`, `bad-input`, `budget-reached`, `more-not-returned`, `more-new-than-readable` (with `note` saying why). |
| `changeType` | Monitor mode only: `first-check`, `new`, `price-drop` or `resurfaced`. `null` in a normal search. |
| `itemId`, `title`, `url` | Vinted item ID, title as the seller wrote it, link to the listing. |
| `brand`, `size`, `condition` | As Vinted shows them on the listing, in the site's language. `null` when the listing has none. |
| `price`, `currency` | Item price without the buyer protection fee, in the site's currency. |
| `buyerProtectionFee`, `totalPrice` | Vinted's buyer protection fee and the total a buyer pays before postage. |
| `previousPrice`, `priceDrop` | Monitor mode price drops: the price at the previous check and the difference. |
| `isResurfaced` | `true` when the listing is shown above listings created after it (older listing moved back to the top: bump, relisting or delayed publication). |
| `matchesSearchWords` | `true` when every search word is in the title or brand, `false` when Vinted matched it loosely, `null` without a search term. |
| `isBoosted` | Vinted marks the listing as promoted. |
| `favouriteCount`, `photoUrl`, `thumbnailUrl`, `photoCount` | Favourites, main photo (800 px), thumbnail, number of photos on the search card. |
| `sellerId`, `sellerIsBusiness` | Numeric seller ID shown on the listing and whether the seller is a business (Vinted Pro). No names. |
| `country`, `positionInSearch`, `listingsInSearch`, `listingsInSearchCapped` | Site, position in the newest-first list, the number of listings Vinted reports for the search (Vinted stops counting at 960). |
| `searchQuery`, `searchFilters`, `searchUrl` | The search that found it. `searchFilters` lists the brand, size and condition names Vinted confirmed for your IDs. |
| `watchName`, `checkedAt`, `note` | Monitor name, time of the check, and the reason on free rows. |

### Input

| Field | Type | What it does |
|---|---|---|
| `searchQueries` | list | What you would type into the Vinted search box, for example `nike air max`, `levis 501`, `lego star wars`. Each term is searched in each country. All empty (and no IDs or URLs) = example search `nike` on vinted.fr. |
| `countries` | list | `fr` `de` `uk` `us` `it` `es` `nl` `be` `at` `pl` `pt` `cz` `sk` `lt` `lv` `ee` `se` `dk` `fi` `ie` `lu` `ro` `hu` `hr` `si` `gr`. Empty = `fr`. |
| `categoryIds` | list | Vinted category numbers (the digits after `catalog[]=` in a Vinted search address). A category alone lists everything new in it. |
| `brandIds` | list | Vinted brand numbers (after `brand_ids[]=`, for example 53 = Nike). |
| `sizeIds` | list | Vinted size numbers (after `size_ids[]=`). Sizes are numbered per category: choose the size once in your browser and copy the number. |
| `conditions` | list | `new_with_tags`, `new_without_tags`, `very_good`, `good`, `satisfactory`. Empty = all. |
| `minPrice`, `maxPrice` | number | Price range in each site's currency (Vinted's own price filter, price without the buyer protection fee). |
| `searchUrls` | list | Vinted search pages copied from your browser. Sort order is always set to newest first and the page number is dropped. |
| `maxResultsPerSearch` | integer | Most listings per search, 1 to 960 (Vinted shows at most 960 for any search). Empty = 50. In monitor mode it limits the new listings and price drops per search in one run; the rest come in the next run while they are still on the pages the watch reads (on very busy searches, set it higher or narrow the search). |
| `requireAllSearchWords` | boolean | Leave out listings whose title and brand do not contain every search word. Default off. |
| `onlyNew` | boolean | Monitor mode (see below). Default off. |
| `includePriceDrops` | boolean | Monitor mode: also return price drops. Default on. |
| `includeResurfaced` | boolean | Monitor mode: also return older listings moved back to the top, as `resurfaced`. Default off. |
| `watchName` | string | Separate memories per watch (letters, digits, `.` `-` `_`, up to 40). |
| `resetMonitoringState` | boolean | Forget what this watch has seen for these searches and start again. |

### Monitor mode

1. The first run (`changeType: first-check`) returns up to `maxResultsPerSearch` listings and remembers everything it read as the starting point.
2. Every later run reads the newest page (96 listings) of each search. When a lot changed and none of the remembered listings is on that page, or when new listings from the previous run are still owed to you, it reads up to 3 pages.
3. It returns listings that are new since the previous check (`new`) and remembered listings whose price went down (`price-drop`). A listing it has not seen that sits below listings it already knew, or that is older than listings shown after it, is not called new.
4. When more new listings appeared than fit on 3 pages, a free `more-new-than-readable` row says some may have been missed: run the watch more often or narrow the search.
5. New listings or price drops that did not fit into `maxResultsPerSearch` or your maximum charge are not charged and come in the next run, as long as they are still on the pages the watch reads. On a very busy search (vinted.fr `nike` gets about 30-40 new listings a minute) set `maxResultsPerSearch` high enough or narrow the search.

Production check on 2026-09-25: a watch on `nike` at vinted.fr, run 50 seconds after its first check, returned 34 new listings (positions 1 to 35 of the newest page). One of them had an item ID about 12 minutes older than the others: a listing Vinted published late, which is still returned as new because it had never been visible before.

### Countries tested

Every site below was read from the Apify platform in production on 2026-09-25 without a proxy (26 of 26 searches returned listings). When Vinted refuses a request without a check page (a plain 403, 429 or a timeout), the Actor asks once more, then through a residential IP address in the same country (at most 3 pages per run). When Vinted shows a check page, the Actor stops and says so.

France, Germany, United Kingdom, United States, Italy, Spain, Netherlands, Belgium, Austria, Poland, Portugal, Czechia, Slovakia, Lithuania, Latvia, Estonia, Sweden, Denmark, Finland, Ireland, Luxembourg, Romania, Hungary, Croatia, Slovenia, Greece.

### Pricing

Pay per event:

- **Run start**: once per run that returns listings, and once per monitor-mode run that read and compared the list, even when nothing changed or nothing matched your words (it pays for the check). Not charged when Vinted refuses the request or an ID is unknown, and not charged in a normal search where nothing matches.
- **Listing returned**: per listing row delivered. Free rows (nothing new, not found, blocked, and so on) are never charged.

If your maximum total charge has no room for the run start fee plus one listing, the run requests nothing and is charged nothing. When a run reaches its maximum charge, the listings it could not deliver are not charged; in monitor mode they come in the next run while they are still on the pages the watch reads.

### Limits

- Vinted shows at most 960 listings for any search (10 pages of 96). Narrow the search with a brand, size, category or price range to reach older listings.
- Monitor mode sees price drops on the pages it reads (the newest 96 to 288 listings of the search).
- Brand, size and condition are the words shown on the listing card in the site's language.
- Two schedules running the same search with the same `watchName` at the same moment can return the same new listing twice; give each schedule its own `watchName`.

### Support

Found a search that does not work, or need another field? Open an issue on the Issues tab of this Actor with the input you used, and it will be looked at.

# Actor input Schema

## `searchQueries` (type: `array`):

What you would type into the Vinted search box, for example nike air max, levis 501, lego star wars, stone island. Each search term is searched in each country. Leave Search terms, Category IDs, Brand IDs and Search URLs all empty to run the example search nike on vinted.fr.

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

Which Vinted sites to search. Empty = France (vinted.fr). Prices, the price filter and the currency follow each site (EUR, GBP on vinted.co.uk, USD on vinted.com, PLN on vinted.pl, and so on).

## `categoryIds` (type: `array`):

Optional. Vinted category numbers, the digits after catalog\[]= in a Vinted search address (for example 1231 for men's shoes on vinted.fr). A category alone, without a search term, lists everything new in it.

## `brandIds` (type: `array`):

Optional. Vinted brand numbers, the digits after brand\_ids\[]= in a Vinted search address (for example 53 for Nike). A number Vinted does not know comes back as a free not-found row, not as an empty result.

## `sizeIds` (type: `array`):

Optional. Vinted size numbers, the digits after size\_ids\[]= in a Vinted search address (sizes are numbered per category, so choose the size once in your browser and copy the number).

## `conditions` (type: `array`):

Optional. Only listings in these conditions. Empty = all conditions.

## `minPrice` (type: `number`):

Optional. Only listings at or above this price, in the currency of each Vinted site. Vinted's own price filter is used (the price without the buyer protection fee).

## `maxPrice` (type: `number`):

Optional. Only listings at or below this price, in the currency of each Vinted site (the price without the buyer protection fee).

## `searchUrls` (type: `array`):

Optional. Vinted search pages copied from your browser after choosing filters there, for example https://www.vinted.de/catalog?search\_text=lego\&brand\_ids\[]=89162. The sort order is always set to newest first and the page number is dropped. Countries, category, brand, size, condition and price inputs above do not change these URLs.

## `maxResultsPerSearch` (type: `integer`):

Most listings returned for one search (96 per Vinted page). Vinted shows at most 960 listings for any search, so 960 is the maximum. Empty = 50. In monitor mode it limits the new listings and price drops returned per search in one run; the rest come in the next run while they are still on the pages the watch reads.

## `requireAllSearchWords` (type: `boolean`):

Off (default): return what Vinted returns. Vinted matches search terms loosely (a nonsense term still returns listings), so every row has matchesSearchWords. On: listings whose title and brand do not contain every search word are left out and not charged.

## `onlyNew` (type: `boolean`):

On: every run reads the newest page of each search (up to 3 pages when a lot changed) and returns only listings that are new since the previous check (changeType new) and listings whose price went down (changeType price-drop, with previousPrice). Older listings that Vinted moved back to the top, or that moved up from further down, are not sold as new. The first run returns up to Max listings per search (changeType first-check) and remembers the list as the starting point. Each run that reads the list is charged the run start fee even when nothing changed; runs where nothing changed return a free row saying so.

## `includePriceDrops` (type: `boolean`):

On (default): in monitor mode, also return listings on the pages read whose price is lower than at the previous check. Off: only new listings.

## `includeResurfaced` (type: `boolean`):

Off (default): an older listing that shows up above newer listings (a paid bump, a relisting, or a publication Vinted delayed) and that this watch had not seen is not returned. On: return it with changeType resurfaced. It is never called new.

## `watchName` (type: `string`):

Optional. Keeps separate memories for monitor mode, for example one per client (letters, digits, dot, dash, underscore; up to 40). Runs with the same watch name and the same search share what has already been returned.

## `resetMonitoringState` (type: `boolean`):

On: forget what this watch has seen for these searches before running, so this run is a first check again.

## Actor input object example

```json
{
  "searchQueries": [
    "levis 501"
  ],
  "countries": [
    "fr"
  ],
  "includePriceDrops": true,
  "includeResurfaced": false
}
```

# Actor output Schema

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

One row per Vinted listing: item ID, title, brand, size, condition, price and currency, buyer protection fee and total, photo, favourites, Boosted flag, seller ID and business flag, link, country and its position in the search. Monitor mode marks rows first-check, new, price-drop (with the previous price) or resurfaced. Every row says whether the listing is an older one Vinted moved back to the top (isResurfaced) and whether every search word is in its title or brand. A search with no listings, nothing new, an unknown brand or size ID, a refused request or a run that hit its maximum charge comes back as a free row that says why.

# 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 = {
    "searchQueries": [
        "levis 501"
    ],
    "countries": [
        "fr"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("neverempty/vinted-new-listings-monitor").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 = {
    "searchQueries": ["levis 501"],
    "countries": ["fr"],
}

# Run the Actor and wait for it to finish
run = client.actor("neverempty/vinted-new-listings-monitor").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 '{
  "searchQueries": [
    "levis 501"
  ],
  "countries": [
    "fr"
  ]
}' |
apify call neverempty/vinted-new-listings-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,neverempty/vinted-new-listings-monitor"
        }
    }
}
```

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/BPf94QumR61aBrcqK/builds/EspoE12F9GamMha99/openapi.json
