Skip to content

Instantly share code, notes, and snippets.

@cdrini
Created May 15, 2026 14:56
Show Gist options
  • Select an option

  • Save cdrini/f4a87ab17fed19885599a2a4f38ce224 to your computer and use it in GitHub Desktop.

Select an option

Save cdrini/f4a87ab17fed19885599a2a4f38ce224 to your computer and use it in GitHub Desktop.
Edit History and Diffs API Guide

Edit History and Diffs API Guide

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.py
  • openlibrary/plugins/upstream/code.py (?m=history override)
  • vendor/infogami/infogami/core/code.py (?m=diff, core ?v= behavior)
  • vendor/infogami/infogami/infobase/_dbstore/read.py (recentchanges query engine)

Quick Endpoint Map

1) Recent changes feed (global or filtered)

  • GET /recentchanges.json
  • GET /recentchanges.yml
  • GET /recentchanges/{kind}.json
  • GET /recentchanges/{yyyy|yyyy/mm|yyyy/mm/dd}.json
  • GET /recentchanges/{yyyy|yyyy/mm|yyyy/mm/dd}/{kind}.json

Supported query params on encoded (.json / .yml) endpoints:

  • author: author key like /people/alice
  • bot: true or false
  • limit: clamped to 0..1000
  • offset: clamped to 0..10000
  • text=true: forces Content-Type: text/plain

Notes:

  • Date range filtering from path is inclusive start, exclusive end internally (begin_date <= created < end_date).
  • kind maps to transaction action (examples: edit, add-book, merge-authors, undo, etc.).
  • Ordering is newest first.
  • Known issue: offset is currently buggy on public recentchanges routes; use a large enough limit to capture the full window you need.

2) One record's version history

  • 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 /versions with sort=-created
  • Allow filters: author, offset, limit
  • Force key=<path>
  • Strip ip from each returned row before responding

3) One record at a specific revision

  • GET /works/OL39178846W.json?v=1

v is revision number. If omitted, latest revision is returned.

4) Renderable object diff mode

  • GET /works/OL39178846W.json?m=diff&b=12&a=11

Semantics:

  • b: newer revision (defaults to latest)
  • a: older revision (defaults to b - 1)
  • Non-integer a/b triggers redirect to normalized query.

For machine analysis, many workflows still prefer fetching ?v= snapshots and diffing JSON locally.

5) One specific changeset by id

  • 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 Response Shape

/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:

  • author is null for anonymous edits; then ip may be present.
  • changes contains all documents touched by the transaction (not just one key).
  • data is arbitrary indexed metadata and can be queried in backend APIs (see Advanced Query Notes below).

Case Studies

Case Study A: Walk over a specific user's edits

Goal: enumerate edits by /people/alice, newest to oldest.

  1. Request:

    • GET /recentchanges.json?author=/people/alice&limit=1000
  2. Because offset is currently buggy on this endpoint, request a sufficiently large limit for your analysis window.

  3. For each changeset, inspect:

    • id, timestamp, kind, comment
    • changes[*].key and changes[*].revision
  4. Build per-record timelines by grouping on changes[*].key.

Why this works: recentchanges supports direct author filtering at query time.

Case Study B: Analyze one work's full edit history

Goal: understand revision trail for one work.

  1. Request:

    • GET /works/OL39178846W.json?m=history&limit=200&offset=0
  2. Each row gives a revision event for that key (newest first).

  3. Extract revision numbers (for example, 12, 11, 10, ...).

  4. Pull specific snapshots as needed:

    • GET /works/OL39178846W.json?v=12
    • GET /works/OL39178846W.json?v=11
  5. Diff snapshot JSONs locally.

Why this works: m=history is key-scoped and directly backed by /versions.

Case Study C: Reconstruct diffs for user edits only

Goal: "show what this user changed", with before/after JSON.

  1. Fetch user feed:

    • GET /recentchanges.json?author=/people/alice&limit=1000
  2. For each changeset c, iterate c.changes entries.

  3. For each changed doc:

    • after = GET {key}.json?v={revision}
    • before = GET {key}.json?v={revision-1} (or treat revision==1 as creation)
  4. Compute JSON Patch / structural diff in your analysis pipeline.

  5. Attach context from changeset fields (kind, comment, timestamp, id).

This gives robust diffs even when one transaction touched multiple documents.

Case Study D: Investigate a known change id

Goal: inspect one suspicious change quickly.

  1. Open canonical route if you know id/date/kind:

    • GET /recentchanges/2026/05/14/edit/12345678.json
  2. If you only know the id, use redirect helper in browser flow:

    • GET /recentchanges/goto/12345678
  3. Use changes[*] revisions to fetch exact before/after snapshots with ?v=.

Advanced Query Notes

The underlying recentchanges engine supports additional filters (key, ip, kind, bot, begin_date, end_date, data).

  • Public /recentchanges*.json currently exposes author, bot, limit, offset, and (via path) date/kind filters.
  • Internal code paths can pass richer query objects to web.ctx.site.recentchanges(query).
  • data filtering is exact-match per indexed key/value in transaction metadata.

Practical Caveats

  • limit on public recentchanges is hard-capped to protect DB load.
  • offset is currently buggy on public recentchanges routes, so pagination by offset is unreliable.
  • ?m=history in Open Library intentionally removes ip from output.
  • ?m=diff is useful for human-facing compare pages; machine workflows are usually easier with ?v= snapshots.

Minimal Workflow Recipe (Recommended)

If your goal is "user edit analytics + precise diffs":

  1. Pull user-scoped changes from /recentchanges.json?author=....
  2. Expand each transaction's changes[] list.
  3. Fetch ?v=n and ?v=n-1 snapshots per changed key.
  4. Diff locally and store (changeset_id, key, revision, diff, metadata).

This keeps API calls predictable and avoids relying on undocumented template-level behavior.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment