# Vinted.fr - Listings, Item Detail & Seller Reviews (`abotapi/vinted-fr-scraper`) Actor

Scrape Vinted.fr, France's C2C second-hand fashion marketplace: search with brand/price/condition/sort filters, paste item or seller URLs, or pull a seller's feedback history. Returns title, brand, price, condition, size, photos, seller rating per listing; reviews mode returns all seller ratings.

- **URL**: https://apify.com/abotapi/vinted-fr-scraper.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Categories:** E-commerce, Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 listing 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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Vinted.fr Scraper

Pull rich listing data from Vinted.fr, France's C2C second-hand fashion marketplace. Search by keyword with brand, price, condition, and sort filters, paste item or seller/member links directly, or pull a seller's complete feedback history. Every record includes identity, price, brand, and photos out of the box; turning on detail enrichment adds full description, condition, size, material, colour, and the seller's live feedback rating and count.

### Why This Scraper?

- **Real listing filters, not guesswork.** Brand id, price range, condition, and sort order all map directly to Vinted's own search parameters and are verified to narrow results, not just accepted and ignored.
- **Honest pricing, no fabricated discounts.** Vinted shows a second, larger figure next to the listed price ("buyer protection included"), which is the current price plus a checkout fee, never a former price. This scraper reports the real listed price and does not invent a wasPrice/discount field that the site itself doesn't support.
- **Full item detail from the item's own page.** Condition, size, material, colour, upload date, and full description, matched against the listing's own structured data and on-page attributes.
- **Native seller feedback.** Overall rating, feedback count, and every individual feedback entry (reviewer, date, star rating, and comment) sourced from Vinted's own feedback system, no third-party review vendor.
- **Three ways in.** Keyword/brand/category search with real filters, paste any item or seller/member URL (a seller URL walks their whole storefront), or pull a seller's full feedback history directly.
- **Optional export to your apps.** Send results into Notion, Linear, Airtable, or any Apify MCP connector alongside the dataset.
- **Resume & recurring updates.** Turn on Incremental mode to get only NEW, UPDATED, and REAPPEARED records on every scheduled run — a sold or removed listing comes back as EXPIRED — or resume one specific interrupted crawl with `resumeFromRunId`.

### Input

| Field | Description |
|---|---|
| **Mode** | `search`, `url` or `reviews`. |
| **Search keywords** | Free-text keywords (search mode). Item budget splits evenly across them. |
| **Category ID** | Vinted catalog id (search mode, optional). Narrows a keyword search, or browses the whole category alone. |
| **Brand ID** | Vinted brand id (search mode, optional). |
| **Condition** | Filter to one or more condition levels (search mode, optional). |
| **Minimum / Maximum price (EUR)** | Price range (search mode, optional). |
| **Sort order** | `relevance` (default), `newest_first`, `price_low_to_high`, `price_high_to_low` (search mode). |
| **URLs to scrape** | Item pages (`/items/<id>-<slug>`) or seller/member pages (`/member/<id>`) to paste (url mode). A member URL enumerates every item that seller has listed. |
| **Sellers** | Usernames, numeric member ids, or `/member/<id>` URLs to fetch feedback for (reviews mode). |
| **Fetch item detail** | Adds description, condition, size, material, colour, and the seller's feedback rating/count. On by default. |
| **Max pages per search** | Result pages walked per keyword/category/URL/seller entry. `0` (default) = unlimited, bounded by Max items. |
| **Max items total** | Hard cap on total items/reviews returned across the run. Default 20; `0` = unlimited. |
| **Proxy** | Apify Proxy — Datacenter by default (works reliably and on every plan); switch to Residential only if you see failed runs. |
| **Resume from a previous run** | Continue one specific previous run/dataset: records already collected there are skipped. For recurring monitoring of the same search/URLs/sellers, use Incremental mode instead — see "Resume & recurring updates" below. |
| **Incremental changes for scheduled runs** | Daily/recurring monitoring of the same search/URLs/sellers. First run returns everything as `NEW`; later runs return only `NEW`/`UPDATED`/`REAPPEARED` by default. See "Resume & recurring updates" below. |
| **State key** | Optional name for a monitoring campaign, so its incremental state stays stable or is deliberately shared. Auto-derived from your mode/search/URL/seller/detail settings when left empty. |
| **Emit unchanged records** | Incremental mode only. Also return records unchanged since the last run, marked `UNCHANGED`. Adds and bills extra rows you already have. |
| **Emit expired records** | Incremental mode only. Also return records from a previous run no longer found (sold/removed), marked `EXPIRED`, once a run has fully scanned the tracked scope (not capped, not a resume). Adds and bills extra synthetic rows. |
| `mcpConnectors` | Optional MCP connectors to export results into (Notion, Linear, Airtable, Apify). |
| `notionParentPageUrl` | Notion connector only: page under which item pages are created. |
| `maxNotifyListings` | Cap on items exported to each connector per run. Does not affect the dataset. |

### Data You Get

| Field | Example value |
|---|---|
| itemId | `00000001` |
| title | `Sample Listing Title` |
| url | `https://www.vinted.fr/items/00000001-sample-listing-title` |
| brandTitle | `Sample Brand` |
| priceAmount / currency | `15.0`, `EUR` |
| promoted | `false` |
| photos\[] | `[{"url": "https://images.example.com/000000000.jpeg", "isMain": true}]` |
| seller | `{"id": "00000002", "username": "sample_seller"}` |
| description *(fetchDetails)* | `The listing's own description text appears here when present.` |
| condition *(fetchDetails)* | `Bon état` |
| size *(fetchDetails)* | `M` |
| material *(fetchDetails)* | `Coton` |
| color *(fetchDetails)* | `Bleu` |
| categoryPath *(fetchDetails)* | `Femmes Vêtements` |
| catalogId *(fetchDetails)* | `0000` |
| sellerFeedbackRating / sellerFeedbackCount *(fetchDetails)* | `4.9`, `120` |

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

#### Reviews mode output

| Field | Example value |
|---|---|
| sellerId | `00000002` |
| reviewId | `0000000001` |
| itemId | `00000003` |
| reviewerUsername | `sample_reviewer` |
| rating | `5` |
| body | `Sample feedback comment text.` |
| isSystemFeedback | `false` |
| createdAt | `2026-01-01T00:00:00+01:00` |
| sellerFeedbackRating / sellerFeedbackCount | `4.9`, `120` |

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

**Incremental mode only.** When `incrementalMode` is on, every returned record (items or
reviews) also carries:

| Field | Description |
|---|---|
| `changeType` | `NEW` | `UPDATED` | `UNCHANGED` | `REAPPEARED` | `EXPIRED` |
| `changedFields` | Top-level fields that changed since last seen; non-empty only for `UPDATED` |
| `firstSeenAt` | When this record was first observed by this monitoring campaign |
| `lastSeenAt` | When this record was last observed |

#### No wasPrice / discount field, by design

Vinted item pages and listing cards show a second amount next to the price (e.g. "15,00 €" then "16,45 € Protection acheteurs incluse"). That second figure is the **current price plus the buyer-protection fee** charged at checkout, verified against the item page's own pricing breakdown, where the delta between the two exactly equals the disclosed fee. It is not a former/"was" price, and Vinted does not otherwise expose a markdown history for peer-to-peer listings. This scraper reports only `priceAmount`, the real listed price, rather than fabricating a discount field the marketplace doesn't have.

### How to Use

**Basic keyword search**

```json
{
  "mode": "search",
  "queries": ["sample keyword"],
  "maxItems": 20
}
```

**Search with brand, price range, condition, and sort**

```json
{
  "mode": "search",
  "queries": ["sample keyword"],
  "brandId": "53",
  "minPrice": 5,
  "maxPrice": 60,
  "conditions": ["3", "4"],
  "sortBy": "price_low_to_high",
  "fetchDetails": true,
  "maxItems": 20
}
```

**Paste item or seller/member links**

```json
{
  "mode": "url",
  "urls": [
    "https://www.vinted.fr/items/00000001-sample-listing-title",
    "https://www.vinted.fr/member/00000002"
  ],
  "maxItems": 20
}
```

**Pull a seller's full feedback history**

```json
{
  "mode": "reviews",
  "sellers": ["00000002"],
  "maxItems": 100
}
```

`sellers` accepts a numeric member id or a full `/member/<id>` URL (find the id in the seller's profile page URL).

### Filters (search mode)

| Field | Notes |
|---|---|
| `queries` | Free-text keywords; item budget splits evenly across them. |
| `categoryId` | Vinted catalog id, from a category page URL: `vinted.fr/catalog/<id>-<name>`. |
| `brandId` | Vinted brand id, from a brand page URL: `vinted.fr/brand/<id>-<name>`. |
| `conditions` | Filter to one or more condition levels (new with tags, new without tags, very good, good, satisfactory). |
| `minPrice` / `maxPrice` | EUR price range. |
| `sortBy` | `relevance` (default), `newest_first`, `price_low_to_high`, `price_high_to_low`. |

### Resume & recurring updates

There are two different things here — pick the one that matches what you're doing:

| Need | Use |
| --- | --- |
| A crawl stopped and should continue | `resumeFromRunId` |
| Run the same search/URLs/sellers every day and receive only changes | `incrementalMode` |
| Keep separate monitoring campaigns for similar scopes | distinct `stateKey` values |
| Run a normal full snapshot | leave both off |

**Resume** (`resumeFromRunId`) continues one specific interrupted or previous large crawl: paste a run ID or dataset ID and this run skips records already collected there, returning only the remaining new ones.

**Incremental mode** (`incrementalMode`) is for a schedule (for example, daily): the actor remembers the previous run of the *same* scope by itself, so you never paste a run ID. The first run returns everything as `NEW`. Later runs return only `NEW`, `UPDATED`, and `REAPPEARED` records by default — duplicates and unchanged records are suppressed (and not charged). Because Vinted is a C2C resale marketplace, a listing that sells is gone for good, so `EXPIRED` is a real, useful signal here, not noise — turn on `emitExpired` to also get those rows. Turn on `emitUnchanged` or `emitExpired` only when you also want those rows returned (and billed for). State is isolated per mode and search/URL/seller/detail setup automatically; set `stateKey` to name or deliberately share a monitoring campaign. Covers every mode: search, url (both pasted item links and member enumeration), and reviews.

Scheduled-run example — same search, run daily:

Day 1 (first run ever for this search):

```json
{ "mode": "search", "queries": ["nike"], "incrementalMode": true }
```

→ every item comes back with `"changeType": "NEW"`.

Day 2 (the schedule fires again, identical input):

```json
{ "mode": "search", "queries": ["nike"], "incrementalMode": true }
```

→ items whose price/condition/etc. changed come back as `"changeType": "UPDATED"` with `changedFields` listing what changed, brand-new items come back as `"changeType": "NEW"`, items that sold and were relisted come back as `"changeType": "REAPPEARED"` — and items still listed, unchanged, are **not** returned at all (suppressed, not charged) unless `emitUnchanged` is on. Turn on `emitExpired` to also get a synthetic `EXPIRED` row for any tracked item that sold or was removed.

### Connection

Vinted.fr works reliably over a Datacenter connection, so it stays the cost-efficient default and works on every Apify plan, including free tier. Switch to Residential in the Proxy field only if you notice a rise in failed runs.

### Export to your apps (optional)

Select one or more MCP connectors (Notion, Linear, Airtable, or a custom Apify MCP connector) under `mcpConnectors` to mirror results into the app you already use, alongside the dataset. Authorize the connector once under Apify → Settings → Integrations. For Notion, also set `notionParentPageUrl` to the page results should be created under. `maxNotifyListings` caps how many items are sent per connector per run (does not affect the dataset).

# Actor input Schema

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

'search' finds listings by keyword with Vinted'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).

## `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 Vinted catalog id, e.g. '1242' (baskets homme). Find it in a category page URL: vinted.fr/catalog/<id>-<name>. Narrows a keyword search, or browses the whole category when Search keywords is left empty.

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

A Vinted brand id, e.g. '53' (Nike). Find it in a brand page URL: vinted.fr/brand/<id>-<name>.

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

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

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

Only return items priced at or above this amount.

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

Only return items priced at or below this amount.

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

Order of the returned results.

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

Paste one or more Vinted item pages (/items/<id>-<slug>) or seller/member pages (/member/<id>). A member URL enumerates every item that seller has listed. Filter fields above are ignored in this mode.

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

Seller usernames, numeric member ids, or full /member/<id> URLs to fetch feedback for.

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

Adds full description, condition, size, material, colour, upload date, and the seller's feedback rating/count. Identity, price, brand, and photos are already included without this toggle.

## `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 items (listings or reviews) returned across the whole run. 0 = unlimited (still bounded by Max pages per search).

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

Vinted.fr works reliably over a Datacenter connection, so it stays the cost-efficient default. Switch to Residential only if you see a rise in failed runs.

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

## `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 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, search/URL/seller and detail settings — different searches 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 or when Resume was used. This returns — and bills — extra synthetic rows, so leave it off unless you need expiry tracking.

## Actor input object example

```json
{
  "mode": "search",
  "queries": [
    "nike"
  ],
  "sortBy": "relevance",
  "urls": [
    "https://www.vinted.fr/member/245246277"
  ],
  "fetchDetails": true,
  "maxItems": 20,
  "proxy": {
    "useApifyProxy": true
  },
  "maxNotifyListings": 50,
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false
}
```

# Actor output Schema

## `overview` (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",
    "queries": [
        "nike"
    ],
    "urls": [
        "https://www.vinted.fr/member/245246277"
    ],
    "proxy": {
        "useApifyProxy": true
    },
    "incrementalMode": false,
    "emitUnchanged": false,
    "emitExpired": false
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/vinted-fr-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",
    "queries": ["nike"],
    "urls": ["https://www.vinted.fr/member/245246277"],
    "proxy": { "useApifyProxy": True },
    "incrementalMode": False,
    "emitUnchanged": False,
    "emitExpired": False,
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/vinted-fr-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).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",
  "queries": [
    "nike"
  ],
  "urls": [
    "https://www.vinted.fr/member/245246277"
  ],
  "proxy": {
    "useApifyProxy": true
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false
}' |
apify call abotapi/vinted-fr-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=abotapi/vinted-fr-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

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