open-library-api-rs 0.1.0

Async Rust client for the Open Library API
Documentation

open-library-api-rs

Crates.io Docs.rs License: MIT Rust 1.85+

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

  • Complete API coverage — every documented public read endpoint
  • Strongly typed — dedicated structs for all response shapes; serde with #[serde(default)] and no deny_unknown_fields so new API fields never break deserialization
  • Async-first — built on tokio and reqwest; 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 identifying User-Agent
  • Input validation — OLIDs, ISBN-10/13, subject slugs, usernames, bibkeys, dates all validated before any HTTP call is made
  • Security hardenedrustls TLS only by default, 10 MB response body cap, #![forbid(unsafe_code)], URL-safe parameter construction (no raw string concatenation)
  • CLI includedolib binary prints human-readable output to stdout, errors to stderr; --json flag for machine-readable output

Library

Installation

Add to Cargo.toml:

[dependencies]
open-library-api-rs = "0.1"
tokio = { version = "1", features = ["full"] }

The default feature set uses rustls-tls. If you need the system TLS stack:

open-library-api-rs = { version = "0.1", default-features = false, features = ["native-tls"] }

Quick Start

use open_library_api_rs::{OpenLibraryClient, Result};
use open_library_api_rs::models::search::SearchParams;

#[tokio::main]
async fn main() -> Result<()> {
    // Build a client (1 req/s, rustls TLS, 10s connect / 30s request timeout)
    let client = OpenLibraryClient::builder().build()?;

    // Fetch a work
    let work = client.get_work("OL45804W").await?;
    println!("Title: {:?}", work.title);
    println!("Subjects: {:?}", work.subjects);

    // Search
    let results = client.search(SearchParams {
        q: Some("rust programming language".into()),
        limit: Some(5),
        ..Default::default()
    }).await?;
    println!("Found {} results", results.num_found);
    for doc in &results.docs {
        println!("  {}{:?}", doc.key, doc.title);
    }

    Ok(())
}

Client Configuration

use std::time::Duration;
use open_library_api_rs::OpenLibraryClient;

let client = OpenLibraryClient::builder()
    // Provide a contact email to unlock the 3 req/s identified tier
    .contact_email("me@example.com")?
    // Always pair contact_email with rate_limit(3)
    .rate_limit(3)
    // TCP connect timeout (default: 10 s)
    .connect_timeout(Duration::from_secs(10))
    // Total request timeout (default: 30 s)
    .timeout(Duration::from_secs(30))
    // Override base URL (useful for tests or a local mirror)
    .base_url("https://openlibrary.org")
    // Override covers base URL
    .covers_url("https://covers.openlibrary.org")
    .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("OL45804W").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("OL45804W", 20, 0).await?;
// editions.entries — Vec<WorkEditionEntry>
// editions.size    — total edition count

// Community star ratings
let ratings: WorkRatings = client.get_work_ratings("OL45804W").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("OL45804W").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("OL7353617M").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("978-0-14-032-8721-4").await?;
let edition: Edition = client.get_edition_by_isbn("0140328726").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 open_library_api_rs::models::common::BooksJsCmd;
use std::collections::HashMap;

let bibkeys = vec![
    "ISBN:0451450523".to_string(),
    "OCLC:45883427".to_string(),
    "LCCN:2004046975".to_string(),
];

// BooksJsCmd::Data (default) — full data bundle per book
// BooksJsCmd::Details        — structured details including subjects, excerpts
// BooksJsCmd::ViewApi        — preview / read links
let books: HashMap<String, BooksApiEntry> =
    client.get_books(&bibkeys, BooksJsCmd::Data).await?;

for (key, entry) in &books {
    println!("{key}: {:?}", entry.info_url);
}

Valid bibkey prefixes: ISBN:, OCLC:, LCCN:, OLID:, ID:

Authors

// Get an author by their Author OLID
let author: Author = client.get_author("OL23919A").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("OL23919A", 50, 0).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 open_library_api_rs::models::search::{
    SearchParams, AuthorSearchParams,
    BookDoc, AuthorDoc, SubjectDoc, ListDoc, InsideDoc,
};

// ── Book / work search ────────────────────────────────────────────────────────
let results: SearchResponse<BookDoc> = client.search(SearchParams {
    q:        Some("lord of the rings".into()),
    author:   Some("tolkien".into()),
    language: Some("eng".into()),
    limit:    Some(10),
    offset:   Some(0),
    sort:     Some("new".into()),   // "new" | "old" | "relevance"
    ..Default::default()
}).await?;
// SearchParams also accepts: title, isbn, subject, place, person, publisher, page, fields, lang

// ── Author search ─────────────────────────────────────────────────────────────
let authors: SearchResponse<AuthorDoc> = client.search_authors(AuthorSearchParams {
    q:      Some("tolkien".into()),
    limit:  Some(5),
    offset: Some(0),
}).await?;

// ── Subject search ────────────────────────────────────────────────────────────
let subjects: SearchResponse<SubjectDoc> = client.search_subjects("fantasy").await?;

// ── List search ───────────────────────────────────────────────────────────────
let lists: SearchResponse<ListDoc> = client.search_lists("tolkien", Some(10)).await?;

// ── Full-text inside-book search ──────────────────────────────────────────────
let inside: SearchResponse<InsideDoc> = client.search_inside("lembas bread", Some(5)).await?;

Subjects

Subjects group works by topic, place, person, or time period. The slug must be lowercase ASCII with underscores.

use open_library_api_rs::models::search::SubjectParams;

let subject: Subject = client.get_subject("science_fiction", SubjectParams {
    details:      Some(true),   // include related subjects, authors, publishers
    ebooks:       Some(false),  // filter to works with e-book editions
    published_in: Some("1950-1999".into()),  // year range
    limit:        Some(20),
    offset:       Some(0),
}).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 open_library_api_rs::models::common::{CoverKey, ImageSize};

// Construct a cover URL (no network call)
let url: url::Url = client.cover_url(CoverKey::Id,   "5428012",         ImageSize::Large);
let url: url::Url = client.cover_url(CoverKey::Isbn,  "9780451450524",  ImageSize::Medium);
let url: url::Url = client.cover_url(CoverKey::Olid,  "OL7353617M",     ImageSize::Small);
let url: url::Url = client.cover_url(CoverKey::Oclc,  "45883427",       ImageSize::Large);
let url: url::Url = client.cover_url(CoverKey::Lccn,  "2004046975",     ImageSize::Large);

// Construct an author photo URL (validates the OLID, no network call)
let url: url::Url = client.author_photo_url("OL23919A", ImageSize::Medium)?;

// Fetch cover metadata (makes a network call)
let metas: Vec<CoverMeta> = client.cover_meta(CoverKey::Id, "5428012").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("alice", 20, 0).await?;
// lists.lists — Vec<ListSummary> with key, name, seed_count
// lists.size  — total list count

// A single list
let list: List = client.get_list("alice", "OL123L").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("alice", "OL123L", 20, 0).await?;

// Subjects covered by the works in a list
let subjects: ListSubjects = client.get_list_subjects("alice", "OL123L").await?;
// subjects.subjects, subjects.places, subjects.people, subjects.times

// Raw seeds (works, editions, subjects) in the list
let seeds: ListSeeds = client.get_list_seeds("alice", "OL123L").await?;

Reading Log

Users' public reading shelves — want to read, currently reading, and already read.

let log: ReadingLog = client.get_want_to_read("alice").await?;
let log: ReadingLog = client.get_currently_reading("alice").await?;
let log: ReadingLog = client.get_already_read("alice").await?;

for entry in &log.reading_log_entries {
    if let Some(work) = &entry.work {
        println!("{}: {:?}", work.key, work.title);
    }
    println!("  logged: {:?}", entry.logged_date);
}

Partner / Volumes API

The partner API resolves book identifiers to availability and borrowing information.

use open_library_api_rs::models::common::VolumeIdType;

// Single volume lookup
let v: VolumesResponse = client.read_volume(VolumeIdType::Isbn,  "0451450523").await?;
let v: VolumesResponse = client.read_volume(VolumeIdType::Lccn,  "2004046975").await?;
let v: VolumesResponse = client.read_volume(VolumeIdType::Oclc,  "45883427").await?;
let v: VolumesResponse = client.read_volume(VolumeIdType::Olid,  "OL7408846M").await?;

// Batch lookup — each request is "type/value"
let requests = vec![
    "isbn/0451450523".to_string(),
    "oclc/45883427".to_string(),
];
let v: VolumesResponse = client.read_volumes_batch(&requests).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 open_library_api_rs::models::changes::{ChangesParams, ChangeKind};

let params = ChangesParams {
    limit:  Some(50),
    offset: Some(0),
    bot:    Some(false),  // exclude bot edits
};

// All recent changes
let changes: Vec<RecentChange> = client.get_recent_changes(params).await?;

// Changes on a specific date (YYYY-MM-DD)
let changes: Vec<RecentChange> = client.get_changes_by_date("2024-06-15", params).await?;

// Changes of a specific kind
let changes: Vec<RecentChange> = client.get_changes_by_kind(&ChangeKind::EditBook, params).await?;

// Combined date + kind filter
let changes: Vec<RecentChange> =
    client.get_changes_by_date_and_kind("2024-06-15", &ChangeKind::AddCover, params).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 std::collections::HashMap;

let mut fields = HashMap::new();
fields.insert("isbn".to_string(), "0451450523".to_string());

let results: QueryResponse = client.query(
    "/type/edition",
    &fields,
    20,   // limit
    0,    // offset
).await?;
// results.result — Vec<serde_json::Value>

Resource History

Fetch the full edit history (revision log) for any Open Library resource.

let history: Vec<HistoryEntry> = client.get_resource_history("/works/OL45804W").await?;

for entry in &history {
    println!("r{:?}  {:?}  {:?}",
        entry.revision, entry.timestamp, entry.comment);
}

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 open_library_api_rs::Error;

match client.get_work("OL45804W").await {
    Ok(work) => { /* ... */ }
    Err(Error::InvalidInput(msg)) => {
        // Input failed validation before any network call
        eprintln!("bad input: {msg}");
    }
    Err(Error::NotFound(url)) => {
        // HTTP 404
        eprintln!("not found: {url}");
    }
    Err(Error::RateLimited) => {
        // HTTP 429 — slow down
    }
    Err(Error::Status { code, url }) => {
        // Unexpected non-2xx response
        eprintln!("HTTP {code} from {url}");
    }
    Err(Error::Deserialize { source, body }) => {
        // Response arrived but could not be parsed
        eprintln!("parse error: {source}\nBody: {body}");
    }
    Err(Error::ResponseTooLarge) => {
        // Response body exceeded the 10 MB cap
    }
    Err(Error::Timeout) => {
        // Connect or read timeout
    }
    Err(Error::Http(e)) => {
        // Lower-level reqwest / network error
        eprintln!("network error: {e}");
    }
    Err(Error::Url(e)) => {
        // URL parsing error (internal)
        eprintln!("url error: {e}");
    }
}

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():

open-library-api-rs = { version = "0.1", features = ["blocking"] }
tokio = { version = "1", features = ["rt"] }
use open_library_api_rs::OpenLibraryClient;

let client = OpenLibraryClient::builder().build()?.into_blocking()?;

// Every async method is available as a synchronous equivalent:
let work = client.get_work("OL45804W")?;
let results = client.search(params)?;
let edition = client.get_edition_by_isbn("9780451450524")?;

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
cargo install open-library-api-rs --bin olib

# From source
cargo install --path . --bin olib

# Or build without installing
cargo build --release --bin olib
./target/release/olib --help

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
olib --email me@example.com --rate-limit 3 work get OL45804W

# JSON output for scripting
olib --json work get OL45804W | jq '.title'

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
olib work get OL45804W

# Get raw JSON
olib --json work get OL45804W | jq '{title, first_publish_date}'

# List editions (first 5)
olib work editions OL45804W --limit 5

# Paginate: second page of 20
olib work editions OL45804W --limit 20 --offset 20

# Star ratings
olib work ratings OL45804W

# Bookshelf counts (want-to-read / currently-reading / already-read)
olib work bookshelves OL45804W

edition

olib edition <COMMAND>

Commands:
  get   Fetch an edition by OLID
  isbn  Fetch an edition by ISBN-10 or ISBN-13
# By OLID
olib edition get OL7353617M

# By ISBN-10 (hyphens ignored)
olib edition isbn 0-14-032-872-6

# By ISBN-13
olib edition isbn 9780140328724

# JSON output
olib --json edition isbn 9780140328724 | jq '{title, publishers, publish_date}'

author

olib author <COMMAND>

Commands:
  get    Fetch an author by OLID
  works  Fetch the works attributed to an author (paginated)
# Get an author
olib author get OL23919A

# Get their works
olib author works OL23919A --limit 20

# Page 2
olib author works OL23919A --limit 20 --offset 20

# JSON
olib --json author get OL23919A | jq '{name, birth_date, wikipedia}'

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
olib search books --q "lord of the rings"

# With filters
olib search books --author tolkien --language eng --limit 5

# Sort options: relevance (default) | new | old
olib search books --q "dune" --sort new --limit 10

# Filter by title, isbn, subject, place, person
olib search books --title "Foundation" --author "Asimov"
olib search books --q "python" --subject "programming"

# Author search
olib search authors --q "ursula le guin" --limit 5

# Subject search
olib search subjects "artificial intelligence"

# List search
olib search lists "classic literature" --limit 5

# Inside search (scans book text)
olib search inside "call me ishmael" --limit 3

# JSON output
olib --json search books --q "tolkien" --limit 3 | jq '.docs[] | {key, title}'

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
olib subject love

# With related subjects, authors, publishers
olib subject science_fiction --details

# Only e-books published in the 1980s
olib subject cyberpunk --ebooks --published-in 1980-1989

# Paginate
olib subject mystery --limit 10 --offset 30

# JSON
olib --json subject fantasy --details | jq '.related_subjects[].name'

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
olib cover url id 5428012 large

# Cover URL by ISBN
olib cover url isbn 9780451450524 medium

# Cover URL by OLID
olib cover url olid OL7353617M large

# Cover metadata (width, height, URL)
olib cover meta id 5428012

# Author photo URL (validates OLID)
olib cover photo OL23919A large

# Pipe the URL straight to curl
curl -L "$(olib cover url id 5428012 large)" -o cover.jpg

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
olib list user alice

# Paginate
olib list user alice --limit 10 --offset 10

# Show a specific list
olib list show alice OL123L

# Editions contained in the list
olib list editions alice OL123L --limit 20

# Subjects derived from the list
olib list subjects alice OL123L

# Raw seeds (work keys, edition keys, subject URLs)
olib list seeds alice OL123L

# JSON
olib --json list show alice OL123L | jq '{name, seed_count}'

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
olib reading want-to-read alice

olib reading currently-reading alice

olib reading already-read alice

# JSON
olib --json reading already-read alice | jq '.reading_log_entries[] | .work.title'

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)
olib changes

# Changes on a specific date
olib changes --date 2024-06-15

# Changes of a specific type
olib changes --kind edit-book --limit 50

# Date + kind
olib changes --date 2024-06-15 --kind add-cover

# Exclude bot edits
olib changes --bot false --limit 100

# JSON
olib --json changes --kind edit-book --limit 5 | jq '.[].key'

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
olib volume isbn 0451450523
olib volume lccn 2004046975
olib volume oclc 45883427
olib volume olid OL7408846M

# Batch: each argument is "type/value"
olib volume batch isbn/0451450523 oclc/45883427

# JSON (includes borrowability status)
olib --json volume isbn 0451450523 | jq '.items[].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
olib books ISBN:0451450523

# Multiple keys
olib books ISBN:0451450523 OCLC:45883427 LCCN:2004046975

# With details jscmd
olib books ISBN:0451450523 --jscmd details

# Valid prefixes: ISBN: OCLC: LCCN: OLID: ID:
olib books OLID:OL7408846M ID:5428012

# JSON
olib --json books ISBN:0451450523 | jq '.'

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
olib query --type /type/edition --field isbn=0451450523

# Find works by title
olib query --type /type/work --field title="Dune"

# Multiple fields
olib query --type /type/edition --field publishers=Penguin --field publish_date=1988

# JSON
olib --json query --type /type/edition --field isbn=0451450523 | jq '.result'

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.

olib history /works/OL45804W

olib history /books/OL7353617M

olib history /authors/OL23919A

# JSON
olib --json history /works/OL45804W | jq '.[0] | {revision, timestamp, comment}'

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

git clone https://github.com/aVOIDSTARch/open-library-api-rs.git
cd open-library-api-rs

# Build the library
cargo build

# Build the CLI binary
cargo build --bin olib --release

# Run the full test suite (unit + integration + CLI + doctest)
cargo test

# Run only integration tests
cargo test --test integration

# Check for warnings
cargo clippy -- -D warnings

# Build with the blocking feature
cargo build --features blocking

# Build with native-tls instead of rustls
cargo build --no-default-features --features native-tls

# Generate docs
cargo doc --no-deps --open

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.