# Vestiaire Collective Scraper: New Listings, Price Drops & Sold (`accountable_eel/vestiaire-listing-lookup`) Actor

Vestiaire Collective scraper by keyword: title, brand, price, size, image and link per luxury resale listing, no login. Monitoring returns new listings, price drops, and gone or confirmed-sold ones with a running sell-through rate. Pay per listing returned; quiet runs are free.

- **URL**: https://apify.com/accountable\_eel/vestiaire-listing-lookup.md
- **Developed by:** [Adrian Voss](https://apify.com/accountable_eel) (community)
- **Categories:** E-commerce, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.42 / 1,000 listing returneds

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?

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

## Vestiaire Collective Scraper: New Listings, Price Drops & Sold

**Watch a search on Vestiaire Collective, get only what's new.** A Vestiaire Collective alert for
new listings, price drops and confirmed sold items since your last run, charged per new row, free
on quiet days. Schedule it and send it to Discord, Slack, n8n, or to Google Sheets.

This actor searches Vestiaire Collective the way its own search bar does: type a keyword (a brand,
a model, a category) and get one clean row per listing with the title, brand, price, a euro
conversion, size, the main photo and the link. Turn on monitoring and each run returns only the
listings that appeared, or got cheaper, since the previous run, plus, if you ask, the ones that
disappeared — some confirmed sold by Vestiaire's own data, the rest presumed: a luxury resale price
tracker and sold items tracker with a running sell-through rate.

### How to search

| What you type in `searches` | What you get | Example |
|---|---|---|
| Brand + model | The tightest match — this is how Vestiaire's own search bar works too | `chanel classic flap bag`, `hermes birkin 30` |
| A single brand or category word | Everything under it — can be thousands of listings; only the newest `maxListingsPerSearch` come back | `chanel` |
| `brand: <name>` | A brand-only browse, no keywords at all | `brand: Chanel` |

**Be specific: model names narrow results.** A one-word brand search returns whatever Vestiaire
ranks first for it, not everything it has; add the model, size, or a distinctive word from the
listing title to get the pieces you actually want.

**Finding one exact item.** Search the model name as specifically as you can (`hermes birkin 30 togo`
beats `hermes bag`), then use `includeKeywords` to require a size, colour, or other detail, or
`maxListingsPerSearch` set low with `sort: "relevance"` to see only the closest matches.

**Keeping results to one brand.** Set **Brands** (below the search settings) to a list of brand
names — every line in `searches` is then restricted to those brands, resolved against Vestiaire's
own brand list live (exact name, case-insensitive). A name Vestiaire doesn't recognize costs nothing
and comes back as one row naming the closest matches instead of silently returning nothing or
guessing an id. `brand: Chanel` as a search line does the same thing for just that one line, without
touching the Brands setting.

Vestiaire has no structured "condition" field on its public search — condition only appears as free
text inside the listing description, so this actor doesn't invent a condition column either way.

### How to filter

All filters are optional and off by default — leave them all empty and you get exactly what this
actor has always returned.

**Only sold pieces, by seller country:**

```json
{ "searches": ["chanel classic flap bag"], "availability": "sold", "countries": ["FR", "IT"] }
```

Keeps only listings Vestiaire's own `sold` flag has marked sold, and only where the seller's
country (the `sellerCountry`/`country` columns) is France or Italy. Case-insensitive, ISO 3166-1
alpha-2.

**A price band on one brand, title-filtered:**

```json
{ "searches": ["birkin"], "brands": ["Hermes"], "minPrice": 8000, "maxPrice": 20000, "excludeKeywords": ["mini"] }
```

Restricts every search line to Hermes (resolved against Vestiaire's own brand list, exact name,
case-insensitive), keeps only listings priced $8,000–$20,000, and drops anything whose title or
brand mentions "mini".

**Available only, must mention a size:**

```json
{ "searches": ["chanel bag"], "availability": "available", "includeKeywords": ["30"] }
```

Drops sold listings and keeps only titles or brands mentioning "30" (useful for a bag size like
"Birkin 30"). `includeKeywords`/`excludeKeywords` match the listing's title **and** brand, not the
title alone.

There is no `conditions` filter on this actor: Vestiaire's public search API has no structured
condition field anywhere (see "How to search" above), so there is nothing to filter on and no
`condition` column either.

### Who it's for

- **Resellers and luxury flippers** watching for underpriced pieces from a specific brand or model
  before anyone else grabs them.
- **Sniper-bot and alert-bot builders** who want a stable JSON row per listing instead of
  maintaining their own scraper against a site with no public API docs.
- **Price researchers** tracking what a bag, watch or jacket really sells for on the resale market,
  and how fast it sells.
- **Sourcing teams** building a shortlist of listings across many searches at once, exported
  straight to Google Sheets.

### Why this one

- **A real keyword search, not a personalized feed.** Vestiaire Collective's public search API
  silently ignores the wrong field name and falls back to a "you may also like" feed instead of
  erroring — this actor uses the real one (confirmed live, not guessed), so results actually match
  what you typed.
- **Monitoring built in, not bolted on.** `deltaMode` remembers what each watchlist has seen. New
  listings and price drops (with your own minimum drop %) come back; everything else is removed
  before billing. A quiet run returns one summary row and costs only the start fee.
- **Two sold signals, not one.** A tracked listing that reappears with Vestiaire's own `sold` flag
  set is reported `soldConfirmed: true` — no guessing. A listing that simply vanishes gets
  `gonePresumedSold` only when it was priced at or under the market and stayed up more than one run,
  the same honest heuristic already shipped on this portfolio's Vinted and Marktplaats actors.
- **No login, no personal data.** Vestiaire Collective's own site is Cloudflare-gated even to a
  plain request; this actor reaches its public search API without ever needing an account, a
  cookie, or a CAPTCHA. Seller identity (name, id) is never returned — only the seller's country.

### What you get

By default each listing is its own row. Every row also repeats the search it came from.

| Field | What it is |
|---|---|
| `listingId`, `url`, `title` | Vestiaire's listing ID, the listing link and its title |
| `brand`, `size` | As shown on Vestiaire Collective |
| `price`, `currency` | The asking price in the search country's currency |
| `priceEur` | `price` converted to euros at the ECB daily reference rate |
| `sold` | Vestiaire's own flag for this listing, as returned this run |
| `createdAt` | Vestiaire's own listing timestamp |
| `imageUrl` | Link to the main photo (links only, nothing is downloaded) |
| `sellerCountry` | The seller's country — no seller name, id or other personal data is ever returned |
| `listingStatus`, `country`, `listedAt` | This portfolio's cross-marketplace standard names for `sold`/`sellerCountry`/`createdAt` above — same values, added alongside (not instead of) the existing columns. `listingStatus` is `"available"` or `"sold"` (Vestiaire has no separate reserved state) |
| `isNew`, `changeType`, `firstSeenAt` | Monitoring: `new`, `price-drop`, `gone`, `sold` or `seen`, and when this watchlist first saw it |
| `lastSeenAt`, `daysListed` | Monitoring: the last run that saw the listing, and days between first and last sighting |
| `previousPrice`, `priceDropPct` | Monitoring: the price last seen and the drop in %, on price-drop rows |
| `lastPrice`, `gonePresumedSold` | Monitoring, on `gone` rows: the last price seen, and the presumed-sold flag (see below) |
| `soldConfirmed` | Monitoring, on `sold` rows: always `true` — Vestiaire's own data confirmed the sale |
| `sellThroughRate` | Monitoring: (gone + sold) listings / listings ever tracked by this watchlist, 0 to 1 |
| `medianPriceEur` | Monitoring: median euro price of everything the run read for this search |
| `searchQuery`, `listingCount`, `totalAvailable`, `truncated` | The search summary |
| `newCount`, `priceDropCount`, `goneCount`, `soldCount`, `monitorStatus` | Monitoring summary: `NEW_ROWS`, `NO_NEW_ROWS` or `SEEDED` |

Not available anywhere on Vestiaire Collective's public search, so not in this actor: a structured
numeric condition (it only exists as free text inside the full listing description, and as an
internal filter id — this actor doesn't invent a column from either), and the seller's name or id.

### Monitoring: new listings and price drops

The recipe most buyers use:

1. Put your search in `searches` (keywords like "chanel bag" or "rolex submariner"), and turn on
   **Only return results that are new, cheaper, gone or sold since the last run** (`deltaMode`).
2. Optionally turn on **Seed silently** so the first run remembers today's listings without sending
   them all to your webhook.
3. Save it as a Task and add a **Schedule** in Apify.
4. Add a webhook or integration on the Task for **Run succeeded**: Discord or Slack webhook, an n8n
   or Make automation, a Google Sheets append, or an HTTP call to your own bot.

```json
{
  "searches": ["chanel bag"],
  "country": "US",
  "maxListingsPerSearch": 120,
  "deltaMode": true,
  "skipFirstRun": true,
  "alertOnNew": true,
  "alertOnPriceDrop": true,
  "minPriceDropPct": 10
}
```

How it decides:

- **New** means this watchlist has never returned that listing ID before.
- **Price drop** means the price is at least `minPriceDropPct` below the price last seen. The
  remembered price updates every run, so a second cut is measured from the latest price.
- Unchanged listings are removed before you are billed. A run with nothing new returns one summary
  row with `monitorStatus: NO_NEW_ROWS`, `listingCount: 0`, and costs only the start fee.
- Monitoring always searches newest-first, regardless of the **Sort order** setting — that's the
  only sort Vestiaire Collective's API is confirmed to return in a genuine, ordered sequence.
- Each watchlist remembers up to 5,000 listings per search, oldest forgotten first, and forgets any
  listing it has not seen for 30 days.
- The watchlist name is derived from your country, price and keyword filters, so two schedules with
  different filters never share a memory. Set `deltaName` to choose your own.

**The window limitation.** A run only sees the newest `maxListingsPerSearch` listings (120 by
default). A listing that drops in price after it has slid out of that window is not seen again. For
price-drop alerts keep searches narrow (brand plus model) or raise `maxListingsPerSearch`.

### Sold signal and sell-through

Turn on **Alert on gone/sold listings** (`alertOnGone`) next to `deltaMode` and a watchlist also
returns the listings it saw on its previous run that have since left the search — split honestly
into two kinds, not lumped into one guess.

**The honesty note.** Vestiaire Collective does not always confirm a sale on the way out: a listing
that sells is often just gone from search, exactly like a listing that was deleted, reserved, or
edited so it no longer matches your filters. This actor only ever claims a confirmed sale when
Vestiaire's own data says so:

- **`changeType: "sold"`, `soldConfirmed: true`** — a listing this watchlist was tracking came back
  in the same search response with Vestiaire's own `sold` flag set. This is a fact, not a guess: no
  extra request, no heuristic.
- **`changeType: "gone"`, `gonePresumedSold`** — a tracked listing vanished from the search entirely,
  with no such proof. `gonePresumedSold: true` only when its last price was at or under this run's
  median and it had been seen on 2 or more runs in a row — the same pattern of "cheap, sat for a
  while, then vanished" this portfolio's Vinted and Marktplaats actors already use. Otherwise it is
  only `gone`: withdrawn, edited out of your filters, or paused, as often as sold.
- **`sellThroughRate`** is (gone + sold) listings / listings ever tracked, over the watchlist's
  lifetime. It is on every row (and the summary row of a quiet run), and counts both kinds even when
  `alertOnGone` is off.
- **`medianPriceEur`** is the median euro price of everything the run read for the search, not just
  the rows returned.

**How a run decides a listing is gone, not just out of view.** A run only reads the newest
`maxListingsPerSearch` listings. The actor remembers each listing's position in Vestiaire's
newest-first feed (`sortBy: "recency"`, confirmed live to be a genuine, ordered sort keyed on
Vestiaire's own listing timestamp), counts the new arrivals above it, and calls a missing listing
gone or sold only if it should still sit inside the window (with a margin of 2 places or 5%,
whichever is larger). Listings near the bottom edge of the window are therefore never called
gone/sold, so give a sold-signal watchlist some headroom. No check is made on a watchlist's first
run, or on a run Vestiaire refused partway.

A gone or sold listing is reported once. If it reappears later (a cancelled sale, for example), it
is quietly taken back off the sell-through count. Gone/sold rows are billed as ordinary `listing`
rows, at the same price, only when `alertOnGone` is on; no extra request is ever made for them.

```json
{
  "searches": ["chanel bag", "rolex submariner"],
  "maxListingsPerSearch": 200,
  "deltaMode": true,
  "deltaName": "sell-through-luxury",
  "alertOnNew": false,
  "alertOnPriceDrop": false,
  "alertOnGone": true
}
```

Scheduled daily, that returns only the listings that went, split into confirmed sold and presumed,
with `daysListed` and the running `sellThroughRate`: a Vestiaire Collective sold items tracker for a
brand list, in a Google Sheet.

### Price

- **Listing returned**: $4.5 per 1,000 listings

Plus a $0.00005 start fee per run. Each event above is billed independently, only when it actually returns data — misses (`found:false`) are never charged.

- **Listings:** billed per listing returned, plus a small start fee per run (see the price above,
  set once this actor is published — this is a private, unpriced build as of this README).
- **Gone/sold listings** (with "Alert on gone/sold listings" on) are ordinary `listing` rows at the
  same price. No separate event, no extra request.
- A monitoring run with nothing new costs the start fee only. A search that returns nothing is never
  billed.

### How to use

1. **In the Apify Console.** Open the actor page and click **Start** — the `searches` field is already pre-filled with a working example. Results land in the run's dataset as soon as each item is found.
2. **Via the API.** Call it directly with a POST request — no Console needed once you have an API token:
   ```bash
   curl "https://api.apify.com/v2/acts/accountable_eel~vestiaire-listing-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \
     -X POST \
     -H "Content-Type: application/json" \
     -d '{"searches":["chanel bag"]}'
   ```
3. **On a schedule.** Save this actor as an Apify **Task** with the input you want, then add a **Schedule** (hourly, daily, weekly) so it runs on its own — no server of your own required.

Tips:

- Keep a search to a brand plus model ("chanel classic flap", "rolex submariner") for the cleanest
  price-drop and sold-signal tracking — a one-word brand search can have thousands of matches, and
  only the newest `maxListingsPerSearch` are ever seen.
- `includeKeywords` and `excludeKeywords` match the title and brand before billing.
- Only United States (`country: "US"`) is live-verified end to end. The other country options are
  inferred from Vestiaire's own locale rules, not independently confirmed — treat them as best
  effort until proven otherwise.

### Input

```json
{
  "searches": [
    "chanel bag"
  ]
}
```

One search per line. Brand + model works best, the way Vestiaire's own search bar does — "chanel classic flap bag", "hermes birkin 30". Be specific: model names narrow results. Use "brand: Chanel" as a line to browse one brand with no keywords, or set Brands below to keep every search above to that brand. No login required. Accepted formats: chanel classic flap bag, hermes birkin 30, brand: Chanel, rolex submariner.

### Sample output

| query | found | status | searchQuery | listingCount | totalAvailable | truncated | newCount | priceDropCount | goneCount | soldCount | monitorStatus | sellThroughRate | medianPriceEur | listings | listingId | title | brand | price | currency | priceEur | size | sold | createdAt | imageUrl | url | sellerCountry | isNew | changeType | previousPrice | priceDropPct | firstSeenAt | lastSeenAt | daysListed | lastPrice | gonePresumedSold | soldConfirmed | listingStatus | country | listedAt | scrapedAt |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| chanel bag | true | OK | <search> | <listings returned> | <total matching on vestiaire collective> | <more results were available> | <new listings this run> | <price drops this run> | \<gone this run (no sale proof)> | <confirmed sold this run> | <monitor status> | \<sell-through rate (lifetime)> | \<median price this run (eur)> | \<all listings found (full list)> | <vestiaire collective listing id> | <title> | <brand> | <price> | <currency> | \<price in eur (ecb daily rate)> | <size> | \<sold (as returned this run)> | \<listed on (vestiaire's own timestamp)> | <image> | <listing link> | \<seller's country> | \<is this listing new?> | <change type> | <previous price> | \<price drop %> | <first seen on a run> | <last seen on a run> | <days listed> | \<last known price before it went gone/sold> | \<presumed sold (no confirmation)> | \<sold, confirmed by vestiaire's own sold flag> | \<status (available/sold)> | \<seller's country (iso alpha-2)> | \<listed on (alias of createdat)> | 1970-01-01T00:00:00.000Z |

### Use it from Clay, n8n, Make, or an AI agent

This actor runs synchronously over plain HTTP — call it directly from a script, a workflow tool, or an AI agent, no Apify Console needed once you have an API token.

```bash
curl "https://api.apify.com/v2/acts/accountable_eel~vestiaire-listing-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{"searches":["chanel bag"]}'
```

**n8n.** Add an HTTP Request node: Method `POST`, URL `https://api.apify.com/v2/acts/accountable_eel~vestiaire-listing-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>`, Body Content Type `JSON`, JSON Body `{"searches":["chanel bag"]}` (swap in an expression from an earlier node for a real value).

**Clay.** Add an "HTTP API" column: Method `POST`, URL `https://api.apify.com/v2/acts/accountable_eel~vestiaire-listing-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>`, Body `{"searches":["{{search}}"]}`, mapping the row's search into the `searches` array.

**MCP.** In Claude, Cursor, or any MCP client with the Apify MCP server, ask for "Vestiaire Collective Scraper | Apify" — the agent will find and run this actor.

**Discord or Slack alerts without code.** Schedule the monitoring Task, then add an integration: in
n8n or Make, trigger on "Apify: run succeeded", read the run's dataset, skip rows where
`listingCount` is 0, and post `title`, `brand`, `price`, `currency` and `url` to a Discord or Slack
webhook. Google Sheets users can append the same rows to a sheet for a running price log.

### vs. alternatives

| | What it costs | What you get | Trade-off |
|---|---|---|---|
| **This actor** | Pay per listing returned, quiet monitoring runs free | Keyword search, euro prices, built-in new-listing, price-drop, gone and confirmed-sold monitoring, sell-through rate | Search results only; no structured condition or seller name, because Vestiaire doesn't expose them publicly |
| Generic Vestiaire scrapers on the Apify Store | Varies | A dump of search results | Rarely combine monitoring with a keyword search, so you rebuild "what's new" yourself |
| Self-hosted sniper bots (GitHub) | Free, plus your server and time | Full control | Break whenever Vestiaire changes its site; you maintain proxies, parsing and dedupe |

### Data & privacy

This actor reads public search results that anyone can see without logging in. It doesn't log in,
solve CAPTCHAs, or reveal hidden seller details. Seller identity is never returned — no name, no id
— only the seller's country, which Vestiaire's own search response already includes per listing.
Photo links are returned as-is; nothing is downloaded. Not affiliated with Vestiaire Collective.

### FAQ

**Is this allowed?**
It collects the same public listing data your browser shows, for the searches you choose. It's
built for monitoring a search, not for copying the marketplace. Check that your use fits Vestiaire
Collective's terms and your local law.

**Why did my monitoring run return nothing?**
Nothing new or cheaper appeared since the last run. You get one row with `monitorStatus:
NO_NEW_ROWS` and are charged only the start fee. On the first run with "Seed silently" on, the
status is `SEEDED`.

**Does it find sold listings?**
Two ways. If Vestiaire's own data still shows the listing with its `sold` flag set, you get
`changeType: "sold"`, `soldConfirmed: true` — a fact, not a guess. If a tracked listing simply
disappears with no such proof, you get `changeType: "gone"`, with `gonePresumedSold: true` only when
it looks like a sale (cheap, up for 2+ runs). See "Sold signal and sell-through" above.

**What do I need to set up?**
Nothing. No Vestiaire account, no cookies, no proxy settings — the actor handles Vestiaire
Collective's anti-bot protection on its own.

**Can I get the seller's name?**
No. Only the seller's country is returned; names and ids are never collected.

**Can an AI agent call this?**
Yes, through the Apify MCP server or the API call shown above. Ask for "Vestiaire Collective
Scraper: New Listings, Price Drops & Sold".

### Related actors

- [Vinted Listing Lookup](https://apify.com/accountable_eel/vinted-listing-lookup): the same
  new-listing, price-drop and sold-signal monitoring for Vinted's 22 European country sites.
- [Marktplaats Listing Lookup](https://apify.com/accountable_eel/marktplaats-listing-lookup): the
  same monitoring shape for the Netherlands' largest classifieds marketplace.

# Actor input Schema

## `searches` (type: `array`):

One search per line. Brand + model works best, the way Vestiaire's own search bar does — "chanel classic flap bag", "hermes birkin 30". Be specific: model names narrow results. Use "brand: Chanel" as a line to browse one brand with no keywords, or set Brands below to keep every search above to that brand. No login required. Accepted formats: chanel classic flap bag, hermes birkin 30, brand: Chanel, rolex submariner. You're only charged for the ones we actually find — a miss costs nothing.

## `testRun` (type: `boolean`):

Turn this on to test your input on a small sample before running the full list. Turn it off to process everything.

## `onlyFound` (type: `boolean`):

Only keep rows where something was actually found. Misses are always free, whether or not you show them here.

## `includeKeywords` (type: `array`):

Optional. Only keep listings whose title or brand contains at least one of these words. Leave empty to keep everything.

## `excludeKeywords` (type: `array`):

Optional. Drop any listing whose title or brand contains one of these words.

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

Optional. Stop the run once this many results have been found — useful for a quick, cheap sample. Leave blank for no limit.

## `brands` (type: `array`):

Optional. Keep every search above to just these brands. Resolved against Vestiaire's own brand list live (exact name, case-insensitive — e.g. "Chanel", "Hermes"); an unrecognized name returns one free row naming the closest matches instead of guessing.

## `availability` (type: `string`):

Optional. Filter by Vestiaire's own "sold" flag, as returned this run. Vestiaire has no separate "reserved" state. Default keeps every listing.

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

Optional. ISO 3166-1 alpha-2 codes, case-insensitive (e.g. "US", "FR", "GB") — matched against the seller's country as returned by Vestiaire (the sellerCountry column). Leave empty to keep every country.

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

Drives currency and Vestiaire's own size-chart locale. Only United States is live-verified end to end; the others are inferred from Vestiaire's own locale rules and not independently confirmed.

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

Newest first is confirmed live to be a genuine, ordered sort (Vestiaire's own createdAt timestamp, strictly descending) — it's what monitoring uses regardless of this setting. Ignored (forced to Newest first) whenever monitoring is on.

## `maxListingsPerSearch` (type: `integer`):

Vestiaire returns up to 60 listings per request; this actor pages automatically above that, up to 1,000 per search. You pay per listing returned, so this is also your budget control.

## `minPrice` (type: `integer`):

Optional. Drop listings priced below this (in the selected country's currency). Applied after fetching — no confirmed API price-range parameter.

## `maxPrice` (type: `integer`):

Optional. Drop listings priced above this (in the selected country's currency). Applied after fetching — no confirmed API price-range parameter.

## `deltaMode` (type: `boolean`):

Turns this actor into a monitor. A listing counts as new when it hasn't been returned by a previous run of the same watchlist; a price drop counts when a previously-seen listing's price has fallen (see "Minimum price drop" below); it can also be reported gone or confirmed sold (see "Alert on gone/sold listings" below). Already-reported listings are dropped before you're billed, so a run with nothing new or dropped costs only the run fee. The first run has nothing to compare against, so it returns and remembers everything; from the second run on you get only what changed.

## `deltaName` (type: `string`):

Leave empty and we derive one from this run's filters, so two schedules with different filters keep separate memories. Type your own name to keep one memory across a filter change, or to have two schedules share one.

## `alertOnNew` (type: `boolean`):

Part of "Alert on" (split into checkboxes so the Console can render each as a plain toggle). On by default.

## `alertOnPriceDrop` (type: `boolean`):

Part of "Alert on". On by default.

## `alertOnGone` (type: `boolean`):

Off by default. On: a listing this watchlist saw before and that has since disappeared is returned as an ordinary listing row, either changeType "sold" (Vestiaire's own `sold` flag confirmed it, soldConfirmed true) or changeType "gone" (no such proof; gonePresumedSold true only when it was cheap and stayed up 2+ runs). No separate charge event. Both are counted toward "Sell-through rate", "Gone this run" and "Confirmed sold this run" even with this off. See the README's "Sold signal and sell-through" section.

## `minPriceDropPct` (type: `integer`):

Only fires when a previously-seen listing's price has fallen by at least this percentage since it was last seen. Default 5%.

## `skipFirstRun` (type: `boolean`):

On: the run that first starts a watchlist banks every listing silently — no rows, no charge — instead of reporting everything that already existed as "new". Off (default): the first run returns everything it finds, all marked new.

## `columns` (type: `array`):

Choose which pieces of information to include in each result row. All are included by default.

## `expandRows` (type: `boolean`):

When on, each listing found gets its own row instead of being grouped under its search. You're still only charged once per search, no matter how many rows it produces.

## `maxConcurrency` (type: `integer`):

Parallel requests. Keep conservative — this target has no browser fallback, so getting blocked costs more than slow-and-steady.

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

Apify Proxy config. Residential recommended for anti-bot-sensitive targets.

## Actor input object example

```json
{
  "searches": [
    "chanel bag"
  ],
  "testRun": false,
  "onlyFound": false,
  "includeKeywords": [],
  "excludeKeywords": [],
  "brands": [],
  "availability": "all",
  "countries": [],
  "country": "US",
  "sort": "recency",
  "maxListingsPerSearch": 120,
  "deltaMode": false,
  "deltaName": "",
  "alertOnNew": true,
  "alertOnPriceDrop": true,
  "alertOnGone": false,
  "minPriceDropPct": 5,
  "skipFirstRun": false,
  "columns": [
    "searchQuery",
    "listingCount",
    "totalAvailable",
    "truncated",
    "newCount",
    "priceDropCount",
    "goneCount",
    "soldCount",
    "monitorStatus",
    "sellThroughRate",
    "medianPriceEur",
    "listings",
    "listingId",
    "title",
    "brand",
    "price",
    "currency",
    "priceEur",
    "size",
    "sold",
    "createdAt",
    "imageUrl",
    "url",
    "sellerCountry",
    "isNew",
    "changeType",
    "previousPrice",
    "priceDropPct",
    "firstSeenAt",
    "lastSeenAt",
    "daysListed",
    "lastPrice",
    "gonePresumedSold",
    "soldConfirmed",
    "listingStatus",
    "country",
    "listedAt"
  ],
  "expandRows": true,
  "maxConcurrency": 5,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "UNBLOCKER"
    ]
  }
}
```

# 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 = {
    "searches": [
        "chanel bag"
    ],
    "includeKeywords": [],
    "excludeKeywords": [],
    "brands": [],
    "countries": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("accountable_eel/vestiaire-listing-lookup").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 = {
    "searches": ["chanel bag"],
    "includeKeywords": [],
    "excludeKeywords": [],
    "brands": [],
    "countries": [],
}

# Run the Actor and wait for it to finish
run = client.actor("accountable_eel/vestiaire-listing-lookup").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 '{
  "searches": [
    "chanel bag"
  ],
  "includeKeywords": [],
  "excludeKeywords": [],
  "brands": [],
  "countries": []
}' |
apify call accountable_eel/vestiaire-listing-lookup --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,accountable_eel/vestiaire-listing-lookup"
        }
    }
}
```

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/ptgMth1AWKY90EWhc/builds/H6sz29BROQVNciXW3/openapi.json
