Design the domain before the endpoints
An API should expose real distinctions: works, editions, resources, holdings, offers, chapters, places, and change events. Begin with the smallest set the product needs. Avoid one enormous “book” object or a generic endpoint whose shape changes according to dozens of flags.
Recommended first endpoints
GET /v1/works
GET /v1/works/{work_id}
GET /v1/editions
GET /v1/editions/{edition_id}
GET /v1/changes?cursor={opaque_cursor}
GET /v1/feeds/{feed_slug}
GET /opds/{catalog_slug}
Collection endpoints support filters and cursor pagination. Detail endpoints return canonical entities. The changes endpoint supports synchronization. Feed and OPDS endpoints are derived views, not independent databases.
Response contract
{
"data": [{
"id": "ed_9f3a",
"type": "edition",
"work_id": "work_42",
"title": "River Maps",
"language": "en",
"publication_date": "2026-06-03",
"updated_at": "2026-10-12T18:40:00Z",
"rights": {"status": "metadata_only"}
}],
"links": {
"self": "/v1/editions?language=en",
"next": "/v1/editions?cursor=eyJzIjoi..."
},
"meta": {"request_id": "req_7b2c"}
}
Cursor pagination and changes
Use a deterministic sort such as update timestamp plus ID. The cursor should be opaque and returned through a next link. Document whether it expires. For change streams, return event ID, entity ID, operation, timestamp, and a link to the current representation. Deletions need tombstone events so clients do not retain records forever.
Date semantics
Name dates explicitly: work origin date, edition publication date, digital release date, catalog ingest date, source modification date, API update date, and feed event date. Do not convert partial dates into false precision. Use complete timestamps for API events and preserve date-only values when that is all the source supports.
Rights and access
Represent public metadata separately from resource access. A resource can be open, sample-only, purchase, borrow, authenticated, region-limited, embargoed, or unavailable. Store the rights basis and provenance for full text, OCR, and AI-derived content. Avoid ambiguous fields such as free: true.
Caching and conditional requests
Book metadata changes infrequently, so return ETags or last-modified metadata and honor conditional requests. Immutable files can use long cache lifetimes and versioned URLs. Availability and offers require shorter policies. When an upstream source fails, serve the last valid normalized record rather than an empty or partially built representation.
Error design
{
"error": {
"code": "invalid_cursor",
"message": "The pagination cursor is no longer valid.",
"request_id": "req_9c11",
"details": {"restart_url": "/v1/editions"}
}
}
Use stable machine-readable error codes, clear messages, and request identifiers. Validation errors should point to fields or parameters. Do not expose credentials, stack traces, or source-system internals.
Versioning and deprecation
Reserve breaking versions for actual contract changes. Additive fields should be safe for tolerant clients. Publish a deprecation date, replacement endpoint, and sunset date. Keep old IDs resolving through aliases after merges. Never recycle an identifier.
Generate RSS, OPDS, and web pages together
One normalized edition record should produce the same title, contributors, identifiers, image, access status, and canonical URL everywhere. RSS supplies chronology; OPDS supplies publication discovery; the API supplies filtering and synchronization; HTML supplies human-readable context.
Read the long-form Book RSS API Design guide for entity relationships, tests, and operational behavior.