# Vinted Arbitrage Scanner (`lowlanddata/vinted-arbitrage-scanner`) Actor

One query, priced across up to 23 Vinted country sites: median, quartiles, spread percent and an opportunity score per country - where an item is cheap and where it sells high. Aggregates only, zero seller data by construction. About $0.02 per full comparison.

- **URL**: https://apify.com/lowlanddata/vinted-arbitrage-scanner.md
- **Developed by:** [Lowland Data](https://apify.com/lowlanddata) (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.59 / 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?

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 Arbitrage Scanner — where is this item cheap, where does it sell high?

One search query, priced across up to 23 Vinted country sites in a single run. Instead of hundreds of raw listings you get **one statistics row per country**: median, quartiles, min/max asking prices, the median total buyer price, and a spread percentage that tells you exactly which market to buy in and which to sell into.

Built on the [Vinted Scraper](https://apify.com/lowlanddata/vinted-scraper) and stricter still on privacy: the output contains **no listings and no seller data at all** — only per-country aggregates. There is structurally nothing personal in your dataset.

### Quick start (30 seconds)

1. Put what you'd type in the Vinted search box into **searchQuery** — e.g. `nike air max 90`.
2. Keep the default **countries** (eight euro markets: fr, de, es, it, nl, be, pt, at) or pick your own set of 2–23 sites.
3. Click **Start**. Under a minute later, the dataset's **Overview** tab shows one row per country, sorted spreads and all — or **Export** it as CSV/Excel/JSON.

A real run of `nike air max 90` across the default eight: median **€80** on vinted.nl and vinted.be, **€110** on vinted.fr, vinted.de and vinted.it — a **38% spread** between the buy side and the sell side, found in one run for about two cents.

### What you can build with it

- **Cross-border reselling.** Find the country where your item is cheapest, buy there, list where the median is 30–40% higher. The `cheapestInCurrencyGroup` flag marks the buy side; `spreadPct` ranks the sell sides.
- **Price new stock as a Vinted power-seller.** Before listing, run your item's query once and see the median and quartiles in every market you ship to — price at the p75 of the dearest one, not at your local median.
- **Watch second-hand price levels per country.** Brand and category analysts get a clean per-country price table for any product term, refreshed on a schedule, with no listing data to store or scrub.
- **Feed an AI agent one clean table.** Eight rows of aggregates answer "where should I sell this?" directly — no post-processing of hundreds of listings needed.

### What you get

One dataset row per country site:

```json
{
  "searchQuery": "nike air max 90",
  "domain": "fr",
  "site": "vinted.fr",
  "currency": "EUR",
  "matchedListings": 960,
  "sampleSize": 50,
  "medianCents": 11000,
  "p25Cents": 5000,
  "p75Cents": 12500,
  "minCents": 2000,
  "maxCents": 32500,
  "medianTotalCents": 11620,
  "spreadPct": 38,
  "cheapestInCurrencyGroup": false,
  "opportunityScore": 38
}
```

Field notes, so you know exactly what you are buying:

- All amounts are in minor units (cents, pence) in the row's `currency` — never converted between currencies.
- `medianCents`, `p25Cents`, `p75Cents`, `minCents`, `maxCents` describe the sampled **asking** prices; `medianTotalCents` is the median price a buyer actually pays, buyer-protection fee included, when the site reports it.
- `spreadPct` is how far this country's median sits above the cheapest median **of the same currency group**: 0 means this is the cheapest market. In the example above, vinted.fr's €110 median sits 38% above the €80 of vinted.nl.
- `cheapestInCurrencyGroup` marks the buy side of the arbitrage; `opportunityScore` is `spreadPct` clamped to 0–100 — a ready-made ranking for the sell side.
- `sampleSize` tells you how many priced listings the statistics rest on; `matchedListings` is how many the site reported in total. Sampling uses relevance ordering, so the cut is representative of what a buyer actually sees.

### How much does a comparison cost?

$1.99 per 1,000 rows delivered, pay-as-you-go — and a row here is a **country**, not a listing. A default run outputs 8 rows: about **$0.016 per full comparison**. Even all 23 countries cost under five cents.

The price is all-inclusive — platform usage is covered, with no separate compute or proxy charges. Datacenter proxies are sufficient. Empty or failed runs cost nothing.

Free-plan runs are capped at 25 rows — which still covers a complete default comparison, and even a full 23-country one.

### Not technical? Let your AI assistant set it up

Copy this into ChatGPT, Claude or any AI assistant, fill in the one line, and follow the conversation:

```text
Help me set up the "Vinted Arbitrage Scanner" actor on Apify
(https://apify.com/lowlanddata/vinted-arbitrage-scanner). Guide me one step at a time.

What I want to compare: [E.G. "nike air max 90 across the euro Vinted sites"]

Guide me to:
1. Propose my input values: searchQuery (what I'd type in the Vinted search box),
   countries (2-23 site suffixes like fr, de, nl, be; default is eight euro markets),
   maxItemsPerCountry (sample size, default 96), and an optional priceMin/priceMax
   band to cut junk from the sample.
2. Create a free Apify account (apify.com), open the actor page, paste the values
   into the Input form, and start a run.
3. Read the result table with me: which country is cheapest, which has the highest
   spreadPct, and what that means for where to buy and where to sell.
4. Set up a daily Schedule in the Apify Console with the same input, plus an email
   or Slack integration, so I can watch the spread over time.
5. If the results are what I wanted, remind me at the end to leave a quick rating on
   the actor page, and to report anything broken or missing on its Issues tab.
```

### Input

| Field                | Description                                                                                                                                                |
| -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `searchQuery`        | What you'd type in the Vinted search box. Required.                                                                                                        |
| `countries`          | 2–23 country-site suffixes to compare. Default: `["fr", "de", "es", "it", "nl", "be", "pt", "at"]` — eight euro markets, so every spread is same-currency. |
| `maxItemsPerCountry` | Priced listings sampled per site, 20–500. Default 96 ≈ one catalog page. Bigger samples mean steadier medians and more requests.                           |
| `priceMin`           | Ignore listings below this price (site currency) — cuts junk from the sample.                                                                              |
| `priceMax`           | Ignore listings above this price (site currency).                                                                                                          |
| `maxItems`           | Upper bound on output rows (one per country). Default 25.                                                                                                  |
| `proxyConfiguration` | Proxy settings; keep Apify proxy enabled.                                                                                                                  |

A run minimally needs a `searchQuery`. If fewer than 2 countries could be sampled, the run fails honestly with the reason in its status message — a comparison of one is not a comparison.

### Use it from your code

Run the comparison and get the country rows straight back with one HTTP call — a default run finishes well inside the sync window:

```bash
curl "https://api.apify.com/v2/acts/lowlanddata~vinted-arbitrage-scanner/run-sync-get-dataset-items?token=<YOUR_API_TOKEN>" \
  -X POST -H "Content-Type: application/json" \
  -d '{"searchQuery": "nike air max 90"}'
```

Node.js:

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

const client = new ApifyClient({ token: '<YOUR_API_TOKEN>' });
const run = await client.actor('lowlanddata/vinted-arbitrage-scanner').call({
  searchQuery: 'nike air max 90',
  countries: ['fr', 'de', 'nl', 'be'],
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

Python:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_API_TOKEN>")
run = client.actor("lowlanddata/vinted-arbitrage-scanner").call(
    run_input={"searchQuery": "nike air max 90", "countries": ["fr", "de", "nl", "be"]})
items = client.dataset(run["defaultDatasetId"]).list_items().items
```

Schedules, webhooks and the Make/Zapier/n8n integrations all work out of the box — this is a standard Apify actor.

### Use it with AI agents (MCP)

Claude, Cursor and other MCP-capable agents can run this scanner as a tool through Apify's hosted MCP server: the agent picks the query and countries, starts the run and reads the eight-row answer — no glue code.

Claude Code:

```bash
claude mcp add apify --transport http "https://mcp.apify.com?actors=lowlanddata/vinted-arbitrage-scanner"
```

Cursor or Claude Desktop (add a custom connector / MCP server with this URL):

```text
https://mcp.apify.com?actors=lowlanddata/vinted-arbitrage-scanner
```

Sign in with your Apify account when prompted — runs are billed to it. Setup details per client: [Apify MCP docs](https://docs.apify.com/platform/integrations/mcp).

Prompts that work once connected:

- "Where is 'nike air max 90' cheapest on Vinted, and where does it sell highest?"
- "Compare 'barbour jacket' across the euro Vinted sites and tell me if the spread justifies cross-border reselling."
- "Run the arbitrage scan for 'levis 501' across fr, de, nl, be, co.uk and pl and summarize per currency."

### Is it legal to use this data?

The output is per-country price statistics — medians, quartiles, a spread percentage. There is no listing, no title, no photo, no username, no seller anything in it, so the personal-data question the GDPR asks never arises: **aggregates carry nothing to protect.** The market leader in this niche outputs raw listings with seller data attached; this scanner cannot, structurally — the statistics are computed and the listings discarded.

The sampling itself reads only the openly served search pages, paced, with no anti-bot protection bypassed. A blocked country is reported honestly, never fought.

### Is there a Vinted API for price comparison?

Vinted publishes no public API, and no site-provided way to compare its 23 national markets. This actor is the practical alternative: one HTTP call (`run-sync-get-dataset-items`), one row per country, ready for a spreadsheet, a schedule, or an AI agent over MCP.

### What if Vinted blocks a country mid-run?

That country is skipped with a note in the run status and the comparison continues with the rest — one flaky market does not cost you the run. If fewer than 2 countries could be sampled at all, the run fails honestly instead of delivering a one-sided "comparison", and failed runs are free.

### How do I monitor spreads over time?

Put the same input on a daily Schedule in the Apify Console. Each run appends a dated set of country rows; a week of them charts how the nl–fr spread on your item moves, and an email or Slack integration on the runs tells you the moment a market's `opportunityScore` jumps. At about $0.016 per default run, a daily watch costs under fifty cents a month.

### FAQ

**Is Vinted cheaper in France or Germany?** It depends on the item — that is exactly what one run answers. For `nike air max 90`, France and Germany both sat at a €110 median while the Netherlands and Belgium were at €80. Run your own query and read the `medianCents` column.

**How do I find underpriced items on Vinted?** Start here to find the underpriced *market*: the row with `cheapestInCurrencyGroup: true` is where your item is structurally cheap. Then point the [Vinted Scraper](https://apify.com/lowlanddata/vinted-scraper) at that country to pull the individual listings.

**Which country is best to sell on Vinted?** For your item, the row with the highest `opportunityScore` — its median sits furthest above the cheapest market of the same currency.

**How is the spread calculated?** Within each currency group, the cheapest country's median is the baseline; every other country's `spreadPct` is how far its median sits above that baseline, in percent. 0 means cheapest.

**How are different currencies handled?** Each row's amounts stay in that site's own currency, and spreads are only ever computed between sites sharing a currency. The default eight countries are all euro markets, so every spread is directly comparable.

**Why don't you convert pounds or złoty to euros?** Because an FX-converted "spread" would silently mix market pricing with exchange-rate noise and conversion-date assumptions. The scanner reports what the markets actually ask; if you want a cross-currency view, apply your own rate to the clean numbers.

**Does the price include shipping?** No — shipping varies by seller, size and destination and is not part of any amount here. The buyer-protection fee *is* included: `medianTotalCents` is the median of what buyers actually pay at checkout, before shipping.

**Why do I get aggregates instead of the listings?** Because the question this actor answers — where is it cheap, where does it sell high — needs eight numbers, not eight hundred listings. Aggregates are also what makes the output GDPR-clean by construction. When you need the listings themselves, that is the Vinted Scraper's job.

**Can I get seller names or any listing details?** No, structurally. The statistics are computed and the listings discarded; there is no option to change that.

**How big should the sample be?** The default 96 (one catalog page) gives stable medians for common items. For niche queries with few listings, or if medians wobble between runs, raise `maxItemsPerCountry` toward 200–500 — steadier numbers for more requests. `sampleSize` in each row shows what the statistics actually rest on.

**What does a low sampleSize mean?** Few priced listings matched your query on that site. Medians from a handful of listings move around — trust rows with dozens of samples more than rows with five.

**How long does a run take?** Well under a minute for the default eight countries. It fits comfortably inside the synchronous API call.

**How much does it cost?** $1.99 per 1,000 rows, and a row is a country: a default 8-country comparison is about $0.016. Empty or failed runs are free.

**Can I use it on the free plan?** Yes — the 25-row cap covers a full default comparison and even all 23 countries.

**Do I need residential proxies?** No. Standard datacenter proxies through the default Apify proxy setting are sufficient.

**Do I need a Vinted account or API key?** No. The scanner reads openly served search pages; the only account involved is your Apify account.

**Can I export to Excel or CSV?** Yes — every dataset exports as CSV, Excel, JSON or XML from the Apify Console or API. Eight rows per run make a tidy spreadsheet.

**Can I track spreads over time?** Yes — same input on a daily Schedule; each run is a dated snapshot and the datasets accumulate into a spread history. See the monitoring recipe above.

**Does it work with Make, Zapier or n8n?** Yes — it is a standard Apify actor; all platform integrations, webhooks and schedules apply.

### Related scrapers

The same clean-output guarantee, listing-level and beyond:

- [Vinted Scraper](https://apify.com/lowlanddata/vinted-scraper) — the listing-level parent: individual items with prices, brands, sizes and conditions across the same 23 sites. Use it after this scanner tells you which country to dig into.
- [Poshmark Scraper](https://apify.com/lowlanddata/poshmark-scraper) — live fashion-resale listings from the US market.
- [Poshmark Sold Comps](https://apify.com/lowlanddata/poshmark-sold-comps) — sold-price comparables: what items actually fetched, not what sellers asked.

### Troubleshooting

The actor fails fast with the reason in the run's status message:

- **"priceMin must not be higher than priceMax."** — swap the two values.
- **Fewer than 2 countries could be sampled** — the run fails honestly rather than output a one-sided comparison; retry in a few minutes, or widen your country list. Failed runs are free.
- **A country is missing from the results** — it was blocked or returned nothing for your query; the run status note says which and why, and the remaining countries are still compared.
- **Medians differ between two runs** — small samples on niche queries wobble; raise `maxItemsPerCountry` for steadier statistics.

### Support

Found an issue or missing a statistic you need? Open an issue on the actor's **Issues tab** — reports get fixed, this actor is actively maintained.

Working well for you? A **rating on this page** takes ten seconds and helps other resellers find the privacy-clean option — it is also the clearest signal of what we should build next.

# Actor input Schema

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

What to price-compare across country sites, exactly as you would type it in Vinted's search box, e.g. "nike air max 90" or "barbour jacket".

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

Vinted country sites to compare (2-23). Default: eight euro markets, so every spread is same-currency. Non-euro sites (co.uk, pl, cz, se, dk, hu, ro, com) are compared within their own currency only - amounts are never converted.

## `maxItemsPerCountry` (type: `integer`):

Priced listings sampled per site (20-500). One catalog page is ~96; bigger samples cost more requests and steadier medians.

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

Ignore listings below this price (site currency) - cuts junk from the sample.

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

Ignore listings above this price (site currency).

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

Upper bound on output rows (one per country).

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

Proxy settings. Keep Apify proxy enabled.

## Actor input object example

```json
{
  "searchQuery": "nike air max 90",
  "countries": [
    "fr",
    "de",
    "es",
    "it",
    "nl",
    "be",
    "pt",
    "at"
  ],
  "maxItemsPerCountry": 96,
  "maxItems": 25,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `rows` (type: `string`):

One price-statistics row per country site.

# 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 max 90",
    "countries": [
        "fr",
        "de",
        "es",
        "it",
        "nl",
        "be",
        "pt",
        "at"
    ],
    "maxItemsPerCountry": 96,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("lowlanddata/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 max 90",
    "countries": [
        "fr",
        "de",
        "es",
        "it",
        "nl",
        "be",
        "pt",
        "at",
    ],
    "maxItemsPerCountry": 96,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("lowlanddata/vinted-arbitrage-scanner").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 '{
  "searchQuery": "nike air max 90",
  "countries": [
    "fr",
    "de",
    "es",
    "it",
    "nl",
    "be",
    "pt",
    "at"
  ],
  "maxItemsPerCountry": 96,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call lowlanddata/vinted-arbitrage-scanner --silent --output-dataset

```

## MCP server setup

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

```

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/7AQKg3rnunJOqCBIc/builds/PnHUMOyVnMOkvbdpX/openapi.json
