Live site · Research report (plain-language, non-technical)
Looks up an artwork's ownership history across free public sources and emits a provenance passport with a confidence score computed by a fixed algorithm (not by the AI).
https://provenance-search-production.up.railway.app
index.html— static frontend (text form, image upload, camera capture)server.js— Express backend; the only thing that talks to external APIs- Frontend only ever calls its own server:
POST /api/identify,POST /api/verify - All API keys live in
.envon the server and are never sent to the browser
- Text form — title, artist, period, medium (+ optional last known sale price)
- Upload image — pick a photo from disk
- Camera — opens the device camera (works on mobile, e.g. in a museum); falls back to a helpful error if the browser denies camera access
Providing an image calls /api/identify (Gemini Vision, free tier), which
auto-fills the form and automatically triggers /api/verify.
| Source | Used for | Key required? |
|---|---|---|
| Tavily Search API | Primary research engine — provenance history, looting alerts, ownership records, restricted to metmuseum.org, getty.edu, interpol.int, unesco.org, artloss.com, lostart.de, lootedart.com, christies.com, sothebys.com, artnet.com, fbi.gov, ifar.org, wikipedia.org | Yes (free dev tier) |
| The Met Museum API | Supplementary: museum collection records | No |
| Art Institute of Chicago API | Supplementary: museum collection + provenance text | No |
| MoMA open dataset | Supplementary: MoMA collection records (bundled locally, see below) | No |
| Wikipedia API | Supplementary: general background/encyclopedic hits | No |
| Wikidata (SPARQL) | Supplementary: structured facts — inception date, collection history, ownership, significant events | No |
| Europeana API | Supplementary: European cultural heritage records | Yes (free) |
| Gemini API | Vision-based identification + synthesizing the timeline/flags from the facts above | Yes (free tier) |
Tavily is the main research engine: it's the first source Gemini is told to weight most heavily, and the only source whose hits trigger the deterministic high-severity "watchlist match" flag (a hit on interpol.int, artloss.com, lostart.de, lootedart.com, or fbi.gov). The rest are supplementary — they corroborate or add exact dates/accession records around what Tavily finds.
MoMA has no live public search API — its website is behind Cloudflare's bot
protection, and its only open data is a static export on GitHub
(MuseumofModernArt/collection).
A slimmed, gzip-compressed copy (title/artist/date/medium/credit line/URL for
~159k works, ~3.7MB) is bundled at data/moma-artworks.json.gz and loaded
into memory at startup — no network call needed at request time. To refresh
it from the upstream dataset:
npm run build:momaFor very famous, well-documented works (e.g. the Mona Lisa, The Starry Night),
if all of the live sources above return little or no ownership history, Gemini
is allowed to fill in widely-known historical facts from its own training data.
Every such entry is explicitly labeled "isGeneralKnowledge": true and
sourceAuthority: "General knowledge — not from live source" in the JSON, is
tagged general knowledge in the timeline UI, is never marked verified,
and never overrides what a live source actually says. A medium-severity risk
flag is added automatically whenever this fallback is used, so it's always
visible even without reading the raw timeline.
- Go to aistudio.google.com/apikey
- Sign in with a Google account, click "Create API key"
- Copy it into
.envasGEMINI_API_KEY
- Go to tavily.com and sign up (free dev tier, no billing info)
- Copy your API key from the dashboard into
.envasTAVILY_API_KEY - If left blank, the server skips Tavily entirely and falls back to the supplementary sources only (this also removes the deterministic high-severity "watchlist match" flag, since that depends on Tavily's hits)
- Go to pro.europeana.eu/page/get-api
- Fill out the free request form (instant, no billing info)
- Copy the key into
.envasEUROPEANA_API_KEY - If left blank, the server simply skips this source
The Met, Art Institute of Chicago, MoMA (bundled), Wikipedia, and Wikidata need no keys at all.
npm install
cp .env.example .env # then fill in your keys
npm startVisit http://localhost:3000.
Computed entirely in server.js (computeConfidenceScore), never by the AI:
- Start at 100%
- −30% per custody gap in the provenance timeline
- −25% if fewer than 3 of the 7 sources returned a verified hit
- −10% per high-severity risk flag
- −10% if the valuation is flagged anomalous
Score is clamped to 0–100%.
- Push this project to a GitHub repo (or use
railway upfrom this directory) - In Railway, create a new project from the repo
- Railway auto-detects
npm startfrompackage.json - Add the environment variables from
.env.examplein the Railway project's Variables tab (do not commit.env— it's already in.gitignore) - Railway sets
PORTautomatically;server.jsreadsprocess.env.PORT
.env and .env.save contain live API keys and are excluded via
.gitignore. If either file was ever committed or shared, rotate the keys
before relying on this project further.