# Vinted Scraper (`mlg14/vinted-scraper`) Actor

Search public Vinted catalog listings by keyword or filtered URL. Export asking prices, brands, sizes, condition, photos, buyer protection totals, and source links with search attribution.

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

## Pricing

$1.00 / 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.

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 Scraper

Search public Vinted catalog listings by keyword or filtered catalog URL. Export Vinted asking prices, brands, sizes, condition labels, main photos, and product links to structured JSON, CSV, or Excel for resale research and repeatable market checks. This scraper reads the catalog that a visitor can see without signing in; it does not open each item page.

The input supports multiple searches in one run. Choose a country for keyword searches, or paste catalog URLs that already contain category, brand, size, price, and sort filters. Each saved row includes the search that produced it and a UTC collection time, so overlapping queries and later snapshots are easier to compare.

### What data can you extract from Vinted?

One dataset row represents one listing found on a public catalog page. The following fields are flat and keep the same names across markets. A null value means that the catalog did not show that detail; it does not mean zero or an empty seller account.

| Field | Description | Example |
| --- | --- | --- |
| `id` | Public listing identifier, useful for matching snapshots. | `10141260203` |
| `title` | Listing title displayed with the catalog card. | `Nike polo` |
| `url` | Direct public item link without tracking parameters. | `https://www.vinted.com/items/10141260203-nike-polo` |
| `price` | Asking price as a number. | `5` |
| `currency` | Currency code for the catalog country. | `USD` |
| `brand` | Brand label displayed in the card. | `Nike` |
| `size` | Public size label, if the card has one. | `L` |
| `condition` | Public condition label in the site's language. | `Very good` |
| `imageUrl` | URL of the main card image. | `https://images1.vinted.net/...` |
| `photoUrls` | List of card image URLs, normally containing the main image only. | `[...]` |
| `totalPrice` | Displayed amount including buyer protection, before shipping. | `5.95` |
| `serviceFee` | Difference between `totalPrice` and `price`. | `0.95` |
| `sellerId` | Seller identifier if the catalog exposes it. | `null` |
| `sellerName` | Seller name if the catalog exposes it. | `null` |
| `sellerUrl` | Seller profile link if the catalog exposes it. | `null` |
| `favouriteCount` | Favourite count if present on the card. | `null` |
| `viewCount` | View count if present on the card. | `null` |
| `contentSource` | Source page type. | `catalog` |
| `searchTerm` | Keyword that produced the row; null for a supplied URL. | `nike` |
| `searchUrl` | Catalog URL used for this search. | `https://www.vinted.com/catalog?search_text=nike&order=newest_first` |
| `siteCountry` | Country code of the catalog domain. | `us` |
| `collectedAt` | UTC timestamp when the run collected the page. | `2026-09-26T13:35:21.316839+00:00` |

Catalog cards currently expose the main image rather than a full photo gallery. Seller identity, favourites, and views are retained as nullable fields for a stable export shape, but the catalog pages observed during validation did not supply their values. The scraper does not guess them or visit every listing to fill them. A full item detail export requires a different collection method and may face different access limits.

`price` is an asking price, not a confirmed sale price. `totalPrice` is the amount displayed next to the card with buyer protection included; it does not establish the final checkout amount. Shipping, taxes, promotions, and local rules can change the amount a buyer eventually pays. Keep `currency` with every value, especially when combining countries.

### How to scrape Vinted

1. Enter one or more keywords, paste one or more filtered catalog URLs, or use both. Each entry is a separate search.
2. For keywords, select the country and optional price bounds. A pasted URL keeps the filters and country already in that URL.
3. Set `maxProducts` to the most listings you want from each search. Start with 40 or 50 to inspect the market before requesting many pages.
4. Run the actor and open its dataset. Inspect several item links and the currency before using prices in a report.
5. Download the dataset in JSON, CSV, or Excel. Repeat the same input later to compare snapshots by `id`.

For a narrow category, set the filters on the Vinted catalog page and copy its URL. A URL can encode category, brand, size, price, and order settings that would otherwise require several input fields. Direct item and seller profile URLs are outside this actor's search mode.

### Input

Supply at least one keyword or catalog URL. An empty input fails with a clear message. The default country applies to keywords only; it never changes a supplied URL. The result limit is per search, while `maxItems` caps the dataset as a whole.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `startUrls` | List of URL objects | Empty | Public HTTPS Vinted catalog URLs. Each is searched independently. |
| `keywords` | List of strings | Empty | Terms to search on the selected country site. |
| `country` | Country code | `us` | Market for keyword searches. There are 26 selectable country domains. |
| `minPrice` | Number | Empty | Minimum asking price for keywords in the market currency. |
| `maxPrice` | Number | Empty | Maximum asking price for keywords in the market currency. |
| `sort` | Choice | `newest_first` | Sort keyword results by newest, relevance, or price. |
| `maxProducts` | Integer | `50` | Maximum products per URL or keyword, from 1 to 960. |
| `maxItems` | Integer | `0` | Total saved row cap across searches; zero adds no total cap. |

For example, this input searches for denim jackets in the United States and runs a second search using a French catalog URL. The French URL retains its own price and sort settings. `maxProducts` applies separately, so the maximum before cross-search deduplication is 80 rows:

```json
{
  "keywords": ["denim jacket"],
  "country": "us",
  "minPrice": 10,
  "maxPrice": 50,
  "sort": "newest_first",
  "startUrls": [
    {"url": "https://www.vinted.fr/catalog?search_text=veste%20en%20jean&price_to=40&order=newest_first"}
  ],
  "maxProducts": 40,
  "maxItems": 80
}
```

The country option covers Austria, Belgium, Croatia, Czechia, Denmark, Estonia, Finland, France, Germany, Greece, Hungary, Ireland, Italy, Latvia, Lithuania, Luxembourg, the Netherlands, Poland, Portugal, Romania, Slovakia, Slovenia, Spain, Sweden, the United Kingdom, and the United States. A selectable domain does not guarantee that every regional catalog is reachable from every run. Test a small search for a new market before relying on it for a large workflow.

### Output example

This row came from the successful 40-item United States validation run on 26 September 2026. The link and image address are source data and may change or disappear as the listing changes. The example includes nulls for fields that the catalog did not expose.

```json
{
  "id": "10141260203",
  "title": "Nike polo",
  "url": "https://www.vinted.com/items/10141260203-nike-polo",
  "price": 5,
  "currency": "USD",
  "brand": "Nike",
  "size": "L",
  "condition": "Very good",
  "imageUrl": "https://images1.vinted.net/t/04_01b5a_q62yJVhNVJbhssN3Jznz6dMb/310x430/0653942f.webp?s=003f8eaa28bb0a373f2ebdf56f38472890ce6ac2",
  "photoUrls": ["https://images1.vinted.net/t/04_01b5a_q62yJVhNVJbhssN3Jznz6dMb/310x430/0653942f.webp?s=003f8eaa28bb0a373f2ebdf56f38472890ce6ac2"],
  "totalPrice": 5.95,
  "serviceFee": 0.95,
  "sellerId": null,
  "sellerName": null,
  "sellerUrl": null,
  "favouriteCount": null,
  "viewCount": null,
  "contentSource": "catalog",
  "searchTerm": "nike",
  "searchUrl": "https://www.vinted.com/catalog?search_text=nike&order=newest_first",
  "siteCountry": "us",
  "collectedAt": "2026-09-26T13:35:21.316839+00:00"
}
```

The validation input requested 40 products. The run completed with 40 rows, and every row had an ID, title, URL, asking price, currency, brand, condition, main image URL, and country code. That result confirms the small public search used here; it does not guarantee the same fill rates for every category, filter, or regional site.

### Use cases

- **Resale price research:** Compare current asking prices for a specific product type, brand, size, and condition before choosing a listing price. Keep the source URLs so an analyst can inspect outliers.
- **New-listing review:** Use newest-first sorting, run the same search on a schedule, and compare the returned IDs with the previous run. A new ID indicates a newly observed listing, not necessarily a newly created one.
- **Category sampling:** Collect a bounded sample from a catalog URL with selected categories and brands. Group the rows by condition and size to see the asking-price range for comparable items.
- **Cross-market comparison:** Run the same term in more than one country and preserve `currency` and `siteCountry`. Convert currencies separately before comparing numeric price levels.
- **Inventory watchlists:** Keep a set of relevant item IDs and revisit the public links when a buyer or researcher needs to inspect a listing. A missing row in a later search alone does not prove the product sold.
- **Price-change snapshots:** Store results from repeated runs, join by ID, and compare asking prices over time. Record `collectedAt` so changes have a clear observation date.

### How much does it cost to scrape Vinted?

The configured result-event price is **$1 per 1,000 saved listings**, or **$0.001 per saved row**. The total is tied to rows written to the dataset, not the requested `maxProducts` value. A search with fewer available or reachable listings produces fewer saved rows. Check the run's cost and any applicable account or network charges before increasing volume.

| Saved listings | Result-event calculation | Result-event charge |
| ---: | ---: | ---: |
| 50 | `50 × $0.001` | `$0.05` |
| 1,000 | `1,000 × $0.001` | `$1.00` |
| 5,000 | `5,000 × $0.001` | `$5.00` |

Because one search is limited to at most 960 requested products, the larger examples require multiple distinct searches. Overlapping searches are deduplicated by listing ID within a run. Set `maxItems` when you need a firm row cap across all searches. The 40-row validation run would correspond to $0.04 in result events at this rate; its recorded runtime usage was separate from that simple row calculation.

### Tips for best results

Use a specific keyword or a filtered catalog URL when you need a comparable set. Broad terms can mix unrelated product types, sizes, and conditions, so a raw average of all returned prices may be misleading. Review the titles and source links in a small sample before collecting more pages.

The catalog displays up to 96 cards per page. `maxProducts` is an upper bound, and the actor stops when it reaches the limit, sees no cards, reaches the page boundary, or runs out of distinct listings. On the tested United States search, pages 1 through 9 returned cards; a page 10 browser probe did not return cards. Other searches may have fewer pages, and the site's behavior can change. For a larger collection, split the market into distinct category, brand, size, condition, or price ranges using multiple catalog URLs. This produces narrower, more useful comparisons while working around any single search's visible page window.

When supplying several searches, use `searchTerm` and `searchUrl` to understand where each row came from. A repeated item is saved once per run, with the attribution of the first search that found it. If you need separate attribution for every overlap, run those searches separately. A supplied URL with a `page` parameter starts at that page and moves forward while public pagination permits it.

Start with a moderate `maxProducts` value, especially for a market you have not checked before. Public access sometimes changes by request or region. The actor first requests the page normally, then uses a browser session if access is blocked. If a search still cannot load, the run logs the stopped search and continues with other searches. Inspect the dataset count and run log together: a completed run with fewer rows can reflect access limits, sparse results, duplicate listings, or a changed page layout.

### Limits

This is a public **catalog listing** scraper. It does not sign in, message sellers, purchase items, retrieve private information, or scrape item detail pages. It cannot establish that a product was sold. The data is a snapshot of visible asking prices at collection time, and links can become unavailable afterward.

The page may omit seller identity, favourite counts, view counts, additional photos, and full descriptions. Those fields remain null rather than being estimated. `serviceFee` is calculated only when both displayed price amounts are present; shipping is not included in the catalog total. Localized size and condition labels are preserved as displayed instead of translated, so group them carefully across countries.

Catalog access can be blocked or throttled. The actor can retry through a browser session and rotate its network session, but no method guarantees access. A country option is a domain choice rather than a promise of inventory in that region. Pagination visibility also varies; the maximum input limit does not guarantee that many products exist or can be reached.

### Automated workflows

A scheduled workflow can run a small filtered search each morning, read the dataset, compare IDs with the previous snapshot, and send an alert for newly observed items. Another workflow can collect a weekly category sample, retain the link and currency for every row, and calculate price distributions by size and condition. In both cases, the workflow should check the completed run's row count before acting on the data.

For a one-off request, a clear instruction is: “Find up to 40 current denim jacket listings in France under 40 euros; return title, asking price, condition, and source link.” For monitoring: “Run this filtered catalog URL daily, compare listing IDs with yesterday's dataset, and report newly observed IDs.” These examples rely on public catalog data and do not imply a live purchase or a confirmed sale.

### FAQ

#### Is scraping these listings legal?

Collect only publicly visible data that you have a lawful reason to use, respect the site's terms and access restrictions, and follow applicable privacy rules. Public visibility does not make seller details unrestricted for every purpose. This actor does not access accounts or private messages. If you publish derived data, review whether showing titles, images, and links is appropriate for that use.

#### Do I need an account or my own proxy?

No site login is needed for the public catalog mode. The actor handles its network access during a run. Access can still fail if the site challenges or blocks a session, so review the run log when the row count is unexpectedly low.

#### How fast is a run?

A small search usually needs one page request. The 40-row validation run completed in roughly 15 seconds after startup. Larger searches need more pages and can take longer, especially if a browser fallback or network rotation is required. Speed is not fixed across regions or days.

#### Can I filter by category, brand, size, or condition?

Yes. Set the filters on a public Vinted catalog page and pass its URL in `startUrls`. The keyword fields directly support country, price bounds, and sorting. The actor does not override filters embedded in a supplied URL.

#### Can I schedule price monitoring?

Yes. Schedule repeated runs with the same input, store each dataset, and compare rows by `id` and `collectedAt`. The actor does not maintain a built-in price history or decide whether a missing listing sold. Treat missing or changed results as observations to verify.

#### Can I export to a spreadsheet?

Yes. Export the dataset to CSV or Excel. The output uses flat fields for price and listing attributes; `photoUrls` is the one list field, and a spreadsheet may show it as text. JSON preserves its list structure.

#### Why is a field empty, or why are there fewer items than requested?

The catalog does not show every item detail. Seller identifiers, counts, and additional photos may be unavailable. The requested item count is a maximum; fewer rows can result from sparse inventory, duplicate IDs, pagination limits, or access failures. Check the search URL, dataset, and run log before repeating a large request.

### Integrations

Use the run and dataset endpoints to start searches and retrieve results in an application or automation workflow. Webhooks can react to run completion; schedules can create regular snapshots; spreadsheet exports can feed reports. For larger datasets, retrieve rows in pages and keep the run ID, input, and collection time with the export. A downstream workflow should verify row counts and currencies before issuing alerts or calculating aggregate prices.

### Support

Open an issue on the Issues tab; we reply within 24h and add fields on request. Include the run ID, a sample public catalog URL or keyword, the selected country, the observed row count, and the expected result. Do not include account credentials or private seller information.

# Actor input Schema

## `startUrls` (type: `array`):

Filtered HTTPS catalog URLs. Each URL is a separate search; its filters and country take precedence.

## `keywords` (type: `array`):

Search each keyword separately in the selected country.

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

Country site used for keyword searches. Catalog URLs keep their own country.

## `minPrice` (type: `number`):

Lowest asking price for keyword searches, in the selected market currency.

## `maxPrice` (type: `number`):

Highest asking price for keyword searches, in the selected market currency.

## `sort` (type: `string`):

Listing order for keyword searches. Catalog URLs keep their own order.

## `maxProducts` (type: `integer`):

Maximum listings collected from each keyword or URL, up to the public page limit.

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

Hard cap on saved listings across all searches. Zero means no additional total cap.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://www.vinted.com/catalog?search_text=nike&order=newest_first"
    }
  ],
  "keywords": [
    "nike"
  ],
  "country": "us",
  "sort": "newest_first",
  "maxProducts": 50,
  "maxItems": 0
}
```

# Actor output Schema

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

Collected catalog listings in the default dataset.

# 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 = {
    "startUrls": [
        {
            "url": "https://www.vinted.com/catalog?search_text=nike&order=newest_first"
        }
    ],
    "keywords": [
        "nike"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("mlg14/vinted-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 = {
    "startUrls": [{ "url": "https://www.vinted.com/catalog?search_text=nike&order=newest_first" }],
    "keywords": ["nike"],
}

# Run the Actor and wait for it to finish
run = client.actor("mlg14/vinted-scraper").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 '{
  "startUrls": [
    {
      "url": "https://www.vinted.com/catalog?search_text=nike&order=newest_first"
    }
  ],
  "keywords": [
    "nike"
  ]
}' |
apify call mlg14/vinted-scraper --silent --output-dataset

```

## MCP server setup

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

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/dqJlv2S3SAXz2QeHl/builds/cH7S8Ggbw1Ph1ZaPf/openapi.json
