Van Gogh Museum APIvangoghmuseum.nl ↗
Access the Van Gogh Museum collection via API. Search artworks, retrieve full metadata, multi-resolution images, provenance, and collection filters.
What is the Van Gogh Museum API?
The Van Gogh Museum API provides access to the museum's art collection through 4 endpoints, covering artwork search, detailed metadata retrieval, and filter discovery. Using get_artwork_detail, you can pull a single work's title, description, artist, dimensions, provenance, credits, catalogue numbers (f-number, jh-number), and image URLs at thumbnail, medium, and large resolutions — all in one response.
curl -X GET 'https://api.parse.bot/scraper/4aa2462d-3c4b-444f-b172-5aff7a992bd6/get_collection?query=sunflowers&offset=0' \ -H 'X-API-Key: $PARSE_API_KEY'
Typed, relational, agent-ready
A generated client with real types, enums, and the links between objects — the structure a flat JSON response can't carry. Autocompletes in your editor and reads cleanly to coding agents.
- Fully typed · autocompletes
- Objects link to objects
- Typed errors & pagination
Typed Python client. Set up the SDK in your uv project, then pull this API’s typed client:
uv add parse-sdk uv run parse init uv run parse add --marketplace vangoghmuseum-nl-api
uv run parse add --marketplace pulls a pinned snapshot of this canonical API — it won’t change underneath you. To customize it, subscribe and swap to your own copy.
"""
Van Gogh Museum Collection API Client
Get your API key from: https://parse.bot/settings
"""
import os
import requests
from typing import Any, Dict, List, Optional
class ParseClient:
"""Client for interacting with the Van Gogh Museum Collection API via Parse."""
def __init__(self, api_key: Optional[str] = None):
"""
Initialize the Parse client.
Args:
api_key: API key for authentication. If not provided, reads from PARSE_API_KEY env var.
Raises:
ValueError: If API key is not provided and PARSE_API_KEY env var is not set.
"""
self.base_url = "https://api.parse.bot"
self.scraper_id = "4aa2462d-3c4b-444f-b172-5aff7a992bd6"
self.api_key = api_key or os.getenv("PARSE_API_KEY")
if not self.api_key:
raise ValueError("API key not provided and PARSE_API_KEY env var not set")
def _call(self, endpoint: str, method: str = "POST", **params) -> Dict[str, Any]:
"""
Make an API call to the Parse endpoint.
Args:
endpoint: The endpoint name (e.g., "get_collection")
method: HTTP method ("GET" or "POST")
**params: Query/body parameters for the endpoint
Returns:
Response JSON as dictionary
Raises:
requests.RequestException: If the API call fails
"""
url = f"{self.base_url}/scraper/{self.scraper_id}/{endpoint}"
headers = {
"X-API-Key": self.api_key,
"Content-Type": "application/json"
}
if method.upper() == "GET":
response = requests.get(url, headers=headers, params=params)
elif method.upper() == "POST":
response = requests.post(url, headers=headers, json=params)
else:
raise ValueError(f"Unsupported HTTP method: {method}")
response.raise_for_status()
return response.json()
def get_collection(
self,
query: str = "",
offset: int = 0
) -> Dict[str, Any]:
"""
Browse or search the museum collection.
Args:
query: Search keyword to filter artworks (optional)
offset: Result offset for pagination
Returns:
Dictionary containing items, count, and offset
"""
return self._call("get_collection", method="GET", query=query, offset=offset)
def search_artworks(
self,
query: str,
offset: int = 0
) -> Dict[str, Any]:
"""
Search for artworks by keyword.
Args:
query: Search keyword (required)
offset: Result offset for pagination
Returns:
Dictionary containing items, count, and offset
"""
return self._call("search_artworks", method="GET", query=query, offset=offset)
def get_artwork_detail(self, artwork_id: str) -> Dict[str, Any]:
"""
Retrieve full details for a single artwork by its ID.
Args:
artwork_id: Artwork ID (e.g., 's0031V1962')
Returns:
Dictionary containing full artwork details including metadata and images
"""
return self._call("get_artwork_detail", method="GET", artwork_id=artwork_id)
def get_collection_filters(self) -> Dict[str, Any]:
"""
Retrieve available filter categories and their values for the collection.
Returns:
Dictionary containing list of available filters with categories and values
"""
return self._call("get_collection_filters", method="GET")
def main():
"""Practical workflow demonstrating the Van Gogh Museum Collection API."""
# Initialize client
client = ParseClient()
print("=" * 80)
print("VAN GOGH MUSEUM COLLECTION EXPLORER")
print("=" * 80)
# Step 1: Get available filters to understand what we can search
print("\n[STEP 1] Fetching available collection filters...")
try:
filters_response = client.get_collection_filters()
filters_data = filters_response.get("data", {})
filters_list = filters_data.get("filters", [])
print(f"✓ Available filter categories: {len(filters_list)}")
for filter_item in filters_list[:3]:
category = filter_item.get("category", "Unknown")
total_options = filter_item.get("total_options", 0)
print(f" - {category} ({total_options} options)")
except Exception as e:
print(f"✗ Error fetching filters: {e}")
return
# Step 2: Search for artworks using a specific query
search_term = "Sunflowers"
print(f"\n[STEP 2] Searching for artworks matching '{search_term}'...")
try:
search_results = client.search_artworks(query=search_term, offset=0)
search_data = search_results.get("data", {})
total_count = search_data.get("count", 0)
items = search_data.get("items", [])
if total_count == 0:
print(f"✗ No artworks found for '{search_term}'")
return
print(f"✓ Found {total_count} artwork(s)")
print(f" Displaying first {min(3, len(items))} results...\n")
except Exception as e:
print(f"✗ Error during search: {e}")
return
# Step 3: Process each search result and fetch detailed information
detailed_artworks = []
for idx, item in enumerate(items[:3], 1):
artwork_id = item.get("id")
title = item.get("title", "Unknown")
creator = item.get("creator", "Unknown")
position = item.get("position", "N/A")
print(f"[Artwork {idx}] {title}")
print(f" ID: {artwork_id}")
print(f" Creator: {creator if creator else 'Unknown'}")
print(f" Position in collection: {position}")
try:
# Fetch detailed information for this artwork
artwork_detail_response = client.get_artwork_detail(artwork_id)
artwork_detail = artwork_detail_response.get("data", {})
detailed_artworks.append(artwork_detail)
# Display full details
description = artwork_detail.get("description", "No description available")
print(f" Description: {description[:120]}{'...' if len(description) > 120 else ''}")
# Display metadata
metadata = artwork_detail.get("metadata", {})
if metadata:
artist = metadata.get("artist", "Unknown")
dimensions = metadata.get("dimensions", "Unknown")
f_number = metadata.get("f-number", "N/A")
print(f" Artist: {artist}")
if dimensions:
print(f" Dimensions: {dimensions}")
if f_number != "N/A":
print(f" F-Number: {f_number}")
# Display tags if available
tags = artwork_detail.get("tags", [])
if tags:
print(f" Tags: {', '.join(tags)}")
# Display image information
images = artwork_detail.get("images", [])
if images:
image = images[0]
thumbnail_url = image.get("thumbnail", "N/A")
medium_url = image.get("medium", "N/A")
large_url = image.get("large", "N/A")
print(f" Image resolutions available:")
print(f" - Thumbnail: {thumbnail_url[:50]}...")
print(f" - Medium: {medium_url[:50]}...")
print(f" - Large: {large_url[:50]}...")
except Exception as e:
print(f" ✗ Error fetching details: {e}")
print()
# Step 4: Demonstrate pagination for larger result sets
if total_count > 3:
print("[STEP 3] Demonstrating pagination...")
try:
next_page_results = client.search_artworks(query=search_term, offset=3)
next_page_data = next_page_results.get("data", {})
next_items = next_page_data.get("items", [])
next_offset = next_page_data.get("offset", 3)
if next_items:
print(f"✓ Retrieved next page (offset={next_offset})")
print(f" Results {next_offset + 1}-{next_offset + len(next_items)} of {total_count}:")
for item in next_items[:2]:
title = item.get("title", "Unknown")
print(f" - {title}")
else:
print(" No more results available")
except Exception as e:
print(f"✗ Error fetching next page: {e}")
# Step 5: Browse full collection without search
print("\n[STEP 4] Browsing full collection (first page)...")
try:
collection_results = client.get_collection(offset=0)
collection_data = collection_results.get("data", {})
all_items = collection_data.get("items", [])
collection_count = collection_data.get("count", 0)
print(f"✓ Total items in collection: {collection_count}")
print(f" Showing first {min(2, len(all_items))} items:")
for item in all_items[:2]:
title = item.get("title", "Unknown")
item_id = item.get("id", "Unknown")
print(f" - {title} (ID: {item_id})")
except Exception as e:
print(f"✗ Error browsing collection: {e}")
# Step 6: Summary
print("\n" + "=" * 80)
print(f"SUMMARY: Successfully retrieved detailed information for {len(detailed_artworks)} artwork(s)")
print("=" * 80)
if __name__ == "__main__":
main()Browse or search the museum collection. Returns a paginated list of artworks with basic info including title, creator, image URL, and link to the artwork page.
| Param | Type | Description |
|---|---|---|
| query | string | Search keyword to filter artworks (e.g. 'sunflowers'). Omitting returns the full collection. |
| offset | integer | Number of items to skip for pagination. |
{
"type": "object",
"fields": {
"count": "integer total number of items returned in this response",
"items": "array of artwork summary objects with id, title, creator, url, image_url, and position",
"offset": "integer offset used in this request"
},
"sample": {
"data": {
"count": 11,
"items": [
{
"id": "s0031V1962",
"url": "https://www.vangoghmuseum.nl/en/collection/s0031V1962",
"title": "Sunflowers",
"creator": "",
"position": "1",
"image_url": "https://iiif.micr.io/TZCqF/full/200"
}
],
"offset": 0
},
"status": "success"
}
}About the Van Gogh Museum API
Collection Browsing and Search
Two endpoints handle discovery: get_collection and search_artworks. Both return paginated lists of artwork summaries — each item includes an id, title, creator, url (link to the museum page), image_url, and position. get_collection accepts an optional query parameter; omitting it returns the full collection. search_artworks requires a query string (e.g. 'self-portrait', 'night'). Both support an offset integer for pagination, and both return a count field reflecting how many items came back in the current page.
Artwork Detail
get_artwork_detail takes a single required artwork_id — obtainable from any collection or search result — and returns the full record for that work. The metadata object contains structured fields: artist, dimensions, provenance, credits, object_number, f-number, and jh-number (the standard Van Gogh catalogue references). The images array provides multiple resolution variants: thumbnail, medium, and large URLs, plus an id per image. The tags array and free-text description field round out the record.
Filter Discovery
get_collection_filters takes no inputs and returns all available filter categories for the collection. Each category object includes a category label, a param_name for use in queries, a total_options count, and a values array where each entry has a name, value, and result count. Documented categories include On view, Artist, Location, Year, Object type, Genre, and Sub-collections — useful for building faceted browsing UIs or scoping batch requests to a specific artist or object type.
The Van Gogh Museum API is a managed, monitored endpoint for vangoghmuseum.nl — not a raw scraper you maintain. Every endpoint is automatically health-checked on a schedule, and when vangoghmuseum.nl changes and a check fails, the API is automatically queued for repair and re-verified. It is built to keep working as the site underneath it changes.
This isn't an official vangoghmuseum.nl API — it's an independent, maintained REST wrapper over public data. Where the source has no official API (or only a limited one), Parse gives you a stable contract over a source that never promised one, and keeps it current. Need a new endpoint or field? You can revise it yourself in plain English and the agent rebuilds it against the live site in minutes — contributing the change back to the shared API is free.
Will this API break when the source site changes?+
Is this an official API from the source site?+
Can I fix or extend this API myself if I need a new endpoint or field?+
What happens if I call an endpoint that has an issue?+
- Building a searchable gallery app using
search_artworksresults withimage_urlandtitlefields. - Aggregating Van Gogh catalogue data by pulling
f-numberandjh-numberfromget_artwork_detailmetadata. - Rendering faceted collection browsers using filter categories and counts from
get_collection_filters. - Displaying provenance and credits for individual works in an academic or educational context.
- Paginating through the full collection with
get_collectionandoffsetto build a local artwork index. - Feeding multi-resolution artwork images into a print-on-demand or digital exhibition pipeline using
largeimage URLs. - Filtering works by object type or year using values returned from
get_collection_filtersto scope targeted queries.
| Tier | Price | Credits/month | Rate limit |
|---|---|---|---|
| Free | $0/mo | 100 | 5 req/min |
| Hobby | $30/mo | 1,000 | 20 req/min |
| Developer | $100/mo | 5,000 | 100 req/min |
One credit = one API call regardless of which marketplace API you call. Exceeding the rate limit returns a 429 response. Authenticate with the X-API-Key header.
Does the Van Gogh Museum have an official developer API?+
What catalogue identifiers does `get_artwork_detail` return?+
metadata object includes f-number (the De la Faille catalogue number) and jh-number (the Jan Hulsker catalogue number), alongside object_number, dimensions, provenance, and credits. These are the standard references used in Van Gogh scholarship.Does the API cover letters, drawings, and prints, or only paintings?+
get_collection_filters endpoint exposes an Object type category that breaks down available types and their counts.Does the API expose exhibition schedules or visitor information?+
How does pagination work across endpoints?+
get_collection and search_artworks accept an offset integer parameter to skip a given number of items. Each response returns a count of items in that page and echoes back the offset used, so you can step through the collection in batches.