# ArtBrokerage.com Artist & Artwork Scraper (`artsiom_k/artbrokerage-scraper`) Actor

Scrape ArtBrokerage.com listings and artist profiles, with rich per-listing condition/provenance detail and real buyer-demand signals. No login required.

- **URL**: https://apify.com/artsiom\_k/artbrokerage-scraper.md
- **Developed by:** [Artsiom Kunitsyn](https://apify.com/artsiom_k) (community)
- **Categories:** Other, E-commerce
- **Stats:** 2 total users, 1 monthly users, 100.0% 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/actors/running/actors-in-store.md#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

## artbrokerage-scraper

Scrapes **artist profiles** and **artwork listings** from
[ArtBrokerage.com](https://www.artbrokerage.com), a US-based secondary-market art marketplace — via
the site's own public sitemaps, no login required.

### Contents

- [Key features](#key-features)
- [Output](#output)
- [Input](#input)
- [Input examples](#input-examples)
- [Incremental (delta) mode](#incremental-delta-mode)
- [How to scrape ArtBrokerage.com](#how-to-scrape-artbrokeragecom)
- [FAQ](#faq)

### 🔑 Key features

- **Two entity types, one Actor.** Set `entityType` to `artworks` or `artists`.
- **Rich per-listing detail** most marketplace scrapers don't carry: condition, signature details,
  framing, provenance ("Purchased from" / "Provenance history"), a free-text backstory, and the
  certificate-of-authenticity source — genuinely server-rendered on every listing page.
- **A real buyer-demand signal.** Every artist record includes `collector_wanted_count` — genuine
  named-collector buy requests for that artist, a field no sibling art-marketplace Actor in this
  collection has (kept separate from `broker_wanted_count`, ArtBrokerage's own generic standing
  interest, present for nearly every artist and not itself a strong demand signal — see FAQ).
- **A genuine for-sale/sold flag** on every artwork.
- **Delta mode built in.** Every run classifies each item as `new`, `changed`, `unchanged`, or
  `delisted` against a persisted baseline.
- **`maxItems` defaults to 50** — a fast preview, and what keeps an unconfigured run within Apify's
  automated 5-minute QA check. ArtBrokerage's real catalog is substantial (~40,000 listings,
  \~5,800 artists) — clear `maxItems` (`null`) for a full run.

### 📋 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": "artbrokerage",
  "entity_type": "artworks",
  "external_id": "120261",
  "url": "https://www.artbrokerage.com/Larry-Rivers/Madama-Butterfly-1978-120261",
  "title": "Madama Butterfly 1978",
  "artist_name": "Larry Rivers",
  "artist_url": "https://www.artbrokerage.com/Larry-Rivers",
  "category": "Limited Edition Print",
  "medium": "Mixed Media Work in Eight Colors in Lithography And Three Colors",
  "size_raw": "22.75x30.5 in | 58x77 cm",
  "height_in": 22.75,
  "width_in": 30.5,
  "edition": "From the edition of 250",
  "sold": false,
  "price": 2700.0,
  "currency": "USD",
  "price_note": "Mixed Media - Blue Chip",
  "year": "1978",
  "condition": "Excellent",
  "purchased_from": "Gallery",
  "provenance": "Circle Gallery, NY",
  "certificate_of_authenticity": "Art Brokerage",
  "art_styles": ["Contemporary"],
  "art_collections": ["Well Priced", "Blue Chip - Post War - MASTERS"],
  "change_type": "new"
}
```

**Example artist record:**

```json
{
  "source": "artbrokerage",
  "entity_type": "artists",
  "external_id": "Pablo-Picasso",
  "url": "https://www.artbrokerage.com/Pablo-Picasso",
  "name": "Pablo Picasso",
  "location": "Spain",
  "bio": "Art Brokerage: Park West Artist: Pablo Picasso Blue Chip Spanish...",
  "for_sale_count": 151,
  "recent_sold_count": 16,
  "broker_wanted_count": 4,
  "collector_wanted_count": 7,
  "change_type": "new"
}
```

### 🔧 Input

| Field | Type | Default | Description |
|---|---|---|---|
| `entityType` | string | `artworks` | `artworks` or `artists`. |
| `maxItems` | integer | `50` | Stop after pushing this many items. Set to `null` for a full crawl. |
| `mode` | string | `auto` | `auto` / `full` / `incremental` — see [Incremental mode](#incremental-delta-mode). |
| `concurrency` | integer | `10` | How many listing/artist pages to fetch in parallel. Automatically capped to 8 for `entityType=artists` regardless of this value — see FAQ. |
| `impersonate` | string | `chrome` | curl\_cffi TLS-impersonation target. |
| `proxyConfiguration` | object | off | Apify Proxy config — not needed; no anti-bot friction found. |

### 📥 Input examples

**Default preview (50 artworks):**

```json
{ "entityType": "artworks" }
```

**Full artist directory:**

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

**Full listing catalog:**

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

### 🔁 Incremental (delta) mode

`auto` mode does a full scan the first time it runs for a given `entityType`, then only pushes
new/changed items on later runs. Only an uncapped run (no `maxItems` limit reached) can detect
delistings, sold-status changes, or price changes, or update the baseline.

### 🌐 How to scrape ArtBrokerage.com

ArtBrokerage has no JSON API of any kind — this Actor discovers its scope entirely via the site's
own public sitemaps (`sitemap-artists.xml`, `sitemap-listings.xml`) and reads each artwork/artist
page's own server-rendered HTML, the same content your browser sees.

### ❓ FAQ

**Why is `price` null on some for-sale (not sold) listings?**
Some (typically higher-value) listings show a price *tier* instead of an exact figure — the site
displays literal "$$$$$$$" characters with the real range only in a tooltip. This Actor reads that
range into `price_min`/`price_max` instead, leaving `price` null since no single figure exists.

**What's the difference between `broker_wanted_count` and `collector_wanted_count`?**
`broker_wanted_count` is ArtBrokerage's own standing "we'll help you sell this artist's work"
interest — present at a low count for nearly every artist checked, including ones with zero
for-sale listings, so it isn't a meaningful demand signal by itself. `collector_wanted_count` is
genuine named-collector buy requests, confirmed to be entirely absent (not zero) from the page
until at least one real request exists — this is the field worth watching for real demand.

**Does this include realized/historical sale prices?**
No. A sold listing on this site shows only a plain "SOLD" tag — no price. An artist's "Recently
Sold" section is a small, capped preview (also with no price), not a queryable total or archive.
`recent_sold_count` reflects only what that preview shows, not a true total sold count.

**Why is `price` sometimes accompanied by odd text like "23 Watchers" or "Inquire"?**
That's `price_note` — ArtBrokerage's own marketing copy next to the price on some listings. It's
kept verbatim (with decorative emoji stripped) since it occasionally carries real signal (a watcher
count, a negotiability hint), but it's free text, not a structured field.

**Some `size_raw` values look implausible (e.g. "1312x1034 in").**
A real, confirmed site-side data-entry bug on ArtBrokerage's own pages (a missing decimal point) —
not a scraping error. The paired cm value is mathematically derived from the same (buggy) inch
value, so it inherits the same error rather than independently confirming it. Passed through as-is.

**Does this need a proxy?**
No — no anti-bot friction was found anywhere on this site (plain `nginx`, no Cloudflare or
equivalent challenge). The origin server is simply slow (several seconds per page even under
concurrency) — a full uncapped crawl takes hours, which is expected, not a sign of blocking.

**Why is `entityType=artists` capped at concurrency 8 no matter what I set?**
A real large-scale test found artist pages are fast/light enough that even moderate concurrency
drives a high sustained request rate, and above roughly 8 concurrent requests that rate reliably
triggers a real HTTP 403 block for the rest of that run (confirmed: concurrency 20 failed within 3
minutes; concurrency 8 ran clean across 4,500+ consecutive real artist pages). This Actor enforces
the safe value automatically rather than exposing a setting that can silently lose half a run's
results.

# Actor input Schema

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

Scrape artworks or artist profiles. Both entity types cover the whole site via ArtBrokerage's own sitemaps (no JSON API exists). Artwork records include rich per-listing detail (condition, signature, provenance, backstory, certificate of authenticity) and a genuine for-sale/sold flag — but no realized sold price exists anywhere on this site. Artist records include a real for-sale count, a capped 'recently sold' preview, and a real buyer-demand 'wanted' count. See dataset\_schema.json for the full field list per entity type.

## `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. ArtBrokerage's real catalog is substantial (~40,000 listings, ~5,800 artists as of this build) — clear this (set to null) for a full crawl. Note: the site's own server is genuinely slow (5-11s per listing page even under concurrency), so a full uncapped crawl takes hours, not minutes.

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

"auto" (recommended): full scan on the first run for a given entityType, incremental (new/changed only) afterwards. "full": always push every item and refresh the baseline — schedule this periodically to catch delistings/price/sold-status changes. "incremental": always push only new/changed items. Only an uncapped run can detect delistings or update the baseline.

## `concurrency` (type: `integer`):

How many listing/artist pages to fetch in parallel. Defaults to 10. A real sustained large-scale test found artist pages (fast/light, so moderate concurrency drives a high request rate) can trigger a real rate limit above ~8 concurrent requests — this Actor automatically caps concurrency to 8 for entityType=artists regardless of this input. Listing/artwork pages are naturally slow (5-11s each) and not subject to that cap, but sustained crawling there can also hit occasional (self-recovering, no data loss) origin-side stalls — raise this cautiously, higher values increase how many requests get caught in one of those stalls at once.

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

curl\_cffi browser TLS-impersonation target. Defaults to "chrome" internally, though no anti-bot friction was found anywhere on this site (plain nginx, no Cloudflare or equivalent challenge). Override only if that stops holding.

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

Apify Proxy configuration. Leave off unless you start seeing blocks from your own IP reputation — no anti-bot friction was found while building this actor.

## Actor input object example

```json
{
  "entityType": "artworks",
  "maxItems": 50,
  "mode": "auto",
  "concurrency": 10,
  "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/artbrokerage-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/artbrokerage-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/artbrokerage-scraper --silent --output-dataset

```

## MCP server setup

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