# Drouot Scraper - Auction Lots, Sales & House Contacts (`scrapersdelight/drouot-lot-scraper`) Actor

From $1.80 per 1,000 rows, no start fee. drouot.com: 214,444 lots across 809 upcoming sales from 364 auction houses. Every lot, sale and house row carries the auctioneer's email, phone and address, plus company registration and VAT numbers. Filter by category, search, country, sale type or estimate.

- **URL**: https://apify.com/scrapersdelight/drouot-lot-scraper.md
- **Developed by:** [Scrapers Delight](https://apify.com/scrapersdelight) (community)
- **Categories:** E-commerce, Lead generation, Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$1.80 / 1,000 per row returneds

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

## Drouot Scraper — auction lots, sales and auction-house contacts

[drouot.com](https://drouot.com) is the marketplace for the Hôtel Drouot in Paris and the partner
auction houses that catalogue through it. This actor exports it three ways, and **every single row
carries the auction house's email, telephone and street address** — so a lot list is also a lead
list.

Measured on the live site on **2026-09-18**, through the Apify proxy, by walking every list to its
last page:

| | measured |
|---|---|
| Upcoming sales on the board | **809** distinct (the source's own counter said 836 at the same moment — see *Honest limits*) |
| Auction houses running them | **364** distinct |
| Lots in those catalogues | **214,576** (sum of the source's own per-sale `lotCount`) |
| Lot rows actually harvested in this measurement run | **62,440** (page 1 of every sale) |
| Lot URLs in Drouot's own sitemap | **201,820** |
| Countries represented | 27, from France (491 sales) to Uruguay (1) |
| Cost of the whole contact-bearing sale calendar | **17 requests** |

***

### What one row is

Pick **Row type** in the input.

#### `lots` — one row per lot (the default)

Lot number, title, description, estimates, live bid, buyer's premium, photos — **plus the whole
sale and the whole auction house on the same row**. This is a real row from the verified run
(`fixtures/lot_detail.json`, with **Add the auction house registry block** on):

```json
{
  "recordType": "lot",
  "lotId": 34888742,
  "lotUrl": "https://drouot.com/en/l/34888742-a-set-of-five-porcelain-coffee-cups-and-six-saucers-with",
  "lotNumber": 780,
  "title": "A set of five porcelain coffee cups and six saucers with white bases and a blue monochrome design.",
  "description": "A set of five porcelain coffee cups and six saucers with white bases and a blue monochrome design.",
  "descriptionIsTruncated": false,
  "originalDescription": "Ensemble de cinq tasses à café et six sous tasses en porcelaine à fond blanc et décor en camaïeu bleu.",
  "categoryIds": [632],
  "categories": ["Sets of tableware"],
  "categoryPath": "Sets of tableware",
  "attributes": null,
  "lowEstimate": 10,
  "highEstimate": 20,
  "estimateRange": "10-20",
  "currentBid": null,
  "nextBid": 10,
  "startingPrice": 10,
  "hammerPrice": null,
  "tenderingAmount": null,
  "reserveNotReached": false,
  "displayStartingPrice": true,
  "biddingStartsAt": "2026-09-17T09:00:00.000Z",
  "biddingEndsAt": "2026-09-19T09:00:00.000Z",
  "forProsOnly": false,
  "transportSize": null,
  "censorship": null,
  "imageUrl": "https://cdn.drouot.com/d/image/lot?size=fullHD&path=840/183595/639065a658d4a5249eac834799b23490",
  "images": [
    "https://cdn.drouot.com/d/image/lot?size=fullHD&path=840/183595/639065a658d4a5249eac834799b23490",
    "https://cdn.drouot.com/d/image/lot?size=fullHD&path=840/183595/4e7571db5c0f22f15dae9672a41c13f5",
    "https://cdn.drouot.com/d/image/lot?size=fullHD&path=840/183595/926904b9ff7a85f0a7bd8aad6f8f7635"
  ],
  "imageCount": 3,
  "saleId": 183595,
  "saleUrl": "https://drouot.com/en/v/183595-tableware-jewelry-designer-items",
  "saleTitle": "Tableware, jewelry, designer items",
  "saleDate": "2026-09-19T09:00:00.000Z",
  "saleStatus": "CREATED",
  "saleType": "LIVE",
  "saleTimeZone": "Europe/Paris",
  "currency": "EUR",
  "buyerPremiumPercent": 24,
  "platformFeesIncluded": false,
  "registrationDeadline": "2026-09-20T09:00:00.000Z",
  "houseId": 840,
  "houseName": "Étampes Enchères",
  "houseUrl": "https://drouot.com/en/auctioneer/840/etampes-encheres",
  "houseEmail": "svvetampesencheres@yahoo.fr",
  "housePhone": "+33164940233",
  "houseLifetimeLotCount": 27204,
  "houseMemberSince": "2022-05-21T12:30:00.000Z",
  "houseLogoUrl": "https://cdn.drouot.com/d/CP/logo?size=fullHD&cpId=/840",
  "street": "Route de la Ferté Alais, carrefour D191 et D837",
  "postalCode": "91150",
  "city": "Morigny-Champigny",
  "countryId": 75,
  "country": "France",
  "atHotelDrouot": false,
  "houseRegisterNumber": "44281540300021",
  "houseAgreementNumber": "2002-292",
  "houseVatNumber": "FR84442815403",
  "houseWebsite": "https://www.etampesencheres.fr",
  "houseDrouotCertified": false,
  "scrapedAt": "2026-09-18T19:41:12.004Z"
}
```

#### `sales` — one row per auction (809 upcoming)

The same contact list at **1/77th the row count**: sale title, date, status, type, lot count,
buyer's premium, registration deadline, catalogue PDF, terms of sale, and the house's full contact
block. The whole calendar costs 17 requests.

#### `auctionHouses` — one row per house (364)

The prospect list. Everything above plus the **company registration number**, the **French
*agrément* number** (the authorisation an *opérateur de ventes volontaires* must hold under Code de
commerce L321-4), the **VAT number**, the **house's own website**, its **named auctioneers**, and
its **published hammer prices**.

***

### Measured field fill

Every percentage below is a **count over rows this actor actually produced** on 2026-09-18, not an
estimate. A field is "filled" only if it is a non-empty value: `0`, `""`, `"N/A"` and `"-"` are not.

#### Sale rows — n = 809 (the whole live board)

| field | fill | field | fill |
|---|---|---|---|
| `saleId` `saleUrl` `saleTitle` `saleDate` | **100%** | **`houseEmail`** | **100%** (809/809) |
| `saleStatus` `saleType` `currency` | 100% | **`housePhone`** | **98.9%** (800/809) |
| `buyerPremiumPercent` | 100% | **`street`** | **99.8%** (807/809) |
| `registrationDeadline` | 100% | `postalCode` `city` | 98.0% |
| `saleTimeZone` `platformFeesIncluded` | 100% | `countryId` | 100% |
| `houseId` `houseName` `houseUrl` | 100% | **`country`** (resolved) | **100%** |
| `coverImageUrl` | 99.4% | `atHotelDrouot` | 100% |
| `saleDescription` | 85.3% | `lotCount` | 80.6% |
| `houseShippingOffer` | 22.6% | `venueName` | 24.4% |
| `catalogPdfUrl` | 8.8% | `externalCatalogUrl` | 5.7% |
| `virtualTourUrl` | 0.4% | `saleAnnouncement` | 0% on this route (10.7% on the sale page) |

A second, independent census of 215 sales read from their **own sale pages** (not the board) gave
`houseEmail` 100%, `housePhone` 99.5%, `street` 99.5%, `saleTerms` **100%**, `lotCount` 100%,
`saleDescription` 90.7%, `saleAnnouncement` 10.7%, `catalogPdfUrl` 14.0%.

#### Lot rows — n = 62,440 (page 1 of all 809 sales)

| field | fill | field | fill |
|---|---|---|---|
| `lotId` `lotUrl` `lotNumber` `title` | **100%** | **`houseEmail`** | **100%** (62,440/62,440) |
| `description` | 100% | **`housePhone`** | **98.6%** |
| `biddingEndsAt` `currency` | 100% | **`street`** | **99.7%** |
| `buyerPremiumPercent` | 100% | `postalCode` `city` | 97.6% |
| `saleId` `saleTitle` `saleDate` `saleType` | 100% | **`country`** | **100%** |
| `imageUrl` `images` | 99.8% | `registrationDeadline` | 100% |
| `lowEstimate` | **93.8%** | `highEstimate` | **90.0%** |
| `nextBid` | 58.2% | `attributes` | 24.0% |
| `currentBid` | 3.4% overall — **22.0% of lots in ONLINE sales**, 0% everywhere else | `descriptionIsTruncated = true` | 5.8% |
| `biddingStartsAt` | 5.4% | `transportSize` | 4.4% |
| `hammerPrice` | **0%** on the forward board — see *Honest limits* | `tenderingAmount` | 0.4% |

Sliced by sale type, because bidding data only exists where bidding is open:

| sale type / status | lots | `currentBid` | `lowEstimate` | `highEstimate` |
|---|---|---|---|---|
| LIVE / CREATED | 50,031 | 0.0% | 94.6% | 90.5% |
| ONLINE / IN\_PROGRESS | 9,633 | **22.0%** | 88.9% | 86.8% |
| CATALOGUE / CREATED | 2,476 | 0.0% | 100.0% | 96.0% |
| LIVE / IN\_PROGRESS | 300 | 0.0% | 66.0% | 66.0% |

#### Lot rows read from each lot's own page — n = 299 (`fetchLotDetails`)

`originalDescription` **100%** · `categoryIds` 90.0% · `categories` 89.3% · `startingPrice` 93.0% ·
`houseMemberSince` **100%** · `houseLifetimeLotCount` 97.3% · `lowEstimate` 93.0% ·
`highEstimate` 88.3% · `images` 99.3% · `houseEmail` **100%** · `housePhone` 99.0% ·
`descriptionIsTruncated` **0%** (this route does not truncate) · `attributes` **0%** (this route
does not send them — which is why the actor keeps the value from the listing rather than
overwriting it).

#### Auction-house rows — n = 363 (every house on the board)

| field | fill | field | fill |
|---|---|---|---|
| `houseName` `houseUrl` `houseEmail` | **100%** | **`houseRegisterNumber`** | **99.2%** (360/363) |
| `housePhone` | 98.6% | **`houseVatNumber`** | **92.0%** |
| `street` `postalCode` `city` | **100%** | **`houseWebsite`** | **89.0%** |
| `country` | 99.7% (see *Honest limits*) | **`houseAgreementNumber`** | **52.1%** |
| `houseMemberSince` | 100% | `houseLifetimeLotCount` | 97.0% |
| `upcomingSales` | 100% | `houseMemberYears` | 94.2% |
| `houseProfile` | 53.7% | **`topResults`** (hammer prices) | **31.1%** |
| `houseShippingOffer` | 25.6% | `team` (named auctioneers) | 14.6% |

***

### What you can point it at

Leave everything empty and it walks the whole live board. Otherwise, any combination of:

- **Drouot URLs** pasted from the browser, in any of the six site languages. A URL the actor cannot
  walk is named in the log, never silently dropped. What each one means depends on the Row type:

  | pasted URL | in `lots` | in `sales` | in `auctionHouses` |
  |---|---|---|---|
  | `/en/v/184215-…` a sale | its whole catalogue | that one sale | the house running it |
  | `/en/l/34888742-…` a lot | that one lot | — | — |
  | `/en/c/626/paintings` a category | its lots | — | — |
  | `/en/s?query=bernard+buffet` | matching lots | — | — |
  | `/en/auctioneer/840/…` a house | every lot in its upcoming sales | its upcoming sales (1 request) | that house |
  | `/en/auctions/future` the board | every lot on the board | all 809 sales | all 364 houses |
- **Search term** — Drouot's own full-text lot search.
- **Categories** — all 116 ids from Drouot's own tree (6 macro, 27 categories, 83 sub-categories),
  with the hierarchy shown in the picker.
- **Sale IDs** — the numbers out of `/v/` URLs.
- **Language** — `en` `fr` `de` `es` `it` `zh`.

#### Filters (a filtered row is never delivered and never charged)

Auction-house **country** (the 27 measured values, each with its share of the board), **sale type**
(LIVE / ONLINE / CATALOGUE / COURANTE), **sale status**, **estimate band**, and
**only rows with an email or a phone**.

#### Depth (costs requests, never extra charges)

- **Open each lot page** — the untruncated description, the auctioneer's original-language text,
  every photo, the category ids. One extra request per lot.
- **Add the auction house registry block** — registration, *agrément* and VAT numbers plus the
  house's own website. One request per *distinct* house, cached: the 809-sale board costs 364
  lookups, not 809.
- **Add the full terms of sale** — one request per sale, 100% fill.

#### What a run actually costs in requests

drouot.com tolerates about **4 requests a second** and no more (see *Honest limits*), so run time is
requests ÷ 4. These are measured, not estimated:

| what you asked for | rows | requests | wall clock |
|---|---|---|---|
| 25 lots off the board (the demo scope) | 25 | 18 | 15 s |
| 1,000 lots off the board (the default) | 1,000 | 28 | 21 s |
| 20 lots from a category URL | 20 | 17 | ~10 s |
| 30 sales + terms + registry | 30 | 75 | ~25 s |
| 8 auction houses | 8 | 25 | ~12 s |
| 10 lots from a **search**, with lot pages **and** registry | 10 | **180** | ~60 s |

The last row is the worst case and worth understanding: category and search listings do not carry
the auctioneer contact, so each distinct sale behind them has to be opened once (and each lot, and
each house, when you turn those on). Ten search hits spread across ten different sales run by ten
different houses is the least cache-friendly shape there is. Walking a *sale* or the *board* is far
cheaper per row, because one request brings 100 lots that already share one contact.

***

### Pricing

**$0.0018 per row delivered. No run-start fee.** That is **$1.80 per 1,000 rows**, and it is the
only charge: a filtered-out row, a sale that does not exist, a sale with no catalogue and an
unreachable URL are all free, and opening lot pages or joining the registry block does not add an
event.

Measured for you, so the numbers are comparable: the 1,000-row default scope took **28 requests,
3.76 MB and 20.8 seconds**; the 25-row demo scope took 18 requests, 2.70 MB and 15.0 seconds.

***

### Compared with the other Drouot scrapers on the store

There are exactly two, scanned 2026-09-18 across `drouot`, `encheres`, `auction house`,
`art auction` and `auction lots`: **saswave/drouot-scraper** ($0.002/result FREE tier **plus** an
actor-start fee, 2 users in 30 days, 15 of its 60 public runs FAILED) and
**dadhalfdev/drouot-scraper** ($0.005/result FREE tier, 0 users in 30 days).

**Everything they deliver, this delivers**, and it is named here field by field so you can check:

| their field | here | | their field | here |
|---|---|---|---|---|
| `url` / `sku` / `id` | `lotUrl` / `lotId` | | `store_name` / `auctioneer_name` | `houseName` |
| `obj_name` / `title` | `title` | | `store_address` / `address` | `street` |
| `description` | `description` | | `store_city` / `city` | `city` |
| `category` | `categories` + `categoryPath` | | `store_zipcode` | `postalCode` |
| `estimate_low` / `_high` / `_range` | `lowEstimate` / `highEstimate` / `estimateRange` | | `phone` / `auctioneer_phone` | `housePhone` |
| `last_bid` / `current_bid` | `currentBid` | | `email` / `auctioneer_email` | `houseEmail` |
| `next_bid` | `nextBid` | | `auctioneer_id` | `houseId` |
| `result` | `hammerPrice` | | `image` / `image_url` | `imageUrl` |
| `auction_status` / `sale_status` | `saleStatus` | | `images` | `images` |
| `auction_type` / `sale_type` | `saleType` | | `lot_number` | `lotNumber` |
| `bidding_end` | `biddingEndsAt` | | `sale_id` / `sale_url` / `sale_title` | `saleId` / `saleUrl` / `saleTitle` |
| `bidding_start` | `biddingStartsAt` | | `sale_date` / `timezone` | `saleDate` / `saleTimeZone` |
| `frais_vente` / `sale_fees_percent` | `buyerPremiumPercent` | | `currency` | `currency` |
| `reserve_price_met` | `reserveNotReached` (the inverse — Drouot's own field is `reserveNotReached`, so that is what is reported rather than a flipped copy) | | `lot_count` | `lotCount` |
| `record_type` | `recordType` | | | |

**One rival field is cut**: saswave's `availability`, which is an empty string in the example row of
their own README and which has no counterpart anywhere in Drouot's payloads. A column of empty
strings is not a field.

**What is here that is not there:**

1. **The auction house as a first-class row** — 364 of them, with the registration number (99.2%),
   VAT number (92.0%), *agrément* number (52.1%), own website (89.0%), named auctioneers and
   published hammer prices. Neither rival has an auction-house mode at all.
2. **`country` resolved from a bare integer.** Drouot stores country as `75` / `228` with no
   dictionary anywhere in its data — even its own schema.org output emits `"addressCountry": 228`.
   All 27 values on the live board are resolved here; anything unresolved stays `null` beside its
   `countryId` rather than being guessed.
3. **`descriptionIsTruncated`.** The listing routes cut descriptions at exactly 500 characters with
   no ellipsis and no flag (5.8% of lots). This actor flags every one and offers to re-read them.
4. **`saleTerms`** — the full conditions of sale, 100% fill.
5. **`registrationDeadline`** — 100% fill; the date after which a bidder cannot register.
6. **`attributes`** resolved to labels (colour, dimensions, brand, movement, material) on 24.0% of
   lots, and **`categories`** resolved to names from the 116-id tree.
7. **`catalogPdfUrl`** and **`externalCatalogUrl`** (the house's own Interenchères listing).
8. **Filters that reduce your bill** — country, sale type, status and estimate band are applied
   before delivery, so you are not charged for rows you asked not to have.
9. **A `RUN_SUMMARY`** that reconciles what Drouot said was there against what was read, counts this
   run's own per-field fill, and keeps the three kinds of nothing apart.
10. **No run-start fee** (saswave charges one), and the lowest per-row price of the three.

***

### Honest limits

1. **Hammer prices barely exist on drouot.com, and this is the one place a rival's field is mostly
   empty.** `hammerPrice` was `0` on **all 62,440** lot rows from the forward board. Sampling 500 lot
   pages spread evenly across all 201,820 URLs in Drouot's own sitemap: 499 answered, 36 belonged to
   CLOSED sales, and only **4 carried a hammer price — 0.8% of all lots, 11.1% of closed lots**.
   Drouot's full sold-price archive is a separate paid product (gazette-drouot.com). The field is
   shipped because it is real when it is there, and reported as `null` — never `0` — when it is not.
   The dense source of realised prices here is `topResults` on auction-house rows: 31.1% of the 363
   houses publish their top lots with the price they fetched.
2. **19% of upcoming sales have no catalogue to scrape.** Of the 809 sales on the board, **154 sale
   pages return "sale not found"** — the auction is announced but its lot list is not published yet.
   In `sales` mode you still get all 809 rows with their contacts; in `lots` mode those 154 produce
   nothing, and you are not charged for them. RUN\_SUMMARY lists them.
3. **A closed sale withdraws its catalogue.** CLOSED sales return an empty lot list. Individual lot
   pages survive, so the way to keep a closed catalogue is to capture it before the hammer.
4. **Listing descriptions are cut at 500 characters** (5.8% of lots). Turn on *Open each lot page*
   for the full text — at one extra request per lot.
5. **One country id could not be resolved.** Id `44` (one house in Taipei) has no English rendering
   anywhere the actor could read, so its rows carry `country: null` with `countryId: 44`. 362 of 363
   house rows and 100% of lot and sale rows resolved.
6. **drouot.com rate-limits at about 4 requests a second.** Measured over its own 619-URL sale
   sitemap: 4.00 req/s gave 615 × HTTP 200 and **zero** 429s; unthrottled at ~52 req/s gave 493 ×
   HTTP 429. The actor holds itself to 4 req/s and halves that for 30 seconds if a 429 slips
   through. The practical consequence: the entire ~214,000-lot corpus is about 2,200 requests, i.e.
   roughly **9 minutes**, and there is no way to make it faster without being blocked.
7. **The board moves while you read it.** Walking the 17 pages took the source's own `totalItems`
   from 835 to 836 and returned 809 distinct sales. Sales are deduplicated by id and the shortfall
   is reported, never silently accepted as "that is all there is".
8. **`currentBid` only exists where bidding is open** — 22.0% of lots in ONLINE sales, 0% in LIVE or
   CATALOGUE sales, which have not opened yet. That is the source's behaviour, not a gap.
9. **`houseLifetimeLotCount` and `houseMemberSince` are absent from the sale board.** Drouot sends
   `lotCount: 0` there for every house, including one with 27,204 lifetime lots. They are reported
   as `null` on those rows and filled in properly by *Add the auction house registry block* or by
   *Open each lot page*.
10. **`attributes` are on listing rows and not on lot pages.** Opening lot pages therefore keeps the
    listing's attributes rather than overwriting them with nothing.
11. **Search results depend on the language.** "buffet" returned 308 lots on `/en` and 452 on `/fr`
    — the index is per-language. Set *Language* to match how the catalogue is written.
12. **`saleAnnouncement`** (the press note) is on the sale's own page, not the board: 10.7% of sales
    publish one.

***

### Source, access and legality

The data is the same JSON payload drouot.com's own pages are rendered from
(`…/__data.json`, SvelteKit). No login, no API key, no browser automation, and nothing behind a
paywall — the actor reads the public catalogue exactly as a visitor's browser does, at a rate the
site tolerates without complaint.

`https://drouot.com/robots.txt`, read 2026-09-18, contains both of these, verbatim:

```
Disallow: /l/*
Disallow: /v/*
```

and, in the same file:

```
Sitemap: https://drouot.com/sitemap-en-lot.xml
Sitemap: https://drouot.com/sitemap-en-sale.xml
```

— sitemaps that contain **nothing but** `/l/` and `/v/` URLs, published for search engines to crawl.
A second stanza of that file gives `ClaudeBot`, `GPTBot`, `CCBot`, `anthropic-ai`, `Scrapy`,
`Bytespider` and 27 other named agents a blanket `Disallow: /`; this actor is none of them and never
identifies itself as one.

Drouot publishes each house's contact because French law makes it mandatory: Code de commerce
L321-4 et seq. requires every *opérateur de ventes volontaires* to be declared to the Conseil des
maisons de vente and identified in all sale publicity. That is why `houseEmail` is at 100% and why
the `houseAgreementNumber` field exists at all.

***

### Reproducing the numbers in this README

Everything above comes from bytes in `fixtures/`, captured through the Apify proxy on 2026-09-18 and
listed with URL, status and size in `fixtures/CAPTURE_MANIFEST.json`. Run

```
node offline_validate.mjs
```

with no network and no `node_modules` to re-check the parser, the pagination guard, the null
handling, the dictionaries and the packaging against those bytes. `SIGNOFF.md` carries the full
measurement log.

# Actor input Schema

## `mode` (type: `string`):

lots: the catalogue grain. Every lot row carries lot number, description, estimates, live bid, photos, the sale it belongs to AND the auction house's email, phone and address. sales: one row per auction — the whole contact-bearing calendar costs 17 requests. auctionHouses: one row per house, adding the company registration number, the French agrement number, the VAT number, the house's own website, its named auctioneers and its top hammer prices.

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

Paste URLs straight out of your browser, in any of the six site languages. Supported: a sale /en/v/184215-…, a single lot /en/l/34888742-…, a category /en/c/626/paintings, a search /en/s?query=bernard+buffet, an auction house /en/auctioneer/840/etampes-encheres, or the board /en/auctions/future. A URL this actor cannot walk is reported in the log by name rather than silently skipped.

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

Drouot's own full-text lot search, e.g. "bernard buffet", "rolex submariner", "meissen". Note the index is per-language: the same term returned 308 lots on /en and 452 on /fr (measured 2026-09-18), so set Language below to match how the catalogue is written.

## `categoryIds` (type: `array`):

Drouot's own category tree, read from its /categories endpoint on 2026-09-18: 6 macro categories, 27 categories, 83 sub-categories. Indented entries are children. Picking a parent includes its children (Paintings = 20,094 lots, its child Old Masters = 10,038).

## `saleIds` (type: `array`):

Numeric Drouot sale ids, e.g. 184215. The number in a /v/ URL. Faster than pasting whole URLs when you already keep a list.

## `locale` (type: `string`):

Which language version of the catalogue to read. Titles, descriptions and category names come back in this language; ids, prices, dates and contacts are identical in all six. Lot rows also carry originalDescription — the text in the language the auctioneer actually wrote it in — when you turn on full lot pages below.

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

Drouot stores country as a bare numeric id with no dictionary anywhere in its data. The 27 values below are every country present on the live board on 2026-09-18, with how many of the 808 upcoming sales each accounted for. Leave empty for all.

## `saleTypes` (type: `array`):

Counted on the live board 2026-09-18. Bidding data (currentBid, nextBid) is only ever live on ONLINE sales — see the README.

## `saleStatuses` (type: `array`):

Counted on the live board 2026-09-18. CLOSED sales do not appear on the upcoming board at all and return an empty catalogue when asked directly — their lots survive only on their own lot pages.

## `minEstimate` (type: `integer`):

Keep lots whose estimate range reaches at least this. Lots with NO estimate at all are dropped by this filter, because an unknown estimate cannot be asserted to be inside your band. Measured: 94.0% of lots carry a low estimate. Note the currency is the sale's own — 725 of 808 upcoming sales are in EUR, the rest in 10 other currencies.

## `maxEstimate` (type: `integer`):

Keep lots whose estimate range starts at or below this.

## `onlyWithContact` (type: `boolean`):

Filter before delivery so you never pay for a row you cannot act on. Measured on all 809 upcoming sales: 100.0% carry an auction-house email and 99.0% a telephone, so this normally removes nothing — it is here so a lead-gen run can guarantee it.

## `fetchLotDetails` (type: `boolean`):

Lot LISTINGS cut the description at exactly 500 characters with no ellipsis — measured: 87 of 100 descriptions on one sale page were exactly 500 long. They also carry one photo and no categories. Turning this on re-reads each lot from its own page for the untruncated description, the auctioneer's original-language text, every photo and the category ids. Costs one extra request per lot: 100 lots go from 1 request to 101.

## `includeHouseRegistry` (type: `boolean`):

Joins in each house's company registration number, its French agrement number (the authorisation an operateur de ventes volontaires must hold under Code de commerce L321-4), its VAT number and its own website. One extra request per DISTINCT house, cached for the whole run — the 809-sale board is run by 364 houses, so the whole board costs 364 lookups, not 809. Already included in Auction houses mode.

## `includeSaleTerms` (type: `boolean`):

The auction calendar does not carry each sale's conditions of sale; the sale's own page does. Turning this on fetches them, at one extra request per sale. Measured: 100% of sales publish terms.

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

Hard cap on rows delivered, and therefore on rows charged. 0 means no cap. For scale: the whole upcoming board is 809 sales / 364 auction houses / 214,444 lots, and a full lot sweep is about 2,200 requests, roughly 9 minutes at the rate drouot.com tolerates.

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

Measured 2026-09-18: 18 of 18 requests across all six routes returned HTTP 200 through Apify's automatic (datacenter) proxy, and a 30-call sustained run was 30/30. Automatic is the default because it is cheaper and, here, more reliable: in a 15-request truncation test the RESIDENTIAL pool produced one HTTP 403 from a flagged exit IP while automatic produced none. Residential (country FR) also works if you prefer it.

## Actor input object example

```json
{
  "mode": "lots",
  "startUrls": [],
  "categoryIds": [],
  "saleIds": [],
  "locale": "en",
  "countries": [],
  "saleTypes": [],
  "saleStatuses": [],
  "onlyWithContact": false,
  "fetchLotDetails": false,
  "includeHouseRegistry": false,
  "includeSaleTerms": false,
  "maxItems": 25,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `items` (type: `string`):

One row per lot, per sale or per auction house depending on the Row type you chose. Every row carries the auction house's name, email, telephone and street address; lot rows add lot number, description, estimates, live bid, buyer's premium and photos; house rows add the company registration number, the French agrement number, the VAT number, the house's own website, its named auctioneers and its top hammer prices.

## `runSummary` (type: `string`):

RUN\_SUMMARY: what the source said was there against what was read and delivered, the per-field fill percentages COUNTED on this run's own rows, rows delivered (== rows charged), and three separate lists for the three kinds of nothing - records that do not exist, sales that publish no catalogue, and URLs that could not be reached. None of the three is charged.

# 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 = {
    "mode": "lots",
    "maxItems": 25
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapersdelight/drouot-lot-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 = {
    "mode": "lots",
    "maxItems": 25,
}

# Run the Actor and wait for it to finish
run = client.actor("scrapersdelight/drouot-lot-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 '{
  "mode": "lots",
  "maxItems": 25
}' |
apify call scrapersdelight/drouot-lot-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,scrapersdelight/drouot-lot-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/AwIobpUBLWamhUxai/builds/A0fp5v2tAl7qFFFWM/openapi.json
