open-library-api-rs
A full-featured async Rust client library and CLI for the Open Library API.
Covers every documented read endpoint across all twelve API domains: works, editions, books (bibkey), authors, full-text search, subjects, covers, user lists, reading logs, the partner/volumes API, recent changes, and the generic query and history endpoints.
Contents
- Features
- Library
- CLI —
olib - Security
- Rate Limits
- Building from Source
- License
Features
- Complete API coverage — every documented public read endpoint
- Strongly typed — dedicated structs for all response shapes;
serdewith#[serde(default)]and nodeny_unknown_fieldsso new API fields never break deserialization - Async-first — built on
tokioandreqwest; blocking wrappers available behind a feature flag - Built-in rate limiting — token-bucket limiter via
governor; 1 req/s default, 3 req/s with an identifyingUser-Agent - Input validation — OLIDs, ISBN-10/13, subject slugs, usernames, bibkeys, dates all validated before any HTTP call is made
- Security hardened —
rustlsTLS only by default, 10 MB response body cap,#![forbid(unsafe_code)], URL-safe parameter construction (no raw string concatenation) - CLI included —
olibbinary prints human-readable output to stdout, errors to stderr;--jsonflag for machine-readable output
Library
Installation
Add to Cargo.toml:
[]
= "0.1"
= { = "1", = ["full"] }
The default feature set uses rustls-tls. If you need the system TLS stack:
= { = "0.1", = false, = ["native-tls"] }
Quick Start
use ;
use SearchParams;
async
Client Configuration
use Duration;
use OpenLibraryClient;
let client = builder
// Provide a contact email to unlock the 3 req/s identified tier
.contact_email?
// Always pair contact_email with rate_limit(3)
.rate_limit
// TCP connect timeout (default: 10 s)
.connect_timeout
// Total request timeout (default: 30 s)
.timeout
// Override base URL (useful for tests or a local mirror)
.base_url
// Override covers base URL
.covers_url
.build?;
OpenLibraryClient is Clone + Send + Sync; clone it cheaply to share across tasks or threads.
API Reference
All methods are async and return Result<T> where T is a strongly-typed struct. Every method validates its inputs before making any HTTP request.
Works
A work is the canonical creative entity (the book as an idea). Editions are concrete printings of a work.
// Get a work by its Work OLID
let work: Work = client.get_work.await?;
// work.key, work.title, work.description, work.authors, work.subjects,
// work.covers, work.first_publish_date, ...
// List editions of a work (paginated)
let editions: WorkEditions = client.get_work_editions.await?;
// editions.entries — Vec<WorkEditionEntry>
// editions.size — total edition count
// Community star ratings
let ratings: WorkRatings = client.get_work_ratings.await?;
// ratings.summary.average, ratings.summary.count
// ratings.counts.one / .two / .three / .four / .five
// Bookshelf counts (want-to-read, currently reading, already read)
let shelves: WorkBookshelves = client.get_work_bookshelves.await?;
// shelves.counts.want_to_read, .currently_reading, .already_read
OLID format: OL<digits>W — e.g. OL45804W.
Editions
An edition is a specific physical or digital printing of a work.
// Get an edition by its Edition OLID
let edition: Edition = client.get_edition.await?;
// edition.key, edition.title, edition.publishers, edition.publish_date,
// edition.isbn_13, edition.isbn_10, edition.languages, edition.number_of_pages,
// edition.physical_format, edition.covers, ...
// Get an edition by ISBN (10 or 13 digits; hyphens and spaces are stripped)
let edition: Edition = client.get_edition_by_isbn.await?;
let edition: Edition = client.get_edition_by_isbn.await?;
OLID format: OL<digits>M — e.g. OL7353617M.
Books (Bibkey Lookup)
The /api/books endpoint returns structured data for one or more books identified by bibliographic keys.
use BooksJsCmd;
use HashMap;
let bibkeys = vec!;
// BooksJsCmd::Data (default) — full data bundle per book
// BooksJsCmd::Details — structured details including subjects, excerpts
// BooksJsCmd::ViewApi — preview / read links
let books: =
client.get_books.await?;
for in &books
Valid bibkey prefixes: ISBN:, OCLC:, LCCN:, OLID:, ID:
Authors
// Get an author by their Author OLID
let author: Author = client.get_author.await?;
// author.key, author.name, author.birth_date, author.death_date,
// author.bio, author.photos, author.wikipedia, author.alternate_names, ...
// Get the works attributed to an author (paginated)
let works: AuthorWorks = client.get_author_works.await?;
// works.entries — Vec<AuthorWorkEntry> with key, title, covers, first_publish_date
OLID format: OL<digits>A — e.g. OL23919A.
Search
Five distinct search endpoints, all returning SearchResponse<T> where num_found is the total match count and docs is the current page.
use ;
// ── Book / work search ────────────────────────────────────────────────────────
let results: = client.search.await?;
// SearchParams also accepts: title, isbn, subject, place, person, publisher, page, fields, lang
// ── Author search ─────────────────────────────────────────────────────────────
let authors: = client.search_authors.await?;
// ── Subject search ────────────────────────────────────────────────────────────
let subjects: = client.search_subjects.await?;
// ── List search ───────────────────────────────────────────────────────────────
let lists: = client.search_lists.await?;
// ── Full-text inside-book search ──────────────────────────────────────────────
let inside: = client.search_inside.await?;
Subjects
Subjects group works by topic, place, person, or time period. The slug must be lowercase ASCII with underscores.
use SubjectParams;
let subject: Subject = client.get_subject.await?;
// subject.name, subject.work_count, subject.works
// subject.authors, subject.publishers (when details=true)
// subject.related_subjects (when details=true)
Covers and Author Photos
Cover images live on a separate CDN (covers.openlibrary.org). The cover_url and author_photo_url methods return a url::Url without making any HTTP request.
use ;
// Construct a cover URL (no network call)
let url: Url = client.cover_url;
let url: Url = client.cover_url;
let url: Url = client.cover_url;
let url: Url = client.cover_url;
let url: Url = client.cover_url;
// Construct an author photo URL (validates the OLID, no network call)
let url: Url = client.author_photo_url?;
// Fetch cover metadata (makes a network call)
let metas: = client.cover_meta.await?;
// meta.id, meta.url, meta.size, meta.width, meta.height
User Lists
Open Library users can create public reading lists. All list endpoints are read-only in v0.1.
// All public lists for a user
let lists: UserLists = client.get_user_lists.await?;
// lists.lists — Vec<ListSummary> with key, name, seed_count
// lists.size — total list count
// A single list
let list: List = client.get_list.await?;
// list.name, list.description, list.seed_count, list.edition_count, list.last_update
// Editions contained in a list (paginated)
let editions: ListEditions = client.get_list_editions.await?;
// Subjects covered by the works in a list
let subjects: ListSubjects = client.get_list_subjects.await?;
// subjects.subjects, subjects.places, subjects.people, subjects.times
// Raw seeds (works, editions, subjects) in the list
let seeds: ListSeeds = client.get_list_seeds.await?;
Reading Log
Users' public reading shelves — want to read, currently reading, and already read.
let log: ReadingLog = client.get_want_to_read.await?;
let log: ReadingLog = client.get_currently_reading.await?;
let log: ReadingLog = client.get_already_read.await?;
for entry in &log.reading_log_entries
Partner / Volumes API
The partner API resolves book identifiers to availability and borrowing information.
use VolumeIdType;
// Single volume lookup
let v: VolumesResponse = client.read_volume.await?;
let v: VolumesResponse = client.read_volume.await?;
let v: VolumesResponse = client.read_volume.await?;
let v: VolumesResponse = client.read_volume.await?;
// Batch lookup — each request is "type/value"
let requests = vec!;
let v: VolumesResponse = client.read_volumes_batch.await?;
// v.records — HashMap<String, VolumeRecord> keyed by "/books/OL…"
// v.items — Vec<VolumeItem> with status (Borrowable/Readable/Limited/Unavailable)
Recent Changes
The recent-changes feed lists edits made to Open Library records.
use ;
let params = ChangesParams ;
// All recent changes
let changes: = client.get_recent_changes.await?;
// Changes on a specific date (YYYY-MM-DD)
let changes: = client.get_changes_by_date.await?;
// Changes of a specific kind
let changes: = client.get_changes_by_kind.await?;
// Combined date + kind filter
let changes: =
client.get_changes_by_date_and_kind.await?;
// Available ChangeKind values:
// AddCover | AddBook | EditBook | MergeAuthors | Update | Revert | NewAccount | Register | Lists
Generic Query
The /query.json endpoint lets you filter any Open Library object type by field values.
use HashMap;
let mut fields = new;
fields.insert;
let results: QueryResponse = client.query.await?;
// results.result — Vec<serde_json::Value>
Resource History
Fetch the full edit history (revision log) for any Open Library resource.
let history: = client.get_resource_history.await?;
for entry in &history
Error Handling
All fallible operations return open_library_api_rs::Result<T>, which is an alias for std::result::Result<T, open_library_api_rs::Error>.
use Error;
match client.get_work.await
Input Validation
Inputs are validated before any network call is made, returning Error::InvalidInput immediately on failure.
| Input | Rule |
|---|---|
| Work OLID | OL<digits>W — e.g. OL45804W |
| Edition OLID | OL<digits>M — e.g. OL7353617M |
| Author OLID | OL<digits>A — e.g. OL23919A |
| ISBN-10 | 10 alphanumeric chars; valid Luhn-mod-11 check digit; trailing X allowed |
| ISBN-13 | 13 digits; prefix 978 or 979; valid EAN-13 check digit |
| Subject slug | Non-empty; [a-z0-9_]+; ≤ 200 chars |
| Search query | Non-empty; ≤ 1000 chars |
| Username | Non-empty; [a-zA-Z0-9_-]+; ≤ 64 chars |
| List ID | Non-empty; [a-zA-Z0-9_-]+ |
limit |
1–1000 |
| Bibkey | Non-empty; prefix must be ISBN:, OCLC:, LCCN:, OLID:, or ID: |
| Date | YYYY-MM-DD format; month 1–12, day 1–31 |
| Contact email | Must contain @, not at start or end |
ISBN hyphens and spaces are stripped automatically for get_edition_by_isbn.
Feature Flags
| Flag | Default | Description |
|---|---|---|
rustls-tls |
on | TLS via rustls (no system certs needed) |
native-tls |
off | TLS via the OS / OpenSSL |
blocking |
off | Synchronous wrappers for all async methods |
Only one TLS flag should be active at a time.
Blocking (Sync) API
Enable the blocking feature and use OpenLibraryClient::into_blocking():
= { = "0.1", = ["blocking"] }
= { = "1", = ["rt"] }
use OpenLibraryClient;
let client = builder.build?.into_blocking?;
// Every async method is available as a synchronous equivalent:
let work = client.get_work?;
let results = client.search?;
let edition = client.get_edition_by_isbn?;
BlockingClient owns a tokio::runtime::Runtime internally. It is not Clone or Send; create one per thread if you need concurrency.
CLI — olib
The olib binary gives command-line access to every library method. Results go to stdout; errors go to stderr; exit code is 0 on success, 1 on failure.
Use --json to get machine-readable JSON output that can be piped directly to jq.
Installing the CLI
# From crates.io
# From source
# Or build without installing
Global Options
These flags apply to every subcommand:
| Flag | Default | Description |
|---|---|---|
--json |
off | Print raw pretty-printed JSON instead of formatted text |
--rate-limit <N> |
1 |
Requests per second sent to the API |
--email <EMAIL> |
none | Contact email appended to User-Agent header; also set --rate-limit 3 |
# Identify your application to get the 3 req/s tier
# JSON output for scripting
|
work
olib work <COMMAND>
Commands:
get Fetch a work by OLID
editions Fetch the editions of a work (paginated)
ratings Fetch community star ratings
bookshelves Fetch bookshelf shelf counts
# Get a work
# Get raw JSON
|
# List editions (first 5)
# Paginate: second page of 20
# Star ratings
# Bookshelf counts (want-to-read / currently-reading / already-read)
edition
olib edition <COMMAND>
Commands:
get Fetch an edition by OLID
isbn Fetch an edition by ISBN-10 or ISBN-13
# By OLID
# By ISBN-10 (hyphens ignored)
# By ISBN-13
# JSON output
|
author
olib author <COMMAND>
Commands:
get Fetch an author by OLID
works Fetch the works attributed to an author (paginated)
# Get an author
# Get their works
# Page 2
# JSON
|
search
olib search <COMMAND>
Commands:
books Search books and works
authors Search authors
subjects Search subjects
lists Search user-created lists
inside Full-text search inside book content
# Book search — free-text
# With filters
# Sort options: relevance (default) | new | old
# Filter by title, isbn, subject, place, person
# Author search
# Subject search
# List search
# Inside search (scans book text)
# JSON output
|
subject
olib subject <SLUG> [OPTIONS]
Options:
--details Include related subjects, authors, publishers
--ebooks Only return works with e-book editions
--published-in Year range, e.g. 1950-1999
--limit Results per page (default: 20)
--offset Pagination offset (default: 0)
Subject slugs are lowercase with underscores (e.g. science_fiction, world_war_2).
# Basic subject page
# With related subjects, authors, publishers
# Only e-books published in the 1980s
# Paginate
# JSON
|
cover
olib cover <COMMAND>
Commands:
url Print a cover image URL (no network call)
meta Fetch cover image metadata (size, dimensions)
photo Print an author photo URL (no network call)
Cover key types: id | isbn | oclc | lccn | olid
Image sizes: small (s) | medium (m) | large (l)
# Cover URL by internal cover ID
# Cover URL by ISBN
# Cover URL by OLID
# Cover metadata (width, height, URL)
# Author photo URL (validates OLID)
# Pipe the URL straight to curl
list
olib list <COMMAND>
Commands:
user List a user's public lists
show Fetch a single list
editions Fetch editions in a list (paginated)
subjects Fetch subjects covered by a list
seeds Fetch raw seeds (items) in a list
# List all public lists for a user
# Paginate
# Show a specific list
# Editions contained in the list
# Subjects derived from the list
# Raw seeds (work keys, edition keys, subject URLs)
# JSON
|
reading
olib reading <COMMAND>
Commands:
want-to-read Books on the want-to-read shelf
currently-reading Books currently being read
already-read Books already finished
# JSON
|
changes
olib changes [OPTIONS]
Options:
--date <DATE> Filter to a specific date (YYYY-MM-DD)
--kind <KIND> Filter by change type
--limit <N> Results per page (default: 20)
--offset <N> Pagination offset (default: 0)
--bot <true|false> Include or exclude bot edits
Kind values: add-cover | add-book | edit-book | merge-authors | update | revert | new-account | register | lists
# All recent changes (last 20)
# Changes on a specific date
# Changes of a specific type
# Date + kind
# Exclude bot edits
# JSON
|
volume
olib volume <COMMAND>
Commands:
isbn Look up a volume by ISBN
lccn Look up a volume by Library of Congress Control Number
oclc Look up a volume by OCLC number
olid Look up a volume by Open Library edition OLID
batch Look up multiple volumes at once
# Single lookups
# Batch: each argument is "type/value"
# JSON (includes borrowability status)
|
books
Bibkey-based lookup using the /api/books endpoint. Supports multiple keys in one request.
olib books <BIBKEY>... [--jscmd data|details|viewapi]
| jscmd | Returns |
|---|---|
data (default) |
Full bibliographic data bundle |
details |
Structured details including subjects and excerpts |
viewapi |
Preview and read-online links |
# Single key
# Multiple keys
# With details jscmd
# Valid prefixes: ISBN: OCLC: LCCN: OLID: ID:
# JSON
|
query
Generic query against any Open Library object type via /query.json.
olib query --type <TYPE> [--field KEY=VALUE]... [--limit N] [--offset N]
# Find editions by ISBN
# Find works by title
# Multiple fields
# JSON
|
history
Fetch the full revision history of any Open Library resource.
olib history <KEY>
The key is an Open Library path, e.g. /works/OL45804W or /books/OL7353617M.
# JSON
|
Security
| Property | Detail |
|---|---|
| TLS | rustls with full certificate verification; system CA bundle not required; native-tls available as an opt-in feature |
| Timeouts | Connect: 10 s; total request: 30 s — both configurable via builder |
| Body cap | Responses truncated at 10 MB before deserialization; Error::ResponseTooLarge returned |
| Unsafe | #![forbid(unsafe_code)] — no unsafe Rust anywhere in the crate |
| URL construction | All path and query values routed through the url crate — no format!("{base}/{user_input}") |
| User-Agent | Statically set to open-library-api-rs/0.1.0; optional contact email appended when provided |
| Input validation | All user-supplied strings validated before making any network call |
Rate Limits
| Context | Limit |
|---|---|
| Anonymous (no User-Agent) | 1 req/s |
| Identified (name + email in User-Agent) | 3 req/s |
| Covers API via non-OLID/ID keys | 100 req per 5 min per IP |
The client ships with a built-in token-bucket rate limiter (governor). Set rate_limit(3) and contact_email(...) together to use the identified tier. The limiter is enforced locally; if the server still returns HTTP 429, the library returns Error::RateLimited.
Building from Source
# Build the library
# Build the CLI binary
# Run the full test suite (unit + integration + CLI + doctest)
# Run only integration tests
# Check for warnings
# Build with the blocking feature
# Build with native-tls instead of rustls
# Generate docs
License
MIT. See LICENSE.
Open Library data is provided by the Internet Archive and is available under the Open Library API Terms. Please attribute the Internet Archive / Open Library when using this data in your application, and provide a contact email via contact_email() / --email so they can reach you if needed.