# Vinted France — Listings, Sold & Removed Status (`noahadler/vinted-sold-comps`) Actor

Scrape vinted.fr listings for a keyword: itemId, title, brand, size, lastPrice and status (active, sold or removed). Try it returns live catalog matches. Sold/removed only when the item page or a scheduled snapshot says so — no dummy IDs. RESIDENTIAL FR recommended.

- **URL**: https://apify.com/noahadler/vinted-sold-comps.md
- **Developed by:** [Noah Adler](https://apify.com/noahadler) (community)
- **Categories:** E-commerce, Lead generation
- **Stats:** 1 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.50 / 1,000 listings

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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 France — Listings, Sold & Removed Status

**vinted.fr** listings for a keyword: `itemId`, title, brand, size, `lastPrice` and **status** (`active`, `sold`, `removed`). Try it with Nike (or any query) returns live catalog matches. **vinted sold** / removed only when the item page or a scheduled snapshot says so — never dummy IDs.

> Vinted has no public sold archive. Official Vinted Pro allowlist is broken. This Actor searches the public catalog HTML. Sold/removed is a status on a real listing (SoldOut, 404, or gone vs last snapshot), not a historical sold feed.

### Table of contents

- [What you get](#what-you-get)
- [How it works](#how-it-works)
- [Input](#input)
- [Output](#output)
- [Example input](#example-input)
- [Use cases](#use-cases)
- [Proxy](#proxy)
- [API quick start](#api-quick-start)
- [FAQ](#faq)
- [Limitations](#limitations)
- [Racimo](#racimo)
- [Keywords](#keywords)

### What you get

**One row per listing (or sold/removed event)**

- `itemId`, `url` (vinted.fr / .es / .de)
- `title`, `brand`, `size`
- `status`: `active` | `sold` | `removed`
- `lastPrice`, `soldPrice` (only when status is sold and a price is still on the page or snapshot), `currency` (EUR)
- `lastSeenAt`, `daysListed` when the item page exposes a created date
- `sellerId`, `country`, `scrapedAt`

Default Try it (`includeActive: true`) is a **vinted france** catalog for your keyword. It is not a fake sold dump.

### How it works

| Step | Action |
|------|--------|
| 1 | Open `vinted.fr` (or `.es` / `.de`) and load catalog HTML for your keyword or `catalogUrl`. |
| 2 | With `includeActive` on (default), push those cards as `active` rows. |
| 3 | Save the universe in Actor storage for the next run. |
| 4 | Probe only **real** `itemIds` / `watchItems` and IDs that vanished since the last snapshot. |
| 5 | Item page: `InStock` = active. JSON-LD SoldOut = `sold`. HTTP 404 = `removed`. Invented IDs are ignored as comps. |

The internal `/api/v2/catalog/items` route is gone (404). v0.2 uses public catalog HTML + item pages over HTTP. No Playwright.

### Input

| Field | Required | Default | Notes |
|-------|----------|---------|-------|
| `keyword` | One of keyword / URL / IDs | nike air force | Brand query as on Vinted |
| `catalogUrl` | One of keyword / URL / IDs | — | Full catalog or `/vetements` URL |
| `itemIds` | One of keyword / URL / IDs | — | Real IDs or item URLs only |
| `watchItems` | No | — | Last-known cards (`itemId`, `title`, `lastPrice`, …) |
| `country` | No | `fr` | `fr` / `es` / `de` |
| `maxItems` | No | 20 | 1–80 |
| `lookbackHours` | No | 168 | Keep IDs between scheduled runs |
| `includeActive` | No | true | On = usable Try it catalog; off = sold/removed diffs only |
| `proxyConfiguration` | No | RESIDENTIAL + market CC | Keep country aligned to the site |

### Output

One row per listing (or sold/removed). A hard block pushes one row with `error: true` and a message that names RESIDENTIAL + FR/ES/DE.

```json
{
  "itemId": "10121328475",
  "url": "https://www.vinted.fr/items/10121328475-basket-nike",
  "title": "Basket nike",
  "brand": "Nike Air",
  "size": "36",
  "status": "active",
  "lastPrice": 10.0,
  "soldPrice": null,
  "currency": "EUR",
  "sellerId": "222058101",
  "country": "fr",
  "scrapedAt": "2026-09-24T16:00:00Z"
}
```

### Example input

Use this as Try it. It must return Nike (or your keyword) catalog rows, not dummy tombstone IDs.

```json
{
  "keyword": "nike air force",
  "country": "fr",
  "maxItems": 12,
  "lookbackHours": 168,
  "includeActive": true,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": ["RESIDENTIAL"],
    "apifyProxyCountry": "FR"
  }
}
```

To watch sold/removed only, set `includeActive` to false and run on a Schedule every 6–12 hours. The first of those runs seeds watch state; later runs emit comps when items leave the catalog.

To check IDs you already have (must be real listings):

```json
{
  "keyword": "nike air force",
  "country": "fr",
  "itemIds": ["10121328475"],
  "includeActive": true,
  "maxItems": 12
}
```

### Use cases

- Pull a Nike / Zara / Sézane catalog from **vinted france** with last asking price.
- Schedule the same keyword with `includeActive` off to catch **vinted sold** / **vinted removed** inside that universe.
- Feed a repricer: last asking price vs sold price when the page still shows it.

### Proxy

Vinted sits behind Cloudflare / DataDome on some JSON routes. This Actor stays on public HTML. Default proxy is Apify `RESIDENTIAL` with country **FR** for `vinted.fr`. Use ES / DE when you change `country`.

### API quick start

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_TOKEN")
run = client.actor("noahadler/vinted-sold-comps").call(run_input={
    "keyword": "nike air force",
    "country": "fr",
    "maxItems": 12,
    "includeActive": True,
})
items = client.dataset(run["defaultDatasetId"]).list_items().items
```

```bash
curl "https://api.apify.com/v2/acts/noahadler~vinted-sold-comps/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"keyword":"nike air force","country":"fr","maxItems":12,"includeActive":true}'
```

### FAQ

**Why did Try it used to return two nonsense IDs?** Old examples used dummy tombstones (`1000000000`). Those are gone. Try it is keyword catalog.

**Will the first Schedule with includeActive off be empty?** Yes, honestly. There is no public sold feed. The next run compares the snapshot.

**Is this another Vinted search scraper?** Search is what Try it delivers. Sold/removed is optional status / schedule, not invented comps.

**Can I use Spain or Germany?** Yes. `country=es` or `de`. Default is France.

**Do I need Vinted Pro?** No.

**Will I get every sale on Vinted?** No. Only sales/removals inside the universe you watch.

### Limitations

- France is the default market (`vinted.fr`). ES/DE via input.
- Cap 80 items per run.
- Sold price equals last asking price when Vinted hides the close price.
- No invented neighbor IDs. Dummy tombstones are not comps.
- `includeActive` false without a prior snapshot is a seed, not a sold archive.

### Racimo

Hero: **vinted listings + status**. Later (only if this one gets runs): Vinted search FR/ES, then seller wardrobe.

### Keywords

vinted sold · vinted sold items · vinted france sold · vinted listings · vinted removed · vinted scraper

# Actor input Schema

## `keyword` (type: `string`):

Brand or query as buyers type it on Vinted (e.g. nike air force). Used to build the watched universe on vinted.fr. Required unless you pass catalogUrl, itemIds or watchItems.

## `catalogUrl` (type: `string`):

Optional vinted.fr / .es / .de catalog or /vetements search URL. Overrides the keyword URL when set.

## `itemIds` (type: `array`):

Optional. Real Vinted item IDs or https://www.vinted.fr/items/{id} URLs to check now. Do not pass invented IDs — they are not sold comps.

## `watchItems` (type: `array`):

Optional last-known cards from a previous run: itemId, title, brand, size, lastPrice. Used as the lookback universe when you paste last run output instead of relying on Actor storage.

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

Vinted market. Default FR (vinted.fr). ES and DE use vinted.es / vinted.de. Proxy country is aligned automatically.

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

Maximum sold/removed rows to save, and size of the watched search universe (1–80).

## `lookbackHours` (type: `integer`):

How long to keep IDs in the watched universe between scheduled runs (1–720). Typical: 168 (7 days).

## `includeActive` (type: `boolean`):

On = also push live catalog rows as status=active (needed for a useful Try it). Off = only sold/removed vs the last snapshot (first run may be empty).

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

Default: Apify RESIDENTIAL. Country is injected from the market (FR / ES / DE) when you leave it blank.

## Actor input object example

```json
{
  "keyword": "nike air force",
  "country": "fr",
  "maxItems": 20,
  "lookbackHours": 168,
  "includeActive": true,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "FR"
  }
}
```

# Actor output Schema

## `comps` (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 = {
    "keyword": "nike air force"
};

// Run the Actor and wait for it to finish
const run = await client.actor("noahadler/vinted-sold-comps").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 = { "keyword": "nike air force" }

# Run the Actor and wait for it to finish
run = client.actor("noahadler/vinted-sold-comps").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 '{
  "keyword": "nike air force"
}' |
apify call noahadler/vinted-sold-comps --silent --output-dataset

```

## MCP server setup

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

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/rvOBmjZ1mDMN8bnbX/builds/YWCTvZq73MBesBwhZ/openapi.json
