# SaatchiArt.com Artist & Artwork Scraper (`artsiom_k/saatchiart-scraper`) Actor

SaatchiArt.com artist & artwork scraper with real per-artist rollup stats (avg price, medium mix, sold count) from each artist's full catalog, plus delta mode.

- **URL**: https://apify.com/artsiom\_k/saatchiart-scraper.md
- **Developed by:** [Artsiom Kunitsyn](https://apify.com/artsiom_k) (community)
- **Categories:** Other, E-commerce
- **Stats:** 2 total users, 1 monthly users, 88.9% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.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/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

## saatchiart-scraper

Scrapes both **artworks** and **artist profiles** from [SaatchiArt.com](https://www.saatchiart.com),
including per-artist rollup stats (average price, medium mix, sold count, latest artwork) computed
from the artist's full catalog — not a sample.

### Contents

- [Key features](#key-features)
- [Output](#output)
- [Input](#input)
- [Input examples](#input-examples)
- [Incremental (delta) mode](#incremental-delta-mode)
- [How to scrape SaatchiArt.com](#how-to-scrape-saatchiartcom)
- [You might also like](#you-might-also-like)
- [FAQ](#faq)

### 🔑 Key features

- **Two entity types, one Actor.** Set `entityType` to `artworks` or `artists` — no need for two
  separate scrapers to build a full picture of an artist and their work.
- **Real per-artist rollup stats, not a sample.** Average price, unique medium mix, sold-artwork
  count, and latest artwork are computed from an artist's **entire** catalog via a paginated API
  call, cross-checked to match the artist's own stated artwork total.
- **Delta mode built in.** Every run classifies each item as `new`, `changed`, `unchanged`, or
  `delisted` against a persisted baseline — pay for what changed, not a full re-scrape every time.
- **Exhaustive when you ask for it.** A plain run (no `category`, no `startUrls`) crawls the full
  site sitemap — every artwork, every artist, not a filtered subset. `maxItems` defaults to 50
  (a fast preview, and what keeps an unconfigured run within Apify's automated 5-minute QA check)
  — clear it or raise it for a real full crawl.
- **Optional cheaper category browse, with narrowing filters.** Setting `category` uses a faster
  search API instead of the sitemap. On its own it's a bounded preview (the API caps its reported
  result count at 10,000, and a broad category has far more artworks than that) — but combining it
  with `subject`/`mediums`/`styles`/`country`/`availability` can narrow a query enough to become
  genuinely exhaustive, and the Actor detects and logs which case you're in (see [Input](#input)).

### 📋 Output

One dataset item per artwork or artist, depending on `entityType` — see
[`.actor/dataset_schema.json`](.actor/dataset_schema.json) for the full field list, or the
Output tab's **Artworks** / **Artists** views for a readable table.

**Example artwork record:**

```json
{
  "source": "saatchiart",
  "entity_type": "artworks",
  "external_id": "P1-U476-A11979-T1",
  "url": "https://www.saatchiart.com/art/Painting-Dinner-With-Tristan/476/4942/view",
  "artwork_title": "Dinner With Tristan",
  "artist_name": "Gregg Simpson",
  "artist_url": "https://www.saatchiart.com/greggsimpson",
  "category": "Painting",
  "mediums": "Oil, Other",
  "materials": "Canvas",
  "price_eur": 12165.0,
  "original_status": "avail",
  "height_cm": 152.4,
  "width_cm": 165.1,
  "change_type": "new"
}
```

**Example artist record:**

```json
{
  "source": "saatchiart",
  "entity_type": "artists",
  "external_id": "476",
  "url": "https://www.saatchiart.com/greggsimpson",
  "full_name": "Gregg Simpson",
  "country": "Canada",
  "followers_total": 1234,
  "total_artworks": 315,
  "sold_artworks": 42,
  "avg_price_eur": 987.5,
  "mediums": "Acrylic, Oil, Watercolor",
  "latest_artwork_title": "Improvised Inlet",
  "change_type": "new"
}
```

Results can be downloaded as JSON, CSV, or Excel from the Console's Output tab, or pulled via the
Apify API/dataset endpoint.

**Known gap:** `uploaded_at` is only populated for artworks discovered via the `category` input
(the search-API path carries a timestamp the sitemap detail-page path doesn't expose) — null
otherwise.

### ⚙️ Input

See [`.actor/input_schema.json`](.actor/input_schema.json) for the full JSON schema.

| Parameter | Type | Default | Description |
|---|---|---|---|
| `entityType` | String | `artworks` | `artworks` or `artists`. |
| `category` | String | *(none)* | Artworks only. Restrict discovery to one category via a faster search API instead of the sitemap. The API caps its reported result count at 10,000 regardless of a category's true size — combine with the filters below to narrow enough for complete, delta-trackable coverage; check the run log for "exhaustive" vs "capped". Leave empty for a full, always-exhaustive sitemap crawl. |
| `availability` | String | `avail` | Artworks only, category mode. Narrow by listing status: `avail`, `sold`, `reserved`, `unavail`, or empty for all. Including more statuses only widens the result count. |
| `subject` | String | *(none)* | Artworks only, category mode. e.g. `floral`, `landscape`, `abstract`, `portrait`. |
| `mediums` | String | *(none)* | Artworks only, category mode. e.g. `oil`, `acrylic`, `watercolor`, `black & white`. |
| `styles` | String | *(none)* | Artworks only, category mode. e.g. `realism`, `abstract expressionism`, `minimalism`. |
| `country` | String | *(none)* | Artworks only, category mode. Artist's country, e.g. `united states`, `sweden`. |
| `startUrls` | Array of strings | *(none)* | Artworks only. Specific artwork URLs to scrape directly, instead of discovering via sitemap or category. |
| `maxItems` | Integer | `50` | Stop after pushing this many dataset items. Defaults to a fast, cheap preview (also what keeps an unconfigured run within Apify's automated 5-minute QA check). A full crawl covers the entire site (hundreds of thousands of artworks, or the full artist directory) — raise this or clear it for that. |
| `mode` | String | `auto` | `auto` (recommended): full scan on the first run, incremental after. `full`: always push every item and refresh the baseline. `incremental`: always push only new/changed items. Only a plain, unscoped sitemap crawl can detect delistings or update the baseline. |
| `impersonate` | String | `firefox` (internal) | curl\_cffi TLS-impersonation target. SaatchiArt blocks plain requests, so this is set internally by default — override only if that stops working. |
| `proxyConfiguration` | Object | `{"useApifyProxy": false}` | Apify Proxy config. Off by default — try residential (not datacenter) proxies only if you start seeing blocks. |

### 🧪 Input examples

**Full artwork catalog scan** (exhaustive, slow — every artwork's detail page is fetched;
`maxItems: null` explicitly overrides the 50-item default):

```json
{ "entityType": "artworks", "maxItems": null }
```

**Quick category preview** (fast, but broad — capped at 10,000 results, not exhaustive; the default
run without `maxItems` set already gives you a 50-item preview):

```json
{ "entityType": "artworks", "category": "photography", "maxItems": 200 }
```

**Narrow category query** (fast *and* exhaustive — narrow enough to drop under the cap):

```json
{ "entityType": "artworks", "category": "photography", "country": "sweden", "subject": "floral", "maxItems": null }
```

**Full artist directory, with rollup stats:**

```json
{ "entityType": "artists", "maxItems": null }
```

**Scheduled tracking run** — only new/changed/delisted items since the last baseline:

```json
{ "entityType": "artworks", "mode": "incremental" }
```

### 🔄 Incremental (delta) mode

Every run classifies each item as `new`, `changed` (price or status moved, for artworks; catalog
size/sold-count/avg-price moved, for artists), `unchanged`, or `delisted`, using a state baseline
persisted in a named Apify Key-Value Store scoped to `entityType` (+ `category`, for artworks).

- `mode: auto` (default) — first run for a scope pushes everything (`full`); later runs push only
  `new`/`changed`/`delisted` (`incremental`).
- A `startUrls`-scoped artwork run is always partial and never updates the baseline. A
  `category`-scoped run updates the baseline only when it's narrow enough to be exhaustive (see
  [Input](#input)) — check the run log to see which case a given run landed in.

Full design: [`docs/incremental-mode.md`](../../docs/incremental-mode.md).

### 🚀 How to scrape SaatchiArt.com

1. Open the SaatchiArt Artist & Artwork Scraper in Apify Console and go to the **Input** tab.
2. Pick `entityType` (`artworks` or `artists`).
3. Optionally set `category` (artworks only) for a quick, cheaper preview, or leave it empty for
   the full, exhaustive catalog.
4. `maxItems` defaults to 50 (a quick preview) — raise it or clear it (set to `null`) for a full,
   uncapped run.
5. Click **Start**.
6. When the run finishes, browse results in the **Output** tab, or download as JSON/CSV/Excel, or
   fetch them via the API.
7. To track over time instead of scraping once: create a **Schedule** with `mode: auto` — the
   first run does a full scan, every run after only bills what actually changed.

### 🔗 You might also like

- **[Artmajeur Artist & Artwork Scraper](https://apify.com/artsiom_k/artmajeur-scraper)** — the same
  entity-type/rollup-stats design pointed at a second, independent art marketplace, for expanding
  artist coverage or cross-referencing the same artist's presence and pricing across platforms.
- **[Artsper Artist & Artwork Scraper](https://apify.com/artsiom_k/artsper-scraper)** — a third,
  independent art marketplace source, same design.

### ❓ FAQ

**Is it legal to scrape SaatchiArt.com?** It's legal to collect publicly available marketplace data
such as artwork listings, prices, and public artist profile information. Scrape it only with a
legitimate purpose under GDPR.

**How do I get only new/changed items?** Use `mode: auto` (or `incremental`) on a schedule — see
[Incremental mode](#incremental-delta-mode).

**Why is `avg_price_eur` different from what's shown on the site?** It's the artist's own catalog
average in SaatchiArt's Central-Europe reference price (`EU1C`), computed across every artwork —
not a single artwork's price.

### Search keywords

saatchiart scraper, saatchi art scraper, art marketplace scraper, artist data scraper, artwork data
scraper, art price data, art market analytics, saatchiart api, art collector data feed

# Actor input Schema

## `entityType` (type: `string`):

Scrape artworks or artist profiles. Each produces a different output shape (see dataset\_schema.json).

## `category` (type: `string`):

Restrict artwork discovery to one category via SaatchiArt's browse API — cheaper than a full sitemap crawl, but the browse API caps its reported result count at 10,000 regardless of a category's true size (a broad category like "photography" has hundreds of thousands of artworks). A run only counts as a complete, delta-trackable scan when the category (combined with the filters below, if any) narrows below that cap — check the run log for "exhaustive" vs "capped, narrow further". Leave category empty to crawl the full site sitemap instead (always exhaustive, but slower — every artwork's detail page is fetched individually). Ignored when entityType is "artists" or startUrls is set.

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

Narrows a category browse by listing status. Only applies when category is set (ignored for a full sitemap crawl, which always returns every status). Leave as "avail" (the default) unless you specifically want sold/reserved/unavailable pieces too — including them only widens the result count, working against the 10,000-result cap.

## `subject` (type: `string`):

Narrows a category browse to one subject, e.g. "floral", "landscape", "abstract", "portrait". Only applies when category is set. Combine with mediums/styles/country to get a specific-enough query to drop under the 10,000-result cap and become exhaustive — see the category field's description.

## `mediums` (type: `string`):

Narrows a category browse to one medium, e.g. "oil", "acrylic", "watercolor", "black & white" (for photography). Only applies when category is set.

## `styles` (type: `string`):

Narrows a category browse to one style, e.g. "realism", "abstract expressionism", "minimalism". Only applies when category is set.

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

Narrows a category browse to artists based in one country, e.g. "united states", "sweden". Only applies when category is set.

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

Optional list of specific SaatchiArt artwork URLs (e.g. https://www.saatchiart.com/art/.../view) to scrape directly, instead of discovering via sitemap or category. Overrides category when set. Ignored when entityType is "artists".

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

Stop after pushing this many dataset items. Defaults to 50 — a fast, cheap preview, and what keeps an unconfigured run within Apify's automated 5-minute QA check. A full, uncapped sitemap crawl covers the entire site — hundreds of thousands of artworks, or the full artist directory — raise this or clear it for that.

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

"auto" (recommended): full scan on the first run for a given entityType/scope, incremental (new/changed only) afterwards. "full": always push every item and refresh the baseline — schedule this periodically to catch delistings. "incremental": always push only new/changed items. Only a plain, unscoped sitemap crawl (no category, no startUrls) can detect delistings or update the baseline — see docs/incremental-mode.md.

## `impersonate` (type: `string`):

curl\_cffi browser TLS-impersonation target. SaatchiArt blocks plain (non-impersonated) requests, so this defaults to "firefox" internally — override only if that stops working.

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

Apify Proxy configuration. Leave off unless you start seeing blocks from your own IP reputation; if so, try residential rather than datacenter groups.

## Actor input object example

```json
{
  "entityType": "artworks",
  "category": "",
  "availability": "avail",
  "maxItems": 50,
  "mode": "auto",
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("artsiom_k/saatchiart-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 = {}

# Run the Actor and wait for it to finish
run = client.actor("artsiom_k/saatchiart-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 '{}' |
apify call artsiom_k/saatchiart-scraper --silent --output-dataset

```

## MCP server setup

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