# Vinted Scraper - Listings, Prices, Sellers & Reviews (`abotapi/vinted-marketplace-scraper`) Actor

Scrape Vinted across 27 country marketplaces by keyword, filters, item or seller URL. Extract prices, currencies, brands, sizes, conditions, views, favourites, seller ratings and profiles. Supports brand, price and condition filters, plus full seller feedback history.

- **URL**: https://apify.com/abotapi/vinted-marketplace-scraper.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Features:** Limited permissions
- **Stats:** 7 total users, 4 monthly users, 94.9% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.20 / 1,000 results

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: Listings, Sellers & Reviews Across 27 Markets

Vinted Scraper turns Vinted, Europe's C2C secondhand fashion marketplace, into structured listing, seller and review data across all 27 of its country marketplaces. Search by keyword with brand, price and condition filters, paste item, seller or catalogue links directly, or pull a seller's complete feedback history. Every listing carries price, currency, brand, size, condition, favourite count and photos, with full description and seller-profile depth available on request. Export to JSON, CSV or Excel, or read results straight through the API.

### Why This Scraper?

- **Three ways in.** Keyword search with source-side brand, price and condition filters; paste any item, seller/member or catalogue link; or fetch a seller's full feedback history.
- **27 country marketplaces, one actor.** Each market has its own catalogue, currency and language, and item and seller ids resolve across all of them.
- **Depth on demand.** Switch on full item description plus seller reputation, response time and bundle discount per run, and pay the enrichment charge only when you use it.
- **Mixed-market URL runs.** Paste links from several countries in one URL-mode run; each link keeps its own market instead of falling back to a single country.
- **Built for schedules.** Incremental mode returns only new, changed and reappeared listings on recurring runs, and, with **Emit expired** on, a sold or removed listing comes back marked EXPIRED once a full scan confirms it is gone.
- **Loud failures, not silent empties.** A run that could not read the marketplace fails with a clear message instead of returning an empty dataset that looks like "no results".
- **Resume without re-billing.** Continue one interrupted run from its own previous dataset without re-collecting or re-charging what it already delivered.

### Use Cases

- **Resale price research:** track a keyword or brand across a market to benchmark asking prices, sizes and condition mix.
- **Deal and restock alerts:** schedule a search with incremental mode on and get notified only about new or changed listings.
- **Seller due diligence:** pull a seller's profile and complete feedback history before buying from or partnering with them.
- **Marketplace analytics:** build a dataset of brand, price and condition trends across one or several country marketplaces.
- **Closet monitoring:** watch one seller's whole listing catalogue for new items through URL mode.

### Data You Get

> Sample shape: values are illustrative placeholders, not from a live record.

| Field | Example |
| --- | --- |
| `itemId` | `"0000000000"` |
| `title` | `"Sample knitted winter sweater"` |
| `brandTitle` | `"Sample Brand"` |
| `priceAmount`, `serviceFee`, `totalPrice` | `12.5`, `1.2`, `13.7` (price, buyer fee, all-in price shown on the card) |
| `currency`, `condition` | `"EUR"`, `"Very good"` |
| `brandId`, `categoryId`, `sizeId`, `statusId`, `categoryBreadcrumbs`, `colors`, `isReserved`, `isHidden` | Ids behind brand, category, size and condition, the category path and colours as lists, and the reserved/hidden flags (item detail, when the page provides them) |
| `conditionSchema` | `"UsedCondition"` (item detail, when the page provides it) |
| `size` | `"M"` |
| `color` | `"Green"` (item detail) |
| `description` | full listing text (item detail, when **Fetch item detail** is on) |
| `uploadDate` | `"12 h"` (item detail) |
| `favouriteCount` | `18` |
| `viewCount` | `null` (the site no longer publishes view counts, so this is reported as unknown rather than a fake `0`) |
| `promoted` | `false` |
| `image` | main photo URL |
| `photos`, `photoCount` | photo list with id, url, `fullSizeUrl`, `dominantColor`, width and height, and the number of photos |
| `url` | link to the listing |
| `sellerId`, `sellerUrl` | `"0000000001"` (with item detail or seller profiles on; otherwise in `seller.id`) and the seller's member page link |
| `seller.username`, `seller.business` | `"sample_seller"`, `false` |
| `seller.feedbackReputation`, `seller.feedbackCount` | `4.9`, `312` (seller profile, when enabled) |
| `seller.city` | `"Sample City"` (seller profile, when enabled) |
| `seller.bundleDiscount` | `0.1`, a fraction, present only when the seller has the bundle discount switched on (seller profile, when enabled) |
| `seller.lastLogedOnTs`, `seller.soldItemCount`, `seller.boughtItemCount`, `seller.followingBrandsCount`, `seller.isOnHoliday`, `seller.isEmailVerified`, `seller.isFacebookVerified`, `seller.isGoogleVerified` | `1790000000` (last login, epoch seconds), `122` sold, `57` bought, `0` followed brands, `false`, `true`, `false`, `false` (seller profile, when enabled) |
| `sellerBadges` | `["ACTIVE_LISTER"]`, the seller badges shown on the item page (item detail, when enabled; absent when the page shows none) |
| `changeType` | `"NEW"` (also `"UPDATED"`, `"UNCHANGED"`, `"REAPPEARED"`, `"EXPIRED"`; incremental mode only) |

Reviews mode returns a different row shape: one entry per feedback with `reviewId`, `rating`, `body`, `createdAt`, `reviewerUsername`, plus the seller's own `sellerFeedbackRating` and `sellerFeedbackCount` repeated on every row.

### How to Use

1. Pick a **Mode**: `search` (keyword), `url` (paste links) or `reviews` (a seller's feedback).
2. Search mode: type **Search keywords**, and optionally narrow with Category ID, Brand ID, Condition or price. URL mode: paste item, seller/member, catalogue, category or brand links. URL mode with the URLs field left untouched now works through one example of each supported URL form (the three item-page forms resolve to one listing, returned once) (item, member, catalogue search with page, sort and filters, category page, brand page, on several markets); clear the list and paste your own links to scrape only those. Links the site does not serve (the m. mobile host, member /items and /feedback sub-pages) are skipped with a warning in the log. Category ID and Brand ID accept either the numeric id or the name as typed on the site; a name with no match stops the run with a clear message. Reviews mode: paste seller ids or member links.
3. Pick the **Country marketplace**, and switch on **Fetch item detail** or **Fetch seller profiles** for extra depth.
4. Set **Max items** to control run size and cost, then click **Start**.

**Search a keyword with filters:**

```json
{
  "mode": "search",
  "queries": ["nike air max"],
  "conditions": ["2"],
  "minPrice": 10,
  "maxPrice": 60,
  "country": "de",
  "maxItems": 50
}
```

**Paste links (item and seller, mixed markets):**

```json
{
  "mode": "url",
  "urls": [
    "https://www.vinted.fr/items/0000000000-sample-knitted-winter-sweater",
    "https://www.vinted.de/member/0000000001"
  ],
  "fetchDetails": true,
  "fetchSellerProfiles": true
}
```

**A seller's full feedback history:**

```json
{
  "mode": "reviews",
  "sellers": ["0000000001"],
  "country": "de"
}
```

**Recurring monitoring of a search, with expiry tracking on:**

```json
{
  "mode": "search",
  "queries": ["vintage denim jacket"],
  "country": "fr",
  "incrementalMode": true,
  "emitExpired": true
}
```

#### Run it from your code

Python:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("abotapi/vinted-marketplace-scraper").call(
    run_input={"mode": "search", "queries": ["nike air max"], "country": "de"})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["title"], item["priceAmount"], item["currency"])
```

JavaScript:

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

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('abotapi/vinted-marketplace-scraper').call({
  mode: 'search', queries: ['nike air max'], country: 'de',
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the **Integrations** tab.

#### Supported URL shapes (url mode)

Each link keeps its own market from its hostname (vinted.de, .co.uk, .com and the others, with or without www).

| Shape | Example path |
| --- | --- |
| Catalogue search | `/catalog?search_text=nike` |
| Catalogue search, page | `/catalog?search_text=nike&page=2` |
| Catalogue search, sort | `/catalog?search_text=nike&order=newest_first` |
| Catalogue search with filters | `/catalog?search_text=nike&catalog[]=1242&brand_ids[]=53&price_to=50&status_ids[]=6` |
| Brand filter only | `/catalog?brand_ids[]=53` |
| Category page | `/catalog/1242-sneakers` |
| Category page, page and sort | `/catalog/1242-sneakers?page=2&order=newest_first` |
| Category page with brand filter | `/catalog/1242-sneakers?brand_ids[]=53` |
| Brand page | `/brand/53-nike` |
| Item | `/items/10566802080-nike-sneaker` |
| Item, id only | `/items/10566802080` |
| Item on another market | `vinted.co.uk/items/...` |
| Member | `/member/3162455054` |
| Member with slug | `/member/3162455054-vrna21` |
| Member, page | `/member/3162455054?page=2` |
| Other market catalogue | `vinted.co.uk/catalog?search_text=nike` |
| US market catalogue | `vinted.com/catalog?search_text=nike` |
| Host without www | `vinted.de/catalog?search_text=nike` |

The URLs field is pre-filled with one example of each of these 18 URL forms, so a run started without editing it works through all of them (a non-empty default). The three item-page examples (id-slug, numeric id only, other market) point to the same listing, so that listing is returned once, not three times. Clear the list and paste your own links to scrape only those. The m. mobile host and the `/member/<id>/items` and `/member/<id>/feedback` sub-pages are not served by the site and are skipped with a warning.

#### Filters and sorts (search mode)

Filters: Category ID and Brand ID (numeric id or the name as typed on the site), Condition (New with tags, New without tags, Very good, Good, Satisfactory), minimum and maximum price. Sort: Relevance, Newest first, Price low to high or Price high to low. Alternate input names are accepted: `query`, `priceMin`, `priceMax`, `catalogId`, `condition`, `startUrls`, `includeItemDetails`, `includeSellerDetails`.

#### Output views

The dataset has four views: Overview (listings), Reviews, Detail (item description and seller depth) and Changes (incremental classification and changed fields).

#### How the item budget is split across multiple targets

**Max items** is the run's total cap. When a run carries more than one target, meaning more than one search keyword, several pasted seller or catalogue links, or several sellers in reviews mode, that cap is divided evenly across them so a multi-target run returns a share from each instead of spending the whole budget on the first one. Pasted item links return one record each and count against the same total. **Max pages** additionally limits how many result pages each individual target may walk. In reviews mode, **Max reviews per seller** (default 20, up to 10000) additionally limits the feedback fetched for each seller.

#### A note on overlapping targets under a low cap

A result page holds 48 listings. When a target's share of **Max items** runs out part-way through a page (with the default Max items of 20 that happens on the very first page), the rest of that page's listings are recognised as already looked at for the remainder of the run, even though they were never returned. With a single target this is invisible, because nothing runs after it. It only matters when you combine **several overlapping listing targets in one run** (for example two keyword searches that both match the same dual-branded listing, or a pasted catalogue link and a seller link that share an item): a listing left over on the first target's cut-short page is then silently skipped by a later target too. Example: searches "nike" and "adidas" with Max items 10 give each a share of 5; "nike" returns its first 5 listings, and if listing number 7 on that page also appears in the "adidas" results, neither search returns it. Reviews mode is not affected, since each review belongs to exactly one seller. This never causes a false `EXPIRED` row (expiry detection only runs after every target reached its genuine natural end, which a cut-short target never does) and it does not affect **Resume** (which reads back the previous run's own dataset, not this in-run bookkeeping). Workaround: give overlapping searches or links a generous **Max items**, or run them separately, rather than combining them under a tight shared cap in a single run.

#### Resume and recurring updates

- **Resume** (`resumeFromRunId`) continues one interrupted run: paste its run or dataset id and the actor skips everything that run already collected, so you don't pay twice.
- **Incremental mode** (`incrementalMode`) is for scheduled runs over the same search, URLs or sellers. Each listing is classified `NEW`, `UPDATED` (with `changedFields`), `UNCHANGED` (suppressed and not billed unless `emitUnchanged` is on), `REAPPEARED`, or `EXPIRED`, meaning sold or removed, only after a run that scanned the whole tracked scope and only with `emitExpired` on. `stateKey` names or shares the stored state; leaving it empty derives one automatically from the mode, country and target settings. `ignoreFieldsForChanges` lets you exclude extra fields from change detection (a dotted name such as `seller.city` or `photos.width` ignores that key inside the nested value); view and favourite counters, the relative upload label, signed photo and avatar links and live seller counters (last seen online, followers, item count, sold and bought counts, followed brands) are already excluded by default, stay in the output, and never cause a false `UPDATED`. With incremental mode off, output is exactly as before.

### Send results into your apps (MCP connectors)

Optionally pipe the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. This is an extra delivery step **after** the scrape: the Apify dataset is never changed.

**What gets written to the connector:** a condensed, human-readable **summary** of each record, not the full JSON. Each item becomes one entry with a **title** and its key fields flattened to plain text. The **complete record always stays in the Apify dataset**.

1. Authorize a connector once under **Apify -> Settings -> Integrations** (Notion, Linear, Airtable, or Apify).
2. Select it in the **"Pipe results into your apps"** input field. (If the picker is empty, you haven't authorized a connector yet.)
3. For **Notion**, also set `notionParentPageUrl` to the page where items should be created.

The connection is mediated by Apify's MCP proxy, so this actor never sees your third-party credentials. Leave the field empty to skip.

### Input Parameters

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `mode` | string | `search` | `search`, `url`, or `reviews`. |
| `country` | string | `de` | Country marketplace to search, and the fallback market for URLs that don't name one. |
| `queries` | array | `["nike"]` | Search keywords (search mode). The budget splits evenly across them. |
| `categoryId` | string | (none) | A catalogue id. Narrows a search, or browses a whole category alone (search mode). |
| `brandId` | string | (none) | A brand id (search mode). |
| `conditions` | array | (none) | Restrict results to these conditions (search mode). |
| `minPrice` | integer | (none) | Only items priced at or above this amount (search mode). |
| `maxPrice` | integer | (none) | Only items priced at or below this amount (search mode). |
| `sortBy` | string | `relevance` | `relevance` or `newest_first` (search mode). |
| `urls` | array | one example per supported URL form | Item, seller/member, catalogue/search, category (`/catalog/<id>-<name>`) or brand (`/brand/<id>-<name>`) links (url mode). Each keeps its own market. |
| `maxReviews` | integer | `20` | Reviews mode: most reviews fetched per seller (1 to 10000); Max items still caps the whole run. |
| `sellers` | array | (none) | Seller numeric ids or member links to fetch feedback for (reviews mode). |
| `fetchDetails` | boolean | `false` | Add full description, colour, upload date and schema condition per listing. Billed as the detail-enrichment surcharge. |
| `fetchSellerProfiles` | boolean | `false` | Add seller depth (reputation, response time, bundle discount and more) per listing. Also billed as the detail-enrichment surcharge. |
| `maxPages` | integer | (none) | Stop each search/URL/seller after this many result pages. `0` or empty = as many as needed. |
| `maxItems` | integer | `20` | Hard cap on total records returned across the whole run. `0` = unlimited. |
| `proxy` | object | Apify residential proxy | Connection settings. |
| `mcpConnectors` | array | (none) | Optional: send a summary of each record to apps you authorized under Integrations. |
| `notionParentPageUrl` | string | (none) | Notion connector only: page under which records are created. |
| `maxNotifyListings` | integer | `50` | Cap on items written to each connector per run. |
| `resumeFromRunId` | string | (none) | Continue one interrupted run without re-collecting or re-charging its records. |
| `incrementalMode` | boolean | `false` | Return only new, changed, reappeared and expired records on scheduled runs. |
| `stateKey` | string | (none) | Name or share an incremental-mode state; auto-derived when left empty. |
| `ignoreFieldsForChanges` | array | `[]` | Incremental mode only. Extra field names (or dotted nested names such as `seller.city`) to leave out of change detection, on top of the built-in exclusions. |
| `emitUnchanged` | boolean | `false` | Also return (and bill) unchanged records. |
| `emitExpired` | boolean | `false` | Also return (and bill) records no longer found after a complete scan. |
| `query` | string | (none) | Alias of `queries`; used only when `queries` is empty. |
| `priceMin` | integer | (none) | Alias of `minPrice`; used only when `minPrice` is empty. |
| `priceMax` | integer | (none) | Alias of `maxPrice`; used only when `maxPrice` is empty. |
| `catalogId` | string | (none) | Alias of `categoryId`; used only when `categoryId` is empty. |
| `condition` | array | (none) | Alias of `conditions`; used only when `conditions` is empty. |
| `startUrls` | array | (none) | Alias of `urls`; used when `urls` is empty or still holds its prefilled example list. Any other `urls` value wins. |
| `includeItemDetails` | boolean | (none) | Alias of `fetchDetails`; `true` turns on Fetch item detail (billed as the detail-enrichment surcharge per returned listing). A `fetchDetails` that is already on stays on. |
| `includeSellerDetails` | boolean | (none) | Alias of `fetchSellerProfiles`; `true` turns on Fetch seller profiles (billed as the detail-enrichment surcharge per returned listing). A `fetchSellerProfiles` that is already on stays on. |

### Output Example

> Sample shape: values are illustrative placeholders, not from a live record.

```json
{
  "itemId": "0000000000",
  "title": "Sample knitted winter sweater",
  "url": "https://www.vinted.de/items/0000000000-sample-knitted-winter-sweater",
  "path": "/items/0000000000-sample-knitted-winter-sweater",
  "brandTitle": "Sample Brand",
  "priceAmount": 12.5,
  "currency": "EUR",
  "size": "M",
  "condition": "Very good",
  "conditionSchema": "UsedCondition",
  "color": "Green",
  "description": "Sample listing description text.",
  "uploadDate": "12 h",
  "favouriteCount": 18,
  "viewCount": null,
  "promoted": false,
  "serviceFee": 1.2,
  "totalPrice": 13.7,
  "photos": [{ "id": 1, "url": "https://images.example/sample.jpg", "fullSizeUrl": "https://images.example/sample-full.jpg", "dominantColor": "#a1b2c3", "width": 800, "height": 800 }],
  "photoCount": 1,
  "sellerUrl": "https://www.vinted.de/member/0000000001",
  "image": "https://images.example/sample.jpg",
  "sellerId": "0000000001",
  "seller": {
    "id": "0000000001",
    "username": "sample_seller",
    "business": false,
    "city": "Sample City",
    "feedbackReputation": 4.9,
    "feedbackCount": 312,
    "bundleDiscount": 0.1,
    "soldItemCount": 122,
    "boughtItemCount": 57,
    "isEmailVerified": true
  },
  "sellerBadges": ["ACTIVE_LISTER"],
  "scrapedAt": "2026-01-01T00:00:00+00:00"
}
```

### Plan Requirement

The default connection uses the Apify residential proxy pool pinned to the market's country, and it works out of the box on a paid Apify plan. The standard datacenter pool also served the marketplace in our checks, so you can select it in the proxy field for a cheaper run; residential stays the default because it is the more reliable choice. Apify free plans do not include residential proxies. When the selected pool is unavailable to your account, the run logs this warning and continues on a backup connection:

> The selected connection is unavailable to this run. Apify free plans do not include residential proxies. Continuing on a backup connection; some pages may still be unavailable.

When pages are then read over the backup, the log says: "Served over the backup connection (the default connection was unavailable)." If no page can be read at all, the run fails with one of three messages instead of returning an empty dataset: the proxy connection was refused for this account (your plan does not include proxy access); no results because residential proxies are unavailable and the backup connection could not load the pages; or "Could not load any results page (the requests failed)... a connection problem, not an empty search". You can also paste your own proxy URLs, which are used as given.

### FAQ

#### How much does it cost?

You pay per result row returned (a listing or a review, charged once per row delivered, with volume tiers by Apify plan), plus the detail-enrichment event (per listing, only when delivered) only on listings where you switched on item detail and/or seller profiles. The **Pricing** tab shows the current rates. Use **Max items** to cap the cost of any run.

#### Is it legal to scrape Vinted?

This actor collects only publicly available marketplace data. You are responsible for how you use it: follow Vinted's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution. Listing text, prices and seller ratings belong to their respective users.

#### Can I get only new or changed listings on a schedule?

Yes. Schedule the actor from the **Schedules** tab and turn on **Incremental mode**. Each run then returns only new, changed and reappeared records, and unchanged ones are not billed. With **Emit expired** on, sold or removed listings come back marked `EXPIRED` once a run has fully scanned the tracked scope.

#### Why does my dataset have fewer rows than I expected when I combine several searches, URLs or sellers in one run?

First, **Max items** is split evenly across every target in the run, so each keyword or link gets only its share. Second, when a target's share runs out part-way through a 48-listing result page, the rest of that page is treated as already looked at for the rest of the run. That is harmless for a single target, but a listing that also matches a later target in the same run is then skipped by that target too: for example, searches "nike" and "adidas" with Max items 10 return 5 each, and a listing sitting 7th on the "nike" page that also shows up under "adidas" is returned by neither. Give overlapping searches or links a generous **Max items**, or run them separately, to avoid it.

#### Why did my run fail instead of returning an empty dataset?

If the marketplace refuses every request, the run stops with a clear message so "no listings for my search" is never confused with "nothing could be read". Run it again in a few minutes. If only some pages were refused but real rows were delivered, you still get what was found, reported as partial results.

#### Can I use it with AI agents or MCP?

Yes. Call it from any Apify integration or MCP client, and use the connector field to push results into Notion, Linear or Airtable.

### 🔗 Want more Vinted data?

Pair this actor with these related scrapers from the same team:

<table>
<tr><td>🏷️ <a href="https://apify.com/abotapi/vinted-fr-scraper"><b>Vinted.fr</b></a><br>Scrape Vinted.fr, France's C2C second-hand fashion marketplace: search with...</td><td>🏷️ <a href="https://apify.com/abotapi/curtsy-app-scraper"><b>Curtsy Fashion Resale Scraper</b></a><br>Scrape Curtsy, the US secondhand fashion marketplace: listings with brand, size, list vs...</td></tr>
<tr><td>🏷️ <a href="https://apify.com/abotapi/poshmark-scraper"><b>Poshmark Scraper</b></a><br>Scrape Poshmark fashion-resale listings: keyword search, brand and category browse, sold...</td><td>🏷️ <a href="https://apify.com/abotapi/daangn-scraper"><b>Daangn Scraper</b></a><br>Scrape Daangn (당근) marketplace listings by keyword and region. Returns 35+ fields per...</td></tr>
<tr><td>🏷️ <a href="https://apify.com/abotapi/thredup-scraper"><b>ThredUp Scraper</b></a><br>Scrape ThredUp resale listings by keyword, department, brand, size, condition, price or...</td><td>🏷️ <a href="https://apify.com/abotapi/wildberries-marketplace-scraper"><b>Wildberries Scraper</b></a><br>Scrape Wildberries marketplace: product search with prices (wallet vs retail), discounts...</td></tr>
</table>

👉 [Browse all abotapi scrapers](https://apify.com/abotapi)

### 💬 Support

- 🐞 **Found a bug or a missing field?** Open a ticket on the [Issues tab](https://apify.com/abotapi/vinted-marketplace-scraper/issues/open). We usually reply within hours.
- 🛠️ **Need another site, extra fields or a private build?** Email <contact@abotapi.com>.
- ⭐ **Enjoying it?** A quick review on the actor page helps other users find it.

# Actor input Schema

## `mode` (type: `string`):

'search' finds listings by keyword with the marketplace's own filters. 'url' scrapes any item page or seller/member page you paste. 'reviews' pulls a seller's full feedback history (rating + every comment).

## `country` (type: `string`):

Which country's catalogue to search, and which market session to use when a pasted URL does not name one. Each country has its own catalogue, language and currency; item and seller ids work across all of them. Pasted URLs always pin their own market from the URL itself.

## `queries` (type: `array`):

One or more free-text keywords, e.g. 'nike air force' or 'robe vintage'. The item budget (Max items below) is split evenly across keywords.

## `categoryId` (type: `string`):

A category id (e.g. '1242') or a category NAME as shown on the site (e.g. 'Sneaker'); a name is looked up in the site's own category list and the run stops with a clear error when nothing matches. Narrows a keyword search, or browses the whole category when Search keywords is left empty. Search mode only.

## `brandId` (type: `string`):

A brand id (e.g. '53') or a brand NAME (e.g. 'Nike'); a name is looked up with the site's own brand search and the run stops with a clear error when nothing matches. Search mode only.

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

Only return items in these conditions. Leave empty for any condition. Search mode only.

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

Only return items priced at or above this amount, in the selected market's currency. Search mode only.

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

Only return items priced at or below this amount, in the selected market's currency. Search mode only.

## `sortBy` (type: `string`):

Order of the returned results: the marketplace's default relevance ranking, newest first, price low to high or price high to low. Search mode only.

## `urls` (type: `array`):

Paste item pages (/items/<id> or /items/<id>-<slug>), seller/member pages (/member/<id>, optional slug or ?page=), catalogue/search pages (/catalog?search_text=..., with filters, order and page), category pages (/catalog/<id>-<name>, the category id is read from the path) or brand pages (/brand/<id>-<name>). A member URL enumerates every item that seller has listed; a catalogue, category or brand URL walks that listing from the page parameter it carries. Each URL's own hostname picks its market (vinted.de, .co.uk, .com and the others), with or without www. Unsupported links (the m. mobile host, /member/<id>/items, /member/<id>/feedback) are skipped with a warning. Left untouched, this field works through one example of each supported URL form; the three item-page forms point to the same listing, which is returned once. Filter fields above are ignored in this mode.

## `sellers` (type: `array`):

Seller numeric member ids or full /member/<id> URLs (any country) to fetch feedback for. A pasted URL's own hostname picks the market; a bare numeric id uses the Country marketplace above.

## `fetchDetails` (type: `boolean`):

Adds the full description, colour, upload date and schema condition to each listing. Identity, price, brand, size, condition label, view/favourite counts and photos are already included without this toggle. Billed as the per-listing detail-enrichment surcharge when a row is returned.

## `fetchSellerProfiles` (type: `boolean`):

Adds seller depth to each listing: city, country, feedback reputation with positive/neutral/negative counts, item and follower counts, bundle discount, response time and online status. Also billed as the detail-enrichment surcharge (once per listing, together with Fetch item detail).

## `maxReviews` (type: `integer`):

Reviews mode only: the most feedback entries fetched for each seller. Max items below still caps the whole run.

## `maxPages` (type: `integer`):

Stop after this many result pages per keyword/category/URL entry. Leave empty for as many pages as needed (bounded by Max items below).

## `maxItems` (type: `integer`):

Hard cap on total records (listings or reviews) returned across the whole run. 0 = unlimited: the run keeps going until the source runs out of results.

## `proxy` (type: `object`):

Runs use the platform's residential proxy pool by default, pinned to the market's country. The standard datacenter pool also served the marketplace in our checks, so you can select it here for a cheaper run, but residential remains the default because it is the more reliable choice. If the selected pool is unavailable on your plan, the run continues on a backup connection. You can also paste your own proxy URLs, which are used as given.

## `resumeFromRunId` (type: `string`):

Paste a previous run ID or dataset ID to continue an interrupted crawl without returning or charging for records already collected there. For recurring daily monitoring of the same search/URLs/sellers, use Incremental mode below instead.

## `incrementalMode` (type: `boolean`):

Turn this on for daily or recurring monitoring. The first run returns everything as NEW. Later runs normally return only NEW, UPDATED, and REAPPEARED records; a sold/removed listing on this marketplace comes back as EXPIRED. Turn on "Emit unchanged" or "Emit expired" only when you also want those records returned (and billed). State is kept separately per mode, country and search/URL/seller/detail setup; use State key when you want to name or deliberately share a monitoring campaign. To continue one specific interrupted run instead, use Resume from a previous run above.

## `stateKey` (type: `string`):

Optional. Name this monitoring campaign to keep its state stable, or to deliberately share state across differently-configured runs. Leave empty to let the actor derive a key automatically from the mode, country, search/URL/seller and detail settings - different setups then never mix state with each other.

## `emitUnchanged` (type: `boolean`):

Off by default. Turn on to also return records that have not changed since the last run, marked UNCHANGED. This returns - and bills - extra rows you already have, so leave it off unless you specifically want the full snapshot every run.

## `emitExpired` (type: `boolean`):

Off by default. Turn on to also return records that were present in a previous run but are no longer found (sold or removed), marked EXPIRED. Only produced once a run has fully scanned the tracked scope - not when Max items capped it, when Resume was used, or when a run scanned nothing. This returns - and bills - extra synthetic rows, so leave it off unless you need expiry tracking.

## `ignoreFieldsForChanges` (type: `array`):

Optional. Field names to exclude from UPDATED change detection, in addition to the built-in handling (view and favourite counters, the relative upload label, signed photo and avatar links, and live seller counters such as last seen online, followers and item count already never trigger a false UPDATED). Use this if another field in your setup turns out to be noisy. A dotted name such as seller.city or photos.width ignores that key inside the nested object or inside every photo. The ignored fields still appear in the output; they are only left out of the comparison.

## `mcpConnectors` (type: `array`):

Optionally send the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. Authorize a connector once under Apify -> Settings -> Integrations, then select it here. The connector receives a condensed, human-readable summary per item (title + key fields), not the full JSON; the complete record stays in the dataset. Leave empty to skip. Supported: Notion (https://mcp.notion.com/mcp), Linear (https://mcp.linear.app/sse), Airtable (https://mcp.airtable.com/mcp), Apify (https://mcp.apify.com).

## `notionParentPageUrl` (type: `string`):

URL (or id) of the Notion page under which item pages are created. Required to enable the Notion export; ignored by other connectors.

## `maxNotifyListings` (type: `integer`):

Cap on items written to each connector per run. Does not affect the dataset.

## `query` (type: `string`):

Alias of Search keywords for a single keyword. Used only when Search keywords is empty.

## `priceMin` (type: `number`):

Alias of Minimum price. Used only when Minimum price is empty.

## `priceMax` (type: `number`):

Alias of Maximum price. Used only when Maximum price is empty.

## `catalogId` (type: `integer`):

Numeric alias of Category ID. Used only when Category ID is empty.

## `condition` (type: `array`):

Alias of Condition (status ids 6, 1, 2, 3, 4). Used only when Condition is empty.

## `startUrls` (type: `array`):

Alias of URLs to scrape, accepting request-list objects. Used when URLs to scrape is empty or still holds its prefilled example list; any other URLs to scrape value wins.

## `includeItemDetails` (type: `boolean`):

Alias of the item-detail toggle. Set to true it turns on Fetch item detail, which is billed as the detail-enrichment surcharge per returned listing. If Fetch item detail is already on, it stays on.

## `includeSellerDetails` (type: `boolean`):

Alias of the seller-profile toggle. Set to true it turns on Fetch seller profiles, which is billed as the detail-enrichment surcharge per returned listing. If Fetch seller profiles is already on, it stays on.

## Actor input object example

```json
{
  "mode": "search",
  "country": "de",
  "queries": [
    "nike"
  ],
  "sortBy": "relevance",
  "urls": [
    "https://www.vinted.de/catalog?search_text=nike",
    "https://www.vinted.de/catalog?search_text=nike&page=2",
    "https://www.vinted.de/catalog?search_text=nike&order=newest_first",
    "https://www.vinted.de/catalog?search_text=nike&catalog[]=1242&brand_ids[]=53&price_to=50&status_ids[]=6",
    "https://www.vinted.de/catalog?brand_ids[]=53",
    "https://www.vinted.de/catalog/1242-sneakers",
    "https://www.vinted.de/catalog/1242-sneakers?page=2&order=newest_first",
    "https://www.vinted.de/catalog/1242-sneakers?brand_ids[]=53",
    "https://www.vinted.de/brand/53-nike",
    "https://www.vinted.de/items/10566802080-nike-sneaker",
    "https://www.vinted.de/items/10566802080",
    "https://www.vinted.co.uk/items/10566802080-nike-sneaker",
    "https://www.vinted.de/member/3162455054",
    "https://www.vinted.de/member/3162455054-vrna21",
    "https://www.vinted.de/member/3162455054?page=2",
    "https://www.vinted.co.uk/catalog?search_text=nike",
    "https://www.vinted.com/catalog?search_text=nike",
    "https://vinted.de/catalog?search_text=nike"
  ],
  "sellers": [
    "https://www.vinted.de/member/3162455054"
  ],
  "fetchDetails": false,
  "fetchSellerProfiles": false,
  "maxReviews": 20,
  "maxItems": 20,
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "ignoreFieldsForChanges": [],
  "maxNotifyListings": 50
}
```

# Actor output Schema

## `overview` (type: `string`):

No description

## `reviews` (type: `string`):

No description

## `detail` (type: `string`):

No description

## `changes` (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 = {
    "mode": "search",
    "country": "de",
    "queries": [
        "nike"
    ],
    "urls": [
        "https://www.vinted.de/catalog?search_text=nike",
        "https://www.vinted.de/catalog?search_text=nike&page=2",
        "https://www.vinted.de/catalog?search_text=nike&order=newest_first",
        "https://www.vinted.de/catalog?search_text=nike&catalog[]=1242&brand_ids[]=53&price_to=50&status_ids[]=6",
        "https://www.vinted.de/catalog?brand_ids[]=53",
        "https://www.vinted.de/catalog/1242-sneakers",
        "https://www.vinted.de/catalog/1242-sneakers?page=2&order=newest_first",
        "https://www.vinted.de/catalog/1242-sneakers?brand_ids[]=53",
        "https://www.vinted.de/brand/53-nike",
        "https://www.vinted.de/items/10566802080-nike-sneaker",
        "https://www.vinted.de/items/10566802080",
        "https://www.vinted.co.uk/items/10566802080-nike-sneaker",
        "https://www.vinted.de/member/3162455054",
        "https://www.vinted.de/member/3162455054-vrna21",
        "https://www.vinted.de/member/3162455054?page=2",
        "https://www.vinted.co.uk/catalog?search_text=nike",
        "https://www.vinted.com/catalog?search_text=nike",
        "https://vinted.de/catalog?search_text=nike"
    ],
    "sellers": [
        "https://www.vinted.de/member/3162455054"
    ],
    "proxy": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    },
    "incrementalMode": false,
    "emitUnchanged": false,
    "emitExpired": false,
    "ignoreFieldsForChanges": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/vinted-marketplace-scraper").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 = {
    "mode": "search",
    "country": "de",
    "queries": ["nike"],
    "urls": [
        "https://www.vinted.de/catalog?search_text=nike",
        "https://www.vinted.de/catalog?search_text=nike&page=2",
        "https://www.vinted.de/catalog?search_text=nike&order=newest_first",
        "https://www.vinted.de/catalog?search_text=nike&catalog[]=1242&brand_ids[]=53&price_to=50&status_ids[]=6",
        "https://www.vinted.de/catalog?brand_ids[]=53",
        "https://www.vinted.de/catalog/1242-sneakers",
        "https://www.vinted.de/catalog/1242-sneakers?page=2&order=newest_first",
        "https://www.vinted.de/catalog/1242-sneakers?brand_ids[]=53",
        "https://www.vinted.de/brand/53-nike",
        "https://www.vinted.de/items/10566802080-nike-sneaker",
        "https://www.vinted.de/items/10566802080",
        "https://www.vinted.co.uk/items/10566802080-nike-sneaker",
        "https://www.vinted.de/member/3162455054",
        "https://www.vinted.de/member/3162455054-vrna21",
        "https://www.vinted.de/member/3162455054?page=2",
        "https://www.vinted.co.uk/catalog?search_text=nike",
        "https://www.vinted.com/catalog?search_text=nike",
        "https://vinted.de/catalog?search_text=nike",
    ],
    "sellers": ["https://www.vinted.de/member/3162455054"],
    "proxy": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
    "incrementalMode": False,
    "emitUnchanged": False,
    "emitExpired": False,
    "ignoreFieldsForChanges": [],
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/vinted-marketplace-scraper").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 '{
  "mode": "search",
  "country": "de",
  "queries": [
    "nike"
  ],
  "urls": [
    "https://www.vinted.de/catalog?search_text=nike",
    "https://www.vinted.de/catalog?search_text=nike&page=2",
    "https://www.vinted.de/catalog?search_text=nike&order=newest_first",
    "https://www.vinted.de/catalog?search_text=nike&catalog[]=1242&brand_ids[]=53&price_to=50&status_ids[]=6",
    "https://www.vinted.de/catalog?brand_ids[]=53",
    "https://www.vinted.de/catalog/1242-sneakers",
    "https://www.vinted.de/catalog/1242-sneakers?page=2&order=newest_first",
    "https://www.vinted.de/catalog/1242-sneakers?brand_ids[]=53",
    "https://www.vinted.de/brand/53-nike",
    "https://www.vinted.de/items/10566802080-nike-sneaker",
    "https://www.vinted.de/items/10566802080",
    "https://www.vinted.co.uk/items/10566802080-nike-sneaker",
    "https://www.vinted.de/member/3162455054",
    "https://www.vinted.de/member/3162455054-vrna21",
    "https://www.vinted.de/member/3162455054?page=2",
    "https://www.vinted.co.uk/catalog?search_text=nike",
    "https://www.vinted.com/catalog?search_text=nike",
    "https://vinted.de/catalog?search_text=nike"
  ],
  "sellers": [
    "https://www.vinted.de/member/3162455054"
  ],
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "ignoreFieldsForChanges": []
}' |
apify call abotapi/vinted-marketplace-scraper --silent --output-dataset

```

## MCP server setup

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

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/1THqtZOjUi1xwmeZ2/builds/io6JOQdsLJIcBaALK/openapi.json
