# Vinted France–Germany Arbitrage Scanner (`automation-lab/vinted-cross-country-arbitrage-scanner`) Actor

Compare public Vinted France and Germany listings, normalize EUR prices, estimate fees and spreads, and rank likely cross-country resale opportunities.

- **URL**: https://apify.com/automation-lab/vinted-cross-country-arbitrage-scanner.md
- **Developed by:** [Stas Persiianenko](https://apify.com/automation-lab) (community)
- **Categories:** E-commerce
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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 France–Germany Arbitrage Scanner

Compare public Vinted France and Germany listings for the same product search, estimate sourcing costs, and rank possible cross-country resale opportunities.

This Vinted scraper is built for recurring resale research rather than a generic catalog dump. It collects current records from `vinted.fr` and `vinted.de`, finds likely comparable items, chooses the cheaper side as the source market, and returns transparent profit and match evidence.

> **Important:** each row compares two independent used-item listings. A high score is a research lead, not proof that the products are identical or that a resale will complete at the observed price.

### What does the Vinted arbitrage scanner do?

For one supplied query, the Actor:

1. opens anonymous public sessions on Vinted France and Vinted Germany;
2. loads structured listing records from both national catalogs;
3. normalizes EUR prices and listing fields;
4. excludes the same listing ID from being matched to itself;
5. scores possible pairs using title tokens, brand, size, and condition;
6. selects the cheaper listing as the buy side;
7. estimates buyer protection, shipping, resale fees, spread, profit, and ROI;
8. saves the strongest opportunities in profit-ranked order.

The default dataset is ready for JSON, CSV, Excel, Google Sheets, API, webhook, and scheduled-run workflows.

### Who is this Actor for?

- **Resellers** comparing sourcing prices between France and Germany.
- **Vintage and sneaker buyers** screening many listings before manual inspection.
- **Marketplace analysts** studying cross-country asking-price differences.
- **Ecommerce teams** feeding current Vinted comparisons into internal dashboards.
- **Automation builders** scheduling a repeatable Vinted API-style workflow without maintaining anonymous sessions.

### Why use this instead of two listing exports?

A pair of raw exports still leaves the hard work to you. This Actor adds a consistent comparison layer:

- a buy/sell direction for every pair;
- Vinted's exposed buyer-protection fee when available;
- configurable fallback fee and shipping assumptions;
- gross spread, estimated net profit, spread percentage, and ROI;
- a bounded confidence score and human-readable match reasons;
- direct source URLs and images for manual validation.

The Actor does **not** claim to identify guaranteed flips. It reduces the catalog to reviewable candidates.

### What Vinted data is returned?

| Field group | Included data |
| --- | --- |
| Ranking | `rank`, `query`, `matchConfidence`, `matchReasons` |
| Source listing | market, ID, title, brand, size, condition, price, URL, image |
| Comparable listing | market, ID, title, brand, size, condition, price, URL, image |
| Costs | buyer protection, shipping assumption, resale-fee estimate, total cost |
| Opportunity | gross spread, estimated net profit, ROI, spread percentage |
| Freshness | `scrapedAt` ISO timestamp |

Fields that Vinted omits can be `null`. Listing titles and conditions remain in their source language.

### How matching works

The scanner compares France records with Germany records and calculates a score from zero to one.

- Title-token overlap carries most of the score.
- An exact normalized brand adds evidence.
- An exact normalized size adds evidence.
- An exact localized condition adds evidence when the text is equal.
- Identical listing IDs are always rejected.

Use `minMatchConfidence` to control strictness. The default `0.3` is useful for discovery. Raise it for a shorter review list. Always open both URLs before acting on an opportunity: color, authenticity, defects, bundle contents, audience, and exact model can still differ.

### Getting started

1. Open the Actor in Apify Console.
2. Enter a real product or model in **Vinted search query**.
3. Keep 48 listings per country for a quick first comparison.
4. Set your realistic shipping and resale-fee assumptions.
5. Click **Start**.
6. Open **Arbitrage opportunities** in the dataset.
7. Sort or filter by profit, ROI, or confidence.
8. Inspect both source URLs before making a sourcing decision.

A good first input is:

```json
{
  "searchQuery": "nike dunk",
  "maxListingsPerCountry": 48,
  "maxOpportunities": 20,
  "minMatchConfidence": 0.3,
  "minNetProfitEur": 0
}
```

### Input parameters

| Parameter | Type | Default | Purpose |
| --- | --- | ---: | --- |
| `searchQuery` | string | required | Product, brand, or model searched on both markets |
| `maxListingsPerCountry` | integer | 48 | Records loaded from each catalog, 5–200 |
| `maxOpportunities` | integer | 20 | Ranked pairs saved, 1–100 |
| `sortBy` | string | `relevance` | Vinted catalog order before matching |
| `minMatchConfidence` | number | 0.3 | Minimum similarity score, 0.2–1 |
| `minNetProfitEur` | number | 0 | Minimum estimated net profit saved |
| `shippingCostEur` | number | 5 | Shipping assumption added to source cost |
| `buyerProtectionRate` | number | 0.05 | Fee-rate fallback when Vinted omits its fee |
| `buyerProtectionFixedEur` | number | 0.7 | Fixed fee fallback when Vinted omits its fee |
| `resaleFeeRate` | number | 0 | Optional fraction reserved from sell price |
| `proxyConfiguration` | object | direct | Optional Apify proxy configuration |

France and Germany are fixed product scope, not user-selectable aliases. Values are compared only when Vinted reports EUR.

### Output example

A real local run for `nike dunk` returned a record shaped like this:

```json
{
  "rank": 1,
  "query": "nike dunk",
  "buyMarket": "DE",
  "sellMarket": "FR",
  "buyListingId": "9616696920",
  "buyTitle": "Nike Dunk low",
  "buyBrand": "Nike",
  "buySize": "40",
  "buyPriceEur": 15,
  "buyBuyerProtectionEur": 1.45,
  "estimatedShippingEur": 5,
  "estimatedTotalCostEur": 21.45,
  "comparableSellPriceEur": 90,
  "grossSpreadEur": 75,
  "estimatedNetProfitEur": 68.55,
  "roiPercent": 319.58,
  "matchConfidence": 0.625,
  "matchReasons": [
    "title token overlap 50%",
    "same brand",
    "same size"
  ],
  "scrapedAt": "2026-08-09T14:51:15.845Z"
}
```

URLs and image URLs are also included in each dataset row. IDs and prices above illustrate the current output shape; live listings can change or disappear.

### How much does it cost to compare Vinted France and Germany?

The Actor uses pay-per-event pricing:

- **$0.021** for one `start` event per run;
- one `opportunity` event per saved comparison, with plan discounts.

At the BRONZE tier, an opportunity costs **$0.016**. That makes the event charge approximately:

| Saved opportunities | Approximate BRONZE charge (USD) |
| ---: | ---: |
| 1 | 0.037 |
| 5 | 0.101 |
| 10 | 0.181 |
| 25 | 0.421 |
| 100 | 1.621 |

FREE through DIAMOND opportunity prices are $0.0184, $0.016, $0.01248, $0.0096, $0.0064, and $0.00448. The start event remains $0.021. The active live prices appear in Apify Console before a run. `maxOpportunities` is the clearest output-cost control.

### Profit assumptions and limitations

The calculation is:

```text
estimated total cost
  = buy price + buyer protection + configured shipping

estimated net profit
  = comparable sell price - estimated total cost - estimated resale fee
```

The comparable price is a current asking price, not a confirmed sale price. The estimate excludes taxes, customs, currency conversion, returns, repairs, cleaning, authentication, packaging, travel, failed delivery, and the value of your time unless you incorporate them into the configurable rates.

Vinted's fee structure can vary by buyer, country, basket, promotion, and time. When the public listing payload includes a service fee, the Actor uses it. Otherwise it applies your percentage-plus-fixed fallback.

### Scheduling recurring sourcing research

Create an Apify schedule for repeated snapshots:

1. save one Task per product model;
2. run it daily or weekly;
3. send the dataset to a webhook, Sheets, or your database;
4. deduplicate on `buyListingId` and `sellListingId`;
5. alert only when profit and confidence cross your own thresholds.

The Actor itself does not maintain price history or send alerts. Apify schedules and integrations provide those workflow layers.

Useful recurring searches include model-specific footwear, designer bags, vintage denim, and collectible outerwear. Narrow product names generally match better than broad terms such as `shoes`.

### Export and integration options

Every run writes normal Apify dataset rows. You can:

- download JSON, CSV, XML, RSS, or Excel;
- connect Google Sheets through an Apify integration;
- trigger a webhook when a run succeeds;
- read results from the dataset API;
- transform rows in Make, Zapier, n8n, or your own code;
- feed reviewed candidates to inventory-planning tools.

Use listing IDs for deduplication and `scrapedAt` for snapshot time. Do not assume rank is stable across runs because live inventories change.

### Run through the Apify API with cURL

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/automation-lab~vinted-cross-country-arbitrage-scanner/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "searchQuery": "adidas samba",
    "maxListingsPerCountry": 60,
    "maxOpportunities": 25,
    "shippingCostEur": 5
  }'
```

Store tokens in environment variables. Never commit them to source code.

### Run through JavaScript

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor(
  'automation-lab/vinted-cross-country-arbitrage-scanner'
).call({
  searchQuery: 'levis 501',
  maxListingsPerCountry: 96,
  maxOpportunities: 30,
  minNetProfitEur: 5,
  shippingCostEur: 6.5,
  resaleFeeRate: 0.05,
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

### Run through Python

```python
import os
from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor(
    "automation-lab/vinted-cross-country-arbitrage-scanner"
).call(run_input={
    "searchQuery": "nike dunk",
    "maxListingsPerCountry": 48,
    "maxOpportunities": 20,
    "minMatchConfidence": 0.4,
})

items = client.dataset(run["defaultDatasetId"]).list_items().items
print(items)
```

### Use with Apify MCP and AI agents

Add this Actor to Claude Code:

```bash
claude mcp add --transport http apify \
  "https://mcp.apify.com?tools=automation-lab/vinted-cross-country-arbitrage-scanner"
```

#### Claude Desktop setup

Claude Desktop can use this MCP configuration:

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com?tools=automation-lab/vinted-cross-country-arbitrage-scanner"
    }
  }
}
```

#### Cursor setup

In Cursor, open **Settings → Tools & MCP → New MCP Server** and use the same `https://mcp.apify.com?tools=automation-lab/vinted-cross-country-arbitrage-scanner` URL.

#### VS Code setup

In VS Code, add the same HTTP endpoint to your workspace MCP server configuration and start the `apify` server from the MCP tools view.

Example prompts:

- “Compare Nike Dunk listings on Vinted France and Germany and show opportunities above €15 estimated profit.”
- “Run a Levi's 501 sourcing comparison with €6.50 shipping and a 5% resale reserve.”
- “Return the five strongest Adidas Samba matches and include both listing URLs.”

An AI agent should still treat match and profit values as screening evidence and ask a human to verify the listings.

### Proxy, retry, and failure behavior

The default route uses anonymous, isolated sessions and structured catalog requests without a proxy. If all bounded direct sessions are blocked, the Actor retries through an Apify residential proxy located in the matching market.

You can provide `proxyConfiguration` to control proxy use explicitly. A configured proxy is not silently replaced if it fails.

The run fails rather than returning a misleading empty success when either market cannot provide useful records. A valid query with no matching pairs can finish successfully with an empty dataset.

### Responsible use and legality

This Actor reads public marketplace listing records. Use it only for lawful research and automation that respects Vinted's terms, applicable database rights, privacy rules, consumer-protection law, tax obligations, and reasonable request volumes.

Do not use the output to harass sellers, evade platform safeguards, misrepresent products, automate prohibited purchases, or make decisions about people. Seller profile identifiers are not included in the result dataset.

You are responsible for verifying product authenticity, listing rights, shipping eligibility, and legal resale requirements in each country.

### Troubleshooting

#### Why are there no opportunities?

The query may have too few comparable products, your confidence threshold may be high, or your fee assumptions may make every pair unprofitable. Try a specific model with more inventory, increase `maxListingsPerCountry`, lower `minMatchConfidence` carefully, or inspect a negative `minNetProfitEur` during research.

#### Why do two matched items look different?

Text matching cannot see every variant or defect. Raise `minMatchConfidence`, prefer model-specific queries, and verify title, size, condition, photos, and description at both URLs.

#### Why did the Actor fail before producing rows?

Vinted may have challenged both direct and residential sessions, one catalog may be unavailable, or the input may be invalid. Check the run log for the market and retry reason. Repeated blind retries are not recommended.

#### Does this scrape Vinted customer service data?

No. The Actor only compares public product listings. It does not retrieve customer-service contacts, messages, accounts, private profiles, sold history, or support cases.

### FAQ

#### Does this guarantee profit?

No. It ranks current asking-price comparisons under your assumptions. Availability, sale price, fees, shipping, returns, condition, authenticity, and demand can change.

#### Are France and Germany prices converted?

Both supported catalogs report EUR, so no foreign-exchange conversion is applied.

#### Can I compare other Vinted countries?

Not in this Actor. Its validated scope is France and Germany. Use [Vinted Scraper](https://apify.com/automation-lab/vinted-scraper) when you need independent listing exports from other supported regional domains.

#### Is a browser used for every request?

No. The production route bootstraps an anonymous session over HTTP and replays the structured catalog endpoint. This is faster and lighter than browser automation.

#### Can I monitor changes?

Yes, by saving this input as an Apify Task and scheduling it. Persist and compare listing-pair IDs in your downstream storage; this Actor does not keep cross-run history.

### Related automation-lab Actor

- [Vinted Scraper](https://apify.com/automation-lab/vinted-scraper) — export independent Vinted listing records across supported regional domains when you do not need France–Germany matching and profit ranking.

### Support

For reproducible questions, include the input, run URL, approximate timestamp, and the market named in the error. Do not post Apify tokens, private account data, or seller contact details.

# Actor input Schema

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

A product, brand, or model to compare on vinted.fr and vinted.de, such as Nike Dunk or Levi's 501.

## `maxListingsPerCountry` (type: `integer`):

Maximum public listings to load from each national Vinted catalog before matching.

## `maxOpportunities` (type: `integer`):

Maximum ranked listing pairs to save to the dataset.

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

How each Vinted catalog is ordered before matching.

## `minMatchConfidence` (type: `number`):

Minimum similarity score from 0.2 to 1. Higher values reduce loose comparisons but may return fewer pairs.

## `minNetProfitEur` (type: `number`):

Only save pairs whose estimated profit meets this amount after configured fees and shipping.

## `shippingCostEur` (type: `number`):

Shipping assumption added to each buy-side listing. Replace the default with your actual route cost.

## `buyerProtectionRate` (type: `number`):

Fallback percentage of the buy price used when Vinted does not expose a service fee for a listing.

## `buyerProtectionFixedEur` (type: `number`):

Fallback fixed amount added to the percentage fee when Vinted does not expose a service fee.

## `resaleFeeRate` (type: `number`):

Optional fraction of the comparable sell price reserved for marketplace or payment fees; 0 means no extra resale fee.

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

Optional Apify proxy settings. Direct session-based requests are tried by default; a country-matched residential fallback is used only if direct access is blocked.

## Actor input object example

```json
{
  "searchQuery": "nike dunk",
  "maxListingsPerCountry": 20,
  "maxOpportunities": 20,
  "sortBy": "relevance",
  "minMatchConfidence": 0.3,
  "minNetProfitEur": 0,
  "shippingCostEur": 5,
  "buyerProtectionRate": 0.05,
  "buyerProtectionFixedEur": 0.7,
  "resaleFeeRate": 0
}
```

# Actor output Schema

## `dataset` (type: `string`):

Default dataset containing all ranked listing comparisons

# 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 dunk",
    "maxListingsPerCountry": 20,
    "maxOpportunities": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation-lab/vinted-cross-country-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 dunk",
    "maxListingsPerCountry": 20,
    "maxOpportunities": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("automation-lab/vinted-cross-country-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 dunk",
  "maxListingsPerCountry": 20,
  "maxOpportunities": 20
}' |
apify call automation-lab/vinted-cross-country-arbitrage-scanner --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automation-lab/vinted-cross-country-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/GchOHgmjCRmToc2DW/builds/uTOdPPNCOJIsx1BSQ/openapi.json
