Documentation
Consume the archive from your application.
The Grafpin public API returns the archive’s approved pieces — title, coordinates, city and photo — as JSON. It is read-only: it does not publish, does not modify, and never exposes data about the people who document the archive.
Base URL
https://api.grafpin.com/v1
The key belongs on your server, never in a browser
An API key identifies and bills its owner. If it ships inside a website’s JavaScript or a mobile app, anyone can extract it and spend your quota. Call the API from your backend and keep the key in an environment variable. That is also why the API does not enable CORS for third parties: a browser call failing is the correct behaviour, not a bug.
01 · Get started
Get started
Three steps, no paperwork.
- 01
Create your account at grafpin.com
The same account used to document the archive. You do not need to upload anything.
- 02
Generate a key at /api-keys
It is shown in full only once, when created — save it then. Afterwards you only see its prefix, to identify it. You can hold up to 5 active keys and revoke any of them at any time.
- 03
Send it in the X-API-Key header
On every request to /v1/pub. Without the header, or with a revoked key, the response is 401.
First call
curl -s https://api.grafpin.com/v1/pub/pieces \
-H "X-API-Key: $GRAFPIN_API_KEY" 02 · Endpoints
Endpoints
Two, both GET. That is all there is.
GET /v1/pub/pieces
List of approved pieces, newest first.
- page
- Page, from 1. Defaults to 1.
- per_page
- Pieces per page, between 1 and 30. Defaults to 30.
- city
- Partial match on city.
- style
- Filter by style.
- sw_lat, sw_lon, ne_lat, ne_lon
- Bounding box: south-west and north-east corners. All four or none.
GET /v1/pub/pieces/{id}
Detail of one piece: adds description, conservation state, styles, context and all its photographs. A piece that is not approved returns 404, whether it exists or not.
Response shape
Every response shares the same envelope. Data goes in data, pagination in meta.
{
"success": true,
"data": [
{
"id": 1042,
"title": "Muro de la 26",
"latitude": "4.65349200",
"longitude": "-74.08368000",
"city": "Bogotá",
"country": "CO",
"cover_url": "https://api.grafpin.com/v1/media/1042/thumb_400_a1b2.jpg?exp=…&v=1&sig=…",
"is_verified": true,
"created_at": "2026-04-12 18:22:05"
}
],
"message": "",
"errors": [],
"meta": { "page": 1, "per_page": 30, "total": 418 }
} Errors
- 401 Missing X-API-Key header, or the key is invalid or revoked.
- 404 The piece does not exist or is not approved.
- 429 Daily quota exhausted. Come back tomorrow or upgrade.
- 503 Temporary unavailability. Retry as indicated by Retry-After.
03 · Images
Images
Photos are not served from a fixed path: every URL is signed and expires.
Store the id, not the URL
The links in cover_url and photos[].url carry a signature and an expiry (a few minutes). After that they return 403. Request the piece again to get fresh links.
Preview size
The public API serves the reduced variant (longest side 300–400 px), enough for listings, maps and cards. Full resolution belongs to the official application only.
Attribution
The photographs belong to the people who document the archive. When you display them, link the piece on grafpin.com and credit Grafpin as the source.
04 · Limits and fair use
Limits and fair use
The quota is daily and per key. Responses tell you how much is left.
1,000 requests per day
That is the free plan, which every new key gets. No card, no paperwork.
Quota headers
Every response carries X-RateLimit-Limit and X-RateLimit-Remaining. When the quota runs out you get a 429: handle it, do not retry in a loop.
Cache on your side
The archive changes slowly. Keep listings in your own cache for a while instead of fetching on every visit — image links do need refreshing.
Need more?
Higher-quota plans exist, and access can be arranged for research or non-profit projects. Write to us.
05
Questions, or a use that does not fit here?
Tell us what you want to build. If your project needs something the public API does not cover — different volumes, another cut of the data — let’s talk.