# Christie's Lot & Auction Scraper (`crawlerbros/christies-scraper`) Actor

Scrape Christie's - one of the world's leading auction houses. Search lots by keyword, browse by department, list upcoming sales from the auction calendar, and fetch lot detail pages by URL - with artist, title, description, estimates, price realized, sale info, images, provenance, and literature.

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

## Pricing

from $3.00 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
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

## Christie's Lot & Auction Scraper

Scrape **Christie's** — one of the world's leading auction houses, with sales of fine art, jewellery, watches, wine, design, and collectibles in New York, London, Paris, Geneva, Hong Kong, and beyond. Search lots by keyword, browse lots by department, list upcoming sales from the auction calendar, or fetch full lot detail pages by URL — with artist, title, description, estimates, price realized, sale dates, lot numbers, images, provenance, and literature. Pure HTTP, no login, no proxy required.

### What this actor does

- **Four modes:** `search`, `browseByDepartment`, `upcomingSales`, `byUrl`
- **Full lot records:** artist, title, catalogue description, estimates, price realized, sale info
- **Lot detail sections:** description, provenance, literature, and exhibition history when fetched by URL
- **Image URLs:** primary image plus all gallery images from the lot page
- **Auction calendar:** upcoming sales with dates, locations, types, and landing URLs
- **Filters:** sale-date range, contains-keyword
- **Empty fields are omitted**

### Data source

Lot search data is served by Christie's public search API (`apim.christies.com/lots`); browse, calendar, and lot-detail data are read from Christie's public website pages (`www.christies.com`), which embed structured JSON for each lot and sale. No authentication, cookies, or paid infrastructure is used. This is a third-party actor — it is not affiliated with Christie's.

### Output per lot (mode = `search` / `browseByDepartment`)

- `lotId` — sale-qualified lot id (e.g. `7404.11`)
- `objectIdDotCom`, `objectIdOnline` — Christie's object identifiers
- `artist` — artist / maker name (primary title)
- `title` — lot title (secondary title)
- `description` — catalogue entry text
- `provenance` — provenance text
- `preLotText` — pre-lot property notes (e.g. "PROPERTY FROM A PRIVATE COLLECTION")
- `estimateLow`, `estimateHigh`, `estimateCurrency` — estimate range and currency
- `estimateLowUsd`, `estimateHighUsd`, `priceRealisedUsd` — USD-converted values when published
- `priceRealised`, `priceCurrency` — realized price and currency (when sold)
- `lotType` — `Live` / `Online`
- `auctionId` — the sale's auction number
- `withdrawn`, `referDepartment` — flags
- `saleId`, `saleNumber`, `saleRoomCode`, `saleType` — sale identifiers
- `imageUrl`, `imageUrls[]`, `imageAlt` — lot images (absolute CDN URLs, incl. private-sale images)
- `livingColors[]` — dominant colour palette (hex / weight / rank / rgb) when the API publishes it (common on watch lots, sparse elsewhere)
- `sourceUrl`, `recordType: "lot"`, `scrapedAt`

### Output per lot (mode = `byUrl` — full detail)

Everything from the listing record plus:

- `lotNumber` — lot number within the sale
- `lotUrl`, `titleTertiary` — page URL and tertiary title line
- `estimateText`, `priceRealisedText` — human-readable estimate / realized price
- `estimateOnRequest`, `priceOnRequest`, `isUnsold` — flags
- `saleTitle`, `saleLocation`, `saleStartDate`, `saleEndDate`, `registrationCloseDate`, `timeZone`
- `previousLotUrl`, `nextLotUrl` — navigation within the sale
- `details`, `provenance`, `literature`, `exhibited`, `specialNotice`, `saleRoomNotice` — full detail-page sections when published

### Output per lot (mode = `browseByDepartment` — listing tiles)

- `objectId`, `lotNumber`, `artist`, `title`, `titleTertiary`, `description`
- `estimateText`, `estimateOnRequest`, `priceOnRequest`, `estimateVisible`, `priceRealisedText`, `currentBidText`
- `saleStartDate`, `saleEndDate`, `saleId`, `saleType`, `saleLocation`
- `withdrawn`, `isAuctionOver`, `isInProgress`, `eventType`
- `imageUrl`, `imageAlt` — incl. private-sale images served from `apim.christies.com`
- `sourceUrl`, `recordType: "lot"`, `scrapedAt`

### Output per sale (mode = `upcomingSales`)

- `eventId`, `saleTitle`, `saleSubtitle`, `eventType` — sale identity
- `dateDisplay`, `startDate`, `endDate` — sale dates
- `location` — sale city/country
- `status`, `cta`, `saleTotal` — calendar metadata
- `isActive`, `isInProgress`, `isLive`, `isOnView` — flags
- `onViewText`, `viewingLocations[]` — public viewing details when published
- `imageUrl`, `imageUrls[]`, `sourceUrl`, `recordType: "sale"`, `scrapedAt`

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `mode` | string | `search` | `search` / `browseByDepartment` / `upcomingSales` / `byUrl` |
| `searchQuery` | string | `monet` | Free-text keyword (mode=search) |
| `department` | string | `jewellery` | Department slug (mode=browseByDepartment) |
| `lotUrls` | array | – | Lot detail URLs (mode=byUrl) |
| `lotIds` | array | – | Numeric lot IDs (mode=byUrl) |
| `dateRangeFrom` | string | – | Keep records whose sale starts ≥ this ISO date |
| `dateRangeTo` | string | – | Keep records whose sale starts ≤ this ISO date |
| `containsKeyword` | string | – | Keep records whose artist/title/description/sale name contains this substring |
| `includeLotDetail` | bool | `false` | Fetch detail pages for search results (slower) |
| `maxItems` | int | `50` | Hard cap on emitted records (1–1000) |
| `proxyConfiguration` | object | off | Optional Apify proxy, engaged only if blocked |

#### Example: search lots by keyword

```json
{
  "mode": "search",
  "searchQuery": "monet",
  "maxItems": 20
}
```

#### Example: browse a department

```json
{
  "mode": "browseByDepartment",
  "department": "jewellery",
  "maxItems": 20
}
```

#### Example: upcoming sales

```json
{
  "mode": "upcomingSales",
  "maxItems": 30
}
```

#### Example: fetch lots by URL

```json
{
  "mode": "byUrl",
  "lotUrls": [
    "https://www.christies.com/en/lot/lot-6452945"
  ]
}
```

#### Example: keyword search with detail enrichment and date filter

```json
{
  "mode": "search",
  "searchQuery": "picasso",
  "includeLotDetail": true,
  "dateRangeFrom": "2020-01-01",
  "maxItems": 10
}
```

### Limitations

- **No online-only lots by URL** — online-only lots live on a separate platform (`onlineonly.christies.com`) whose pages don't expose the same structured data. `byUrl` accepts main-site lot URLs and numeric lot IDs. Online lots still appear in `search` and `browseByDepartment` with listing-level fields.
- **No past-sale result totals** — per-sale result aggregates for past auctions are not exposed by the endpoints used; the search index does include past lots with their realized prices.
- **Placeholder dates dropped** — Christie's marks some historical sales with a placeholder date (`0001-01-01`); the actor drops these instead of emitting sentinel values.
- **`priceRealised` only for sold lots** — unsold, withdrawn, or still-open lots omit the realized price; estimate-on-request lots omit the numeric estimate range.
- **Listing vs. detail depth** — `search` and `browseByDepartment` emit listing-level records; full detail sections (provenance, literature, exhibited) require `includeLotDetail: true` or mode `byUrl`.

### Use cases

- **Art market research** — track estimates and realized prices for artists across sales
- **Price benchmarking** — compare auction results by artist, department, and sale date
- **Collector intelligence** — monitor what's being offered in specific departments
- **Watch lists** — identify lots by artist or keyword ahead of upcoming sales
- **Auction calendar feeds** — power dashboards with upcoming sale schedules
- **Due diligence** — collect provenance and literature for lots of interest

### FAQ

**What is the data source?** Search results come from Christie's public search API; browse, calendar, and lot-detail data come from Christie's public website. This is a third-party actor using public endpoints — it is not affiliated with Christie's.

**Is a proxy or login required?** No. All endpoints used are public and work with plain HTTP requests. An optional proxy is available as a fallback if the source starts blocking requests.

**What does `browseByDepartment` return?** It uses Christie's department filter on the public lot-browse page and returns the lots currently listed for that department (mostly private-sale and online-auction lots, matching what the department's browse view shows).

**Why are some lots missing `priceRealised`?** Only sold lots have a realized price; unsold, withdrawn, or still-open lots omit it. Estimate-on-request lots omit the numeric range.

**How fresh is the data?** Search results mirror the live search index; the calendar reflects the currently published upcoming-sale schedule.

**What does `includeLotDetail` do?** When enabled, every search-result lot is enriched by fetching its detail page — adding provenance, literature, exhibition history, and extra images. This is slower (one extra request per lot) and is off by default.

**What date formats are accepted for `dateRangeFrom` / `dateRangeTo`?** ISO `YYYY-MM-DD` (e.g. `2026-01-01`). The comparison uses each lot/sale's sale start date at day granularity.

**Do image URLs work outside the site?** Yes — the image URLs point at Christie's public image CDN and load without any special headers or referer.

**Can I scrape auction results / past sales?** This actor covers current lot search, department browse, upcoming sales, and lot detail. Past-sale result totals are not exposed; the search index does include past lots with their realized prices.

**Can I fetch online-only lots by URL?** Online-only lots live on a separate platform (`onlineonly.christies.com`) whose pages don't expose the same structured data. `byUrl` accepts main-site lot URLs and numeric lot IDs; online-only URLs are not supported. Online lots still appear in `search` and `browseByDepartment` with listing-level fields.

**Why are some sale dates missing?** Christie's marks some historical sales with a placeholder date (`0001-01-01`). The actor drops such placeholder dates instead of emitting them, so records never contain sentinel values.

**What if my search returns nothing?** Try a broader keyword or fewer filters. Zero results is reported in the run's status message and the run still succeeds.

# Actor input Schema

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

What to fetch.

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

Free-text keyword matching lot artist, title, or description (mode=search).

## `department` (type: `string`):

Christie's department whose lots to browse (mode=browseByDepartment).

## `lotUrls` (type: `array`):

Full lot detail URLs, e.g. `https://www.christies.com/en/lot/lot-6452945` (mode=byUrl).

## `lotIds` (type: `array`):

Numeric lot IDs, e.g. `6452945` (mode=byUrl).

## `dateRangeFrom` (type: `string`):

Only keep lots/sales whose auction starts on or after this date (ISO `YYYY-MM-DD`).

## `dateRangeTo` (type: `string`):

Only keep lots/sales whose auction starts on or before this date (ISO `YYYY-MM-DD`).

## `containsKeyword` (type: `string`):

Only keep records whose artist, title, description, or sale name contains this substring (case-insensitive).

## `includeLotDetail` (type: `boolean`):

When enabled, the actor fetches each search-result lot's detail page and adds detail fields (description sections, provenance, literature, exhibited). Slower; disabled by default (mode=search).

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

Hard cap on emitted records.

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

Optional Apify proxy. The actor first tries a direct connection and only engages the proxy if the source blocks the request.

## Actor input object example

```json
{
  "mode": "search",
  "searchQuery": "monet",
  "department": "jewellery",
  "lotUrls": [],
  "lotIds": [],
  "dateRangeFrom": "",
  "dateRangeTo": "",
  "containsKeyword": "",
  "includeLotDetail": false,
  "maxItems": 50,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `lotsAndSales` (type: `string`):

Dataset containing all scraped Christie's lots and sales.

# 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": "search",
    "searchQuery": "monet",
    "department": "jewellery",
    "lotUrls": [],
    "lotIds": [],
    "dateRangeFrom": "",
    "dateRangeTo": "",
    "containsKeyword": "",
    "includeLotDetail": false,
    "maxItems": 50,
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("crawlerbros/christies-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": "search",
    "searchQuery": "monet",
    "department": "jewellery",
    "lotUrls": [],
    "lotIds": [],
    "dateRangeFrom": "",
    "dateRangeTo": "",
    "containsKeyword": "",
    "includeLotDetail": False,
    "maxItems": 50,
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("crawlerbros/christies-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": "search",
  "searchQuery": "monet",
  "department": "jewellery",
  "lotUrls": [],
  "lotIds": [],
  "dateRangeFrom": "",
  "dateRangeTo": "",
  "containsKeyword": "",
  "includeLotDetail": false,
  "maxItems": 50,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call crawlerbros/christies-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,crawlerbros/christies-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/rl7Vr3e1FHimPYLEx/builds/diceVnckggS1rVj99/openapi.json
