# Vinted Demand & Sell-Through Scraper (`unfenced-group/vinted-arbitrage-scanner`) Actor

Vinted demand intelligence: sell-through rate, realised sale prices, days-to-sell, and cross-market sourcing. No proxy, no PII.

- **URL**: https://apify.com/unfenced-group/vinted-arbitrage-scanner.md
- **Developed by:** [Unfenced Group](https://apify.com/unfenced-group) (community)
- **Categories:** E-commerce, Automation, Developer tools
- **Stats:** 3 total users, 1 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.78 / 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/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 Demand & Sell-Through Scraper

![Vinted Demand & Sell-Through Scraper](https://api.apify.com/v2/key-value-stores/lJlifu6C8YQfZMPKE/records/vinted-arbitrage-scanner)

Analyse demand and sell-through on Vinted at scale: per-query market reports with sold-versus-active ratios, price distributions and demand signals, ready for resale research and pricing decisions. No API key required.

***

### Why this scraper?

#### 🎯 Built for vinted

A dedicated European scraper tuned for vinted — its categories, price formats and currency (EUR), not a generic tool pointed at it.

#### 🤝 Seller and condition

Seller details and item condition in structured fields, ready for filtering.

***

### Input parameters

| Parameter | Type | Default | Description |
|---|---|---|---|
| `searchQuery` | string | `"nike air force 1"` | Product, brand, or model to scan across markets (e.g. 'nike air force 1', 'ralph lauren jumper'). |
| `countries` | array | `["fr", "de", "nl", "it", "es"]` | Vinted markets to scan. Pick 2-5 for a focused arbitrage scan. All listings are normalised to one currency for comparison. |
| `maxResults` | integer | `100` | Total listings to return across all markets (split evenly). Vinted caps each query at ~960 per market. |
| `baseCurrency` | string | `"EUR"` | Currency all markets are converted to for price-gap comparison. Live ECB rates. |
| `sortBy` | string | `"relevance"` | How listings are ordered within each market. |
| `priceMin` | integer | (empty) | Minimum listing price in each market's local currency. Leave empty for no minimum. |
| `priceMax` | integer | (empty) | Maximum listing price in each market's local currency. Leave empty for no maximum. |
| `condition` | array | (empty) | Filter by item condition. Leave empty to include all conditions. |
| `brandNames` | array | (empty) | Filter by brand — just type the names (e.g. 'Nike', 'Stone Island'). The actor looks up each brand's ID automatically. Leave empty for all brands. |
| `brandIds` | array | (empty) | Advanced: filter by raw Vinted brand ID if you already know it. Most users should use Brand names above instead. |
| `sizeIds` | array | (empty) | Advanced: filter by raw Vinted size ID for categories not covered by the dropdowns below. Find it in the URL after selecting a size filter. Leave empty for all sizes. |
| `clothingSizes` | array | (empty) | Filter by clothing size (XS–XXXL). For shoes, use the shoe size filter below. Leave empty for all sizes. |
| `shoeSizes` | array | (empty) | Filter by EU shoe size (38–47.5). For clothing, use the clothing size filter above. Leave empty for all sizes. |
| `kidsSizes` | array | (empty) | Filter by kids clothing size (age / cm, e.g. '6 yr / 116 cm'). Covers newborn to 16 years. Leave empty for all sizes. |
| `colors` | array | (empty) | Filter listings by colour. Leave empty for all colours. Note: Vinted does not return a colour value per listing, so this narrows the scan but does not add a colour output field. |
| `gender` | string | `"all"` | Filter by department. Vinted sells far more than clothing — pick Home, Electronics, Video Games, Books & Media, Beauty, Jewellery, Sports, or Bags for non-fashion categories. Leave as 'All' to search everything. Note: if you pick a specific Category below, that takes precedence over this. |
| `category` | string | `"all"` | Filter by product category. These are the verified cross-department categories. For any other category, use the advanced Category ID field below. If set, this overrides the Department filter. |
| `categoryId` | integer | (empty) | Advanced: filter by a specific Vinted catalog ID not covered by the Category dropdown. Find it in the catalog URL (catalog\[]=NUMBER). Overrides Department and Category if set. |
| `includeListings` | boolean | `true` | Output every matching listing row. Disable to get only the cross-country arbitrage summary (cheaper). |
| `dedupeAcrossMarkets` | boolean | `true` | Vinted shows the same listing in every market it ships to. With this on, each item appears once, with a 'markets' field listing every market it was found in. Turn off to get one row per market. |
| `fetchDetails` | boolean | `false` | Fetch each listing's page for the seller's description, declared colour, and full category path — data not in the fast search API. This makes runs slower and is billed at a higher per-result rate. Leave off for fast, cheap catalog data. |
| `sellThroughTracking` | boolean | `false` | Measure real demand. Run this on a schedule (e.g. daily): the actor snapshots live listings and flags items that disappear as 'likely sold', giving you a sell-through rate and median days-to-sell for your niche. Overrides the normal scan output. |
| `trackerName` | string | (empty) | A stable name for this sell-through tracker (e.g. 'nike-af1-kids'). Reuse the same name across scheduled runs so history accumulates. Leave empty to auto-generate from the search query. |
| `missingRunsBeforeSold` | integer | `2` | How many consecutive runs an item must be absent before it's marked likely sold. Use 2 to avoid false positives from search-ranking shuffle. |
| `emitActiveItems` | boolean | `true` | In sell-through mode, also output items that are still listed (with days-listed and any price drop). Disable to output only newly-sold items and the summary (cheaper). |
| `minSellThroughAlertPct` | integer | (empty) | In sell-through mode, only send a webhook alert when the sell-through rate is at least this percentage. Leave empty to always alert. Pairs with the Telegram/Discord/Slack fields below. |
| `includeArbitrageSummary` | boolean | `true` | Add one summary row comparing median/min/max prices across markets and naming the buy-low / sell-high pair. |
| `telegramBotToken` | string | (empty) | Optional. Send each run's arbitrage alert to Telegram. Create a bot with @BotFather and paste its token here. Also fill in the chat ID below. |
| `telegramChatId` | string | (empty) | Optional. The chat or channel ID to send Telegram alerts to (e.g. your user ID, or -100... for a channel). Required if a bot token is set. |
| `discordWebhookUrl` | string | (empty) | Optional. Send each run's arbitrage alert to a Discord channel. Paste an Incoming Webhook URL (Server Settings → Integrations → Webhooks). |
| `slackWebhookUrl` | string | (empty) | Optional. Send each run's arbitrage alert to a Slack channel. Paste an Incoming Webhook URL (api.slack.com/messaging/webhooks). |
| `minSpreadPctAlert` | integer | (empty) | Only send alerts when the cross-country median spread is at least this percentage. Leave empty to always alert. Useful for scheduled runs that should only ping you on real opportunities. |
| `alertTopListings` | integer | `5` | How many of the cheapest listings to include in each alert message. |

***

### Output schema

Every field below is present on every record. Fields the source does not publish for a given item are returned as `null` rather than omitted.

| Field | Type | Description |
|---|---|---|
| `_type` | string |  type. |
| `id` | string | Vinted listing ID. |
| `country` | string | Market the listing was found in. |
| `url` | string | Direct listing URL. |
| `title` | string | Listing title. |
| `brand` | string | null |
| `size` | string | null |
| `condition` | string | null |
| `currency` | string | Local currency of the listing. |
| `itemPrice` | number | Seller's asking price (local currency). |
| `buyerPrice` | number | Total the buyer pays incl. Buyer Protection (local currency). |
| `serviceFee` | number | null |
| `favouriteCount` | number | null |
| `isPromoted` | boolean | Whether the listing is a promoted/bumped item. |
| `isBusinessSeller` | boolean | null |
| `photoCount` | number | Number of photos on the listing. |
| `photoUrl` | string | null |
| `dominantColor` | string | null |
| `photoUploadedAt` | string | null |
| `source` | string | Source. |
| `scrapedAt` | string | Timestamp when this record was scraped. |
| `itemPriceBase` | number | null |
| `buyerPriceBase` | number | null |
| `baseCurrency` | string | The comparison currency. |
| `markets` | array | Every market this exact item was found in (cross-market duplicates are merged into one row). |

#### Example record

```json
{
  "_type": "LISTING",
  "id": "9222956838",
  "country": "fr",
  "url": "https://www.vinted.fr/items/9222956838-nike-air-max-muse-beige",
  "title": "Nike Air Max Muse beige",
  "brand": "Nike",
  "size": "38.5",
  "condition": "Très bon état",
  "currency": "EUR",
  "itemPrice": 30,
  "buyerPrice": 32.2,
  "serviceFee": 2.2,
  "favouriteCount": 11,
  "isPromoted": false,
  "isBusinessSeller": false,
  "photoCount": 6,
  "photoUrl": "https://images1.vinted.net/t/04_0178a_RicYp7npXNhP88DF81HxR1j5/f800/1782037084.jpeg?s=cc093d2951e723dd88aafcea690c2f782094b11c",
  "dominantColor": "#7D7266",
  "photoUploadedAt": "2026-06-21T10:18:04.000Z",
  "source": "vinted-arbitrage-scanner",
  "scrapedAt": "2026-06-27T12:30:04.894Z",
  "itemPriceBase": 30,
  "buyerPriceBase": 32.2,
  "baseCurrency": "EUR",
  "markets": [
    "fr"
  ]
}
```

***

### Examples

**Search for Nike hoodie:**

```json
{
  "searchQuery": "Nike hoodie",
  "maxResults": 100
}
```

**Filter by category or condition:**

```json
{
  "searchQuery": "Nike hoodie",
  "maxResults": 100,
  "condition": [
    "used"
  ]
}
```

**Larger run with a hard cap:**

```json
{
  "searchQuery": "Nike hoodie",
  "maxResults": 500
}
```

**Daily monitoring (run on a schedule):**

```json
{
  "searchQuery": "Nike hoodie",
  "maxResults": 500
}
```

Schedule this input to run once a day in the Apify Scheduler to track prices, stock and new items over time.

***

### 💰 Pricing

**$0.8 per 1,000 results** — you only pay for successfully retrieved market reports. Failed retries are never charged.

| Results | Cost |
|---|---|
| 100 | ~$0.08 |
| 1,000 | ~$0.8 |
| 10,000 | ~$8 |
| 100,000 | ~$80 |

> Flat-rate alternatives typically charge $29–$49/month regardless of usage.

Subscription tiers lower the rate further, down to $0.76 per 1,000 at the highest tier. Use the **Max results** cap in the input to control your spend exactly.

***

### Performance

| Run size | Typical time |
|---|---|
| 100 results | under 1 minute |
| 1,000 results | 2–4 minutes |
| 10,000 results | 20–35 minutes |

***

### Known limitations

- Some fields (brand, condition, shipping) are only returned when the seller or source publishes them — expect `null` on a share of records.
- Prices and stock change constantly; records reflect the moment of scraping.
- Very large result sets are bounded by the source's own paging limits.
- Items can sell or be withdrawn at any time, so a small share of URLs may expire shortly after scraping.

***

### Technical details

- **Source:** vinted
- **Coverage:** everything the public search exposes, filterable via the input parameters above
- **Retry:** automatic retry on network errors with exponential backoff

***

### Need a custom scraper?

**[Unfenced Group](https://www.unfencedgroup.nl)** builds Apify actors for any website — for free.

If the site you need isn't in our portfolio yet, just ask. We scope, build, and publish it at no cost to you. You only pay for results — we absorb the compute and proxy costs ourselves. Same pay-per-result pricing, same quality, same standards as every actor in this portfolio.

**Get in touch:** [www.unfencedgroup.nl](https://www.unfencedgroup.nl)

# Actor input Schema

## `searchQuery` (type: `string`):

Product, brand, or model to scan across markets (e.g. 'nike air force 1', 'ralph lauren jumper').

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

Vinted markets to scan. Pick 2-5 for a focused arbitrage scan. All listings are normalised to one currency for comparison.

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

Total listings to return across all markets (split evenly). Vinted caps each query at ~960 per market.

## `baseCurrency` (type: `string`):

Currency all markets are converted to for price-gap comparison. Live ECB rates.

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

How listings are ordered within each market.

## `priceMin` (type: `integer`):

Minimum listing price in each market's local currency. Leave empty for no minimum.

## `priceMax` (type: `integer`):

Maximum listing price in each market's local currency. Leave empty for no maximum.

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

Filter by item condition. Leave empty to include all conditions.

## `brandNames` (type: `array`):

Filter by brand — just type the names (e.g. 'Nike', 'Stone Island'). The actor looks up each brand's ID automatically. Leave empty for all brands.

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

Advanced: filter by raw Vinted brand ID if you already know it. Most users should use Brand names above instead.

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

Advanced: filter by raw Vinted size ID for categories not covered by the dropdowns below. Find it in the URL after selecting a size filter. Leave empty for all sizes.

## `clothingSizes` (type: `array`):

Filter by clothing size (XS–XXXL). For shoes, use the shoe size filter below. Leave empty for all sizes.

## `shoeSizes` (type: `array`):

Filter by EU shoe size (38–47.5). For clothing, use the clothing size filter above. Leave empty for all sizes.

## `kidsSizes` (type: `array`):

Filter by kids clothing size (age / cm, e.g. '6 yr / 116 cm'). Covers newborn to 16 years. Leave empty for all sizes.

## `colors` (type: `array`):

Filter listings by colour. Leave empty for all colours. Note: Vinted does not return a colour value per listing, so this narrows the scan but does not add a colour output field.

## `gender` (type: `string`):

Filter by department. Vinted sells far more than clothing — pick Home, Electronics, Video Games, Books & Media, Beauty, Jewellery, Sports, or Bags for non-fashion categories. Leave as 'All' to search everything. Note: if you pick a specific Category below, that takes precedence over this.

## `category` (type: `string`):

Filter by product category. These are the verified cross-department categories. For any other category, use the advanced Category ID field below. If set, this overrides the Department filter.

## `categoryId` (type: `integer`):

Advanced: filter by a specific Vinted catalog ID not covered by the Category dropdown. Find it in the catalog URL (catalog\[]=NUMBER). Overrides Department and Category if set.

## `includeListings` (type: `boolean`):

Output every matching listing row. Disable to get only the cross-country arbitrage summary (cheaper).

## `dedupeAcrossMarkets` (type: `boolean`):

Vinted shows the same listing in every market it ships to. With this on, each item appears once, with a 'markets' field listing every market it was found in. Turn off to get one row per market.

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

Fetch each listing's page for the seller's description, declared colour, and full category path — data not in the fast search API. This makes runs slower and is billed at a higher per-result rate. Leave off for fast, cheap catalog data.

## `sellThroughTracking` (type: `boolean`):

Measure real demand. Run this on a schedule (e.g. daily): the actor snapshots live listings and flags items that disappear as 'likely sold', giving you a sell-through rate and median days-to-sell for your niche. Overrides the normal scan output.

## `trackerName` (type: `string`):

A stable name for this sell-through tracker (e.g. 'nike-af1-kids'). Reuse the same name across scheduled runs so history accumulates. Leave empty to auto-generate from the search query.

## `missingRunsBeforeSold` (type: `integer`):

How many consecutive runs an item must be absent before it's marked likely sold. Use 2 to avoid false positives from search-ranking shuffle.

## `emitActiveItems` (type: `boolean`):

In sell-through mode, also output items that are still listed (with days-listed and any price drop). Disable to output only newly-sold items and the summary (cheaper).

## `minSellThroughAlertPct` (type: `integer`):

In sell-through mode, only send a webhook alert when the sell-through rate is at least this percentage. Leave empty to always alert. Pairs with the Telegram/Discord/Slack fields below.

## `includeArbitrageSummary` (type: `boolean`):

Add one summary row comparing median/min/max prices across markets and naming the buy-low / sell-high pair.

## `telegramBotToken` (type: `string`):

Optional. Send each run's arbitrage alert to Telegram. Create a bot with @BotFather and paste its token here. Also fill in the chat ID below.

## `telegramChatId` (type: `string`):

Optional. The chat or channel ID to send Telegram alerts to (e.g. your user ID, or -100... for a channel). Required if a bot token is set.

## `discordWebhookUrl` (type: `string`):

Optional. Send each run's arbitrage alert to a Discord channel. Paste an Incoming Webhook URL (Server Settings → Integrations → Webhooks).

## `slackWebhookUrl` (type: `string`):

Optional. Send each run's arbitrage alert to a Slack channel. Paste an Incoming Webhook URL (api.slack.com/messaging/webhooks).

## `minSpreadPctAlert` (type: `integer`):

Only send alerts when the cross-country median spread is at least this percentage. Leave empty to always alert. Useful for scheduled runs that should only ping you on real opportunities.

## `alertTopListings` (type: `integer`):

How many of the cheapest listings to include in each alert message.

## Actor input object example

```json
{
  "searchQuery": "nike air force 1",
  "countries": [
    "fr",
    "de",
    "nl",
    "it",
    "es"
  ],
  "maxResults": 100,
  "baseCurrency": "EUR",
  "sortBy": "relevance",
  "gender": "all",
  "category": "all",
  "includeListings": true,
  "dedupeAcrossMarkets": true,
  "fetchDetails": false,
  "sellThroughTracking": false,
  "missingRunsBeforeSold": 2,
  "emitActiveItems": true,
  "includeArbitrageSummary": true,
  "alertTopListings": 5
}
```

# Actor output Schema

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

No description

# API

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

## JavaScript example

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

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

// Prepare Actor input
const input = {
    "searchQuery": "nike air force 1"
};

// Run the Actor and wait for it to finish
const run = await client.actor("unfenced-group/vinted-arbitrage-scanner").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 = { "searchQuery": "nike air force 1" }

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

```

## MCP server setup

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

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/acts/sRni2FBblk2sAHYir/builds/qnmnY9ShfF8mcWlQ6/openapi.json
