This guide documents the current Open Library + Infogami APIs you can use to:
- Walk over a user's edits
- Fetch a record at a specific revision
- Pull revision history for one record
- Build before/after diffs for specific edits
It is based on the current server implementation in:
openlibrary/plugins/upstream/recentchanges.pyopenlibrary/plugins/upstream/code.py(?m=historyoverride)vendor/infogami/infogami/core/code.py(?m=diff, core?v=behavior)vendor/infogami/infogami/infobase/_dbstore/read.py(recentchanges query engine)
GET /recentchanges.jsonGET /recentchanges.ymlGET /recentchanges/{kind}.jsonGET /recentchanges/{yyyy|yyyy/mm|yyyy/mm/dd}.jsonGET /recentchanges/{yyyy|yyyy/mm|yyyy/mm/dd}/{kind}.json
Supported query params on encoded (.json / .yml) endpoints:
author: author key like/people/alicebot:trueorfalselimit: clamped to0..1000offset: clamped to0..10000text=true: forcesContent-Type: text/plain
Notes:
- Date range filtering from path is inclusive start, exclusive end internally (
begin_date <= created < end_date). kindmaps to transaction action (examples:edit,add-book,merge-authors,undo, etc.).- Ordering is newest first.
- Known issue:
offsetis currently buggy on publicrecentchangesroutes; use a large enoughlimitto capture the full window you need.
GET /works/OL39178846W.json?m=history- Also works for other keys, e.g.
/authors/OL...A.json?m=history,/books/OL...M.json?m=history
Open Library overrides this mode to:
- Query Infobase
/versionswithsort=-created - Allow filters:
author,offset,limit - Force
key=<path> - Strip
ipfrom each returned row before responding
GET /works/OL39178846W.json?v=1
v is revision number. If omitted, latest revision is returned.
GET /works/OL39178846W.json?m=diff&b=12&a=11
Semantics:
b: newer revision (defaults to latest)a: older revision (defaults tob - 1)- Non-integer
a/btriggers redirect to normalized query.
For machine analysis, many workflows still prefer fetching ?v= snapshots and diffing JSON locally.
GET /recentchanges/2026/05/14/edit/12345678.json(public route)
Useful when you already have a change id and want full change metadata (changes, data, kind, comment, etc.).
/recentchanges*.json returns an array of changesets. Typical element shape:
{
"id": "12345678",
"kind": "edit",
"timestamp": "2026-05-14T20:15:22.123456",
"comment": "fix subtitle",
"author": { "key": "/people/alice" },
"ip": null,
"changes": [
{ "key": "/works/OL39178846W", "revision": 12 }
],
"data": {
"machine_comment": "..."
}
}Important details:
authorisnullfor anonymous edits; thenipmay be present.changescontains all documents touched by the transaction (not just one key).datais arbitrary indexed metadata and can be queried in backend APIs (see Advanced Query Notes below).
Goal: enumerate edits by /people/alice, newest to oldest.
-
Request:
GET /recentchanges.json?author=/people/alice&limit=1000
-
Because
offsetis currently buggy on this endpoint, request a sufficiently largelimitfor your analysis window. -
For each changeset, inspect:
id,timestamp,kind,commentchanges[*].keyandchanges[*].revision
-
Build per-record timelines by grouping on
changes[*].key.
Why this works: recentchanges supports direct author filtering at query time.
Goal: understand revision trail for one work.
-
Request:
GET /works/OL39178846W.json?m=history&limit=200&offset=0
-
Each row gives a revision event for that key (newest first).
-
Extract revision numbers (for example,
12,11,10, ...). -
Pull specific snapshots as needed:
GET /works/OL39178846W.json?v=12GET /works/OL39178846W.json?v=11
-
Diff snapshot JSONs locally.
Why this works: m=history is key-scoped and directly backed by /versions.
Goal: "show what this user changed", with before/after JSON.
-
Fetch user feed:
GET /recentchanges.json?author=/people/alice&limit=1000
-
For each changeset
c, iteratec.changesentries. -
For each changed doc:
- after =
GET {key}.json?v={revision} - before =
GET {key}.json?v={revision-1}(or treatrevision==1as creation)
- after =
-
Compute JSON Patch / structural diff in your analysis pipeline.
-
Attach context from changeset fields (
kind,comment,timestamp,id).
This gives robust diffs even when one transaction touched multiple documents.
Goal: inspect one suspicious change quickly.
-
Open canonical route if you know id/date/kind:
GET /recentchanges/2026/05/14/edit/12345678.json
-
If you only know the id, use redirect helper in browser flow:
GET /recentchanges/goto/12345678
-
Use
changes[*]revisions to fetch exact before/after snapshots with?v=.
The underlying recentchanges engine supports additional filters (key, ip, kind, bot, begin_date, end_date, data).
- Public
/recentchanges*.jsoncurrently exposesauthor,bot,limit,offset, and (via path) date/kind filters. - Internal code paths can pass richer query objects to
web.ctx.site.recentchanges(query). datafiltering is exact-match per indexed key/value in transaction metadata.
limiton public recentchanges is hard-capped to protect DB load.offsetis currently buggy on public recentchanges routes, so pagination by offset is unreliable.?m=historyin Open Library intentionally removesipfrom output.?m=diffis useful for human-facing compare pages; machine workflows are usually easier with?v=snapshots.
If your goal is "user edit analytics + precise diffs":
- Pull user-scoped changes from
/recentchanges.json?author=.... - Expand each transaction's
changes[]list. - Fetch
?v=nand?v=n-1snapshots per changed key. - Diff locally and store
(changeset_id, key, revision, diff, metadata).
This keeps API calls predictable and avoids relying on undocumented template-level behavior.