# Museum & Cultural Heritage Collections Scraper (`ninhothedev/museum-collections-scraper`) Actor

$0.35/1K 🔥 Museum collections scraper! Artworks from Europeana, Smithsonian & Art Institute in one schema with images. No key. JSON, CSV, Excel or API in seconds. Build art datasets & exhibitions ⚡

- **URL**: https://apify.com/ninhothedev/museum-collections-scraper.md
- **Developed by:** [ninhothedev](https://apify.com/ninhothedev) (community)
- **Categories:**
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.35 / 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

## Museum & Cultural Heritage Collections Scraper

**Search three major open museum APIs at once — Europeana, the Smithsonian Open Access API and the Art Institute of Chicago — and get every artwork, artifact and cultural heritage object back in ONE unified schema.** No API key, no login, no account setup. Just type what you are looking for.

Museum data is notoriously fragmented. Every institution ships its own JSON shape: the Art Institute returns flat fields plus an IIIF image server, Europeana wraps almost every leaf in a list, and the Smithsonian buries titles five levels deep inside `content.descriptiveNonRepeating.title.content`. This Actor does the normalisation work so you can go straight to the data.

***

### How is this different from the Met Museum Scraper?

If you already use [**met-museum-scraper**](https://apify.com/ninhothedev/met-museum-scraper), here is the split:

| | met-museum-scraper | **museum-collections-scraper** (this Actor) |
|---|---|---|
| Sources | **1** — The Metropolitan Museum of Art | **3** — Europeana + Smithsonian + Art Institute of Chicago |
| Coverage | ~500,000 Met objects | Millions of objects across 4,000+ European institutions, 19 Smithsonian museums and the Art Institute |
| Schema | Met-native fields | **Unified cross-museum schema** — the same 26 columns no matter which museum a row came from |
| Best for | Deep dives into the Met collection | Comparative research, cross-institution datasets, large image corpora |
| `museum` column | n/a | `artic` | `europeana` | `smithsonian` |

They complement each other. Use the Met scraper when you need the Met specifically; use this one when you want **breadth across institutions in a single, join-ready table**.

***

### What you get

One row per artwork, identical columns for every source:

```json
{
  "museum": "artic",
  "object_id": "20029",
  "title": "Sunflowers, Marché St Germain, Paris",
  "artist": "James McNeill Whistler",
  "artist_display": "James McNeill Whistler\nAmerican, 1834–1903",
  "date_display": "1888/93",
  "year_start": 1888,
  "year_end": 1893,
  "place_of_origin": "France",
  "medium": "Watercolor, with black chalk, on ivory wove paper",
  "dimensions": "12.7 × 21.6 cm",
  "department": "Prints and Drawings",
  "classification": "drawing and watercolor",
  "object_type": "drawing and watercolor",
  "description": "A work made of watercolor on paper.",
  "image_url": "https://www.artic.edu/iiif/2/2e9476db-2d28-c197-25d8-9b525d63d5a0/full/843,/0/default.jpg",
  "thumbnail_url": "https://www.artic.edu/iiif/2/2e9476db-2d28-c197-25d8-9b525d63d5a0/full/200,/0/default.jpg",
  "is_public_domain": true,
  "rights": "Public Domain",
  "credit_line": "Gift of Bertha E. Brown",
  "data_provider": "Art Institute of Chicago",
  "country": null,
  "detail_url": "https://www.artic.edu/artworks/20029",
  "query": "sunflower",
  "source": "api.artic.edu",
  "scraped_at": "2026-07-28T14:52:11Z"
}
```

#### Field reference

| Field | Meaning |
|---|---|
| `museum` | `artic`, `europeana` or `smithsonian` |
| `object_id` | Stable identifier inside that museum |
| `title` | Object title |
| `artist` | Creator name (plain) |
| `artist_display` | Creator with life dates / nationality, when the source has it |
| `date_display` | Human-readable date exactly as the museum publishes it |
| `year_start` / `year_end` | Numeric year range |
| `place_of_origin` | Geographic origin |
| `medium` | Materials / technique |
| `dimensions` | Physical size |
| `department` | Curatorial department or collection |
| `classification` | Museum classification |
| `object_type` | Object type (painting, sculpture, IMAGE, TEXT…) |
| `description` | Description / notes, capped at 2,000 characters |
| `image_url` | Full-size image |
| `thumbnail_url` | Small preview |
| `is_public_domain` | `true` / `false` / `null` (see the rights note below) |
| `rights` | Rights statement or licence URI |
| `credit_line` | Acquisition / credit text |
| `data_provider` | Holding institution |
| `country` | Country of the holding institution (Europeana) |
| `detail_url` | Public page for the object |
| `query` | The search term that produced this row |
| `source` | API host the row came from |
| `scraped_at` | UTC timestamp |

**Every field is nullable.** When a source does not publish a concept, the column is `null` — never a guessed or invented value. Europeana, for example, has no `dimensions` field in its search API, so that column is `null` for Europeana rows rather than filled with something plausible.

***

### Input

```json
{
  "mode": "search",
  "queries": ["sunflower", "samurai"],
  "sources": ["artic", "europeana", "smithsonian"],
  "maxItemsPerSource": 50,
  "maxItems": 300
}
```

| Input | Type | Default | What it does |
|---|---|---|---|
| `mode` | select | `search` | Currently one mode: keyword search across the selected APIs |
| `queries` | array | `["sunflower","samurai"]` | Free-text terms — artist, motif, culture, material, period |
| `sources` | array | all three | Which museum APIs to query |
| `maxItemsPerSource` | integer | `50` | Cap per source **per query**, so one huge collection cannot dominate |
| `maxItems` | integer | `300` (max 2000) | Hard stop for the whole run |

**A failing source never kills the run.** If Europeana is down or the Smithsonian rate limit is hit, the Actor logs a warning and keeps the other sources going. The run only fails when *every* selected source returned zero items.

***

### Pricing

Roughly **$0.50 per 1,000 artworks** on the Apify platform (512 MB memory, pay-per-usage). A typical exploratory run of 300 objects across all three museums costs a few cents and finishes in well under a minute.

***

### Use cases

- **Art datasets** — build a cross-institution corpus of paintings, sculptures or textiles with consistent columns, ready for pandas or a database import.
- **AI training data** — harvest image URLs plus rich, human-written captions (title, medium, date, description) with explicit rights information, and filter to `is_public_domain: true` when you need permissive material.
- **Education** — assemble teaching packs for a period, region or artist across European and American collections in one query.
- **Digital exhibitions** — power a virtual gallery, a museum-comparison site or a "this day in art history" feed with live links back to each institution.

Also popular for: provenance research, iconography studies, collection-gap analysis, and generative-art reference libraries.

***

### Data sources, keys and limits

All three APIs are queried with **public credentials that ship with the Actor**. You never supply a key.

| Source | Auth | Notes and limits |
|---|---|---|
| **Art Institute of Chicago** (`api.artic.edu`) | **None at all** — fully open | The most generous of the three. Images are delivered through the museum's IIIF server; the Actor builds `{iiif_url}/{image_id}/full/843,/0/default.jpg` for `image_url` and a 200 px variant for `thumbnail_url`. Note that the IIIF host expects a browser-style request — when downloading images programmatically, send a `Referer: https://www.artic.edu/` header. |
| **Europeana** (`api.europeana.eu`) | Public **demo key `api2demo`** | A shared testing key intended for evaluation, so it is rate limited and shared with every other demo user. For heavy production use, request a free personal key at [pro.europeana.eu](https://pro.europeana.eu/pages/get-api). The Actor uses `profile=rich` so creator names arrive as readable labels rather than bare VIAF URIs. |
| **Smithsonian Open Access** (`api.si.edu`) | Public **`DEMO_KEY`** | The strictest limit of the three: the response advertises `x-ratelimit-limit: 10`, and sustained querying returns `429 OVER_RATE_LIMIT`. The Actor spaces requests out and backs off on 429. A free personal key from [api.data.gov](https://api.data.gov/signup/) lifts this substantially. Because relevance-sorted Smithsonian results are dominated by library catalogue records with no media, the Actor queries digitised objects (`online_visual_material:true`) first and then fills the remaining slots from the plain keyword query. |

#### Rights and public domain

`is_public_domain` is only set when the source states it:

- **Art Institute** — taken directly from the museum's own `is_public_domain` flag.
- **Europeana** — `true` when the rights URI contains `publicdomain` or CC0, `false` for a restrictive licence, `null` when no rights statement exists.
- **Smithsonian** — always `null`. The Smithsonian publishes a CC0 flag for its *metadata*, which is not the same as a public-domain statement for the object itself, so the Actor refuses to infer one. The raw value is still available in the `rights` column.

Always verify rights on the museum's own page (`detail_url`) before republishing an image.

***

### Related Actors

- [Met Museum Scraper](https://apify.com/ninhothedev/met-museum-scraper) — deep, single-museum coverage of The Metropolitan Museum of Art
- [Internet Archive Scraper](https://apify.com/ninhothedev/internet-archive-scraper) — books, audio, film and software from archive.org
- [Project Gutenberg Scraper](https://apify.com/ninhothedev/project-gutenberg-scraper) — 70,000+ public-domain ebooks with full metadata
- [Wikivoyage Scraper](https://apify.com/ninhothedev/wikivoyage-scraper) — free travel guides, districts and points of interest

***

### FAQ

**Do I need an API key?** No. All three sources are reached with public or demo credentials bundled with the Actor.

**Why are some fields empty?** Because the source genuinely does not publish them. Europeana's search API has no `medium` or `dimensions`; natural-history records have no artist. The Actor emits `null` instead of inventing values.

**Why does one query return fewer Smithsonian rows than requested?** The public `DEMO_KEY` throttles hard. Lower `maxItemsPerSource`, or run Smithsonian queries in a separate run.

**Can I get only images I'm allowed to reuse?** Filter the dataset on `is_public_domain: true`, or inspect `rights` for a CC0 / CC-BY licence.

**Which formats can I export?** Anything Apify supports: JSON, CSV, Excel, XML, JSONL, plus the API and integrations (Google Sheets, S3, webhooks).

***

### Legal

This Actor reads publicly documented, officially provided open-data APIs and respects their published rate limits. Object metadata and images remain subject to each institution's own terms and to the rights statement recorded in the `rights` column.

# Actor input Schema

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

What the Actor should do. Currently only 'search' is supported: it runs every search term from the 'queries' list against every selected museum API and returns the matching artworks and artifacts.

## `queries` (type: `array`):

One or more free-text search terms, e.g. an artist name ('van gogh'), a motif ('sunflower'), a culture ('samurai') or a material ('bronze'). Every term is sent to every selected museum API.

## `sources` (type: `array`):

Which museum APIs to query. 'artic' = Art Institute of Chicago (fully open, no key), 'europeana' = Europeana aggregator of 4,000+ European institutions (public demo key), 'smithsonian' = Smithsonian Open Access across 19 museums (public DEMO\_KEY). A source that fails only produces a warning; the other sources keep running.

## `maxItemsPerSource` (type: `integer`):

Upper limit of artworks fetched from each museum API for each single search term. Keeps one large collection (e.g. Europeana) from dominating the whole dataset.

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

Hard stop for the whole run across all queries and all sources. The Actor stops pushing data as soon as this number of artworks has been saved.

## Actor input object example

```json
{
  "mode": "search",
  "queries": [
    "sunflower",
    "samurai"
  ],
  "sources": [
    "artic",
    "europeana",
    "smithsonian"
  ],
  "maxItemsPerSource": 50,
  "maxItems": 300
}
```

# Actor output Schema

## `overview` (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 = {
    "queries": [
        "sunflower",
        "samurai"
    ],
    "sources": [
        "artic",
        "europeana",
        "smithsonian"
    ],
    "maxItemsPerSource": 50,
    "maxItems": 300
};

// Run the Actor and wait for it to finish
const run = await client.actor("ninhothedev/museum-collections-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 = {
    "queries": [
        "sunflower",
        "samurai",
    ],
    "sources": [
        "artic",
        "europeana",
        "smithsonian",
    ],
    "maxItemsPerSource": 50,
    "maxItems": 300,
}

# Run the Actor and wait for it to finish
run = client.actor("ninhothedev/museum-collections-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 '{
  "queries": [
    "sunflower",
    "samurai"
  ],
  "sources": [
    "artic",
    "europeana",
    "smithsonian"
  ],
  "maxItemsPerSource": 50,
  "maxItems": 300
}' |
apify call ninhothedev/museum-collections-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,ninhothedev/museum-collections-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/b53wd2npgIVukPCF8/builds/4Y5IbMfpHC38CBN0r/openapi.json
