A comprehensive client for the Open Library API, providing a simple interface to access Open Library’s vast collection of book metadata. Works in both Node.js and browser environments with full TypeScript support.
Installation
npm install @colibri-hq/open-library-client
# or
pnpm add @colibri-hq/open-library-clientQuick Start
Create a client instance with your contact information (required to avoid rate limiting):
import { Client } from "@colibri-hq/open-library-client";
const contactInfo = "myemail@example.com";
const client = new Client(contactInfo);
// Search for books
for await (const book of client.searchBook("The Great Gatsby")) {
console.log(book.title);
console.log(book.author_name);
console.log(book.first_publish_year);
}Supported APIs
The client supports all major Open Library public endpoints:
- Book Search API - Search for books by title, author, ISBN, subject, etc.
- Work & Edition API - Load detailed work and edition data
- Authors API - Get author information and works
- Author Search API - Search for authors by name
- Subjects API - Browse books by subject
- Covers API - Access book cover images
Searching for Books
Simple Search
Search with a plain string query:
const searchResults = client.searchBook("The Great Gatsby");
// Iterate over all results (automatically paginated)
for await (const book of searchResults) {
console.log(`${book.title} by ${book.author_name?.join(", ")}`);
console.log(`Published: ${book.first_publish_year}`);
console.log(`ISBN: ${book.isbn?.[0]}`);
}Advanced Query Builder
Use the query builder for precise searches:
const searchResults = client.searchBook({
title: "The Great Gatsby",
author: "F. Scott Fitzgerald",
publish_year: { from: 1920, to: 1930 },
subject: ["American fiction", "Classics"],
language: "eng",
});
// Collect all results into an array
const books = await Array.fromAsync(searchResults);
console.log(`Found ${books.length} books`);Query Builder Examples
| Expression | Query | SOLR Query |
|---|---|---|
| Single keyword | { title: "flammable" } | title:flammable |
| Exact match | { title_suggest: "vitamin a" } | title:"vitamin a" |
| Multiple keywords | { subject: ["tennis", "rules"] } | subject:(tennis rules) |
| Numeric fields | { publish_year: 2015 } | publish_year:2015 |
| Range query | { publish_year: { from: 2010, to: 2020 } } | publish_year:[2010 TO 2020] |
| Open-ended range | { publish_year: { from: 2010 } } | publish_year:[2010 TO *] |
| OR construct | { subject: ["dogs", ["Juvenile fiction", "Juvenile literature"]] } | subject:dogs subject:("Juvenile fiction" OR "Juvenile literature") |
| Negative query | { "!author_key": "OL3415A" } | NOT author_key:OL3415A |
Search Options
Customize your search with options:
const searchResults = client.searchBook("The Great Gatsby", {
limit: 10, // Results per page
offset: 500, // Skip first 500 results
sortBy: "new", // Sort by newest first
fields: ["title", "author_name", "publish_year"], // Specific fields only
language: "en", // Re-rank by language preference
});Available Options:
| Parameter | Description |
|---|---|
fields | Array of fields to return. Use '*' for all fields (expensive). Add 'availability' for archive.org availability data. |
sortBy | Sort facet: 'new', 'old', 'random', 'key', 'rating', 'want_to_read', 'currently_reading', 'already_read' |
language | ISO 639-1 language code. Influences ranking but doesn’t exclude results. |
offset | Number of results to skip (for pagination) |
limit | Maximum results per page |
Working with Works and Editions
Load a Work
Get detailed information about a work:
const work = await client.loadWork("OL45804W");
if (work) {
console.log("Title:", work.title);
console.log(
"Authors:",
work.authors?.map((a) => a.author.key),
);
console.log("Description:", work.description);
console.log("Subjects:", work.subjects);
console.log("First published:", work.first_publish_date);
}Load an Edition
Get a specific edition by Open Library key:
const edition = await client.loadEdition("OL7353617M");
console.log("Title:", edition.title);
console.log("ISBN:", edition.isbn_13);
console.log("Publisher:", edition.publishers);
console.log("Pages:", edition.number_of_pages);
console.log("Format:", edition.physical_format);Load an Edition by ISBN
Look up an edition using its ISBN:
const edition = await client.loadEditionByIsbn("9780141182636");
if (edition) {
console.log("Found:", edition.title);
console.log("Work:", edition.works?.[0]?.key); // Link to work
} else {
console.log("ISBN not found");
}Load Work Editions
Get all editions of a work:
const editions = client.loadEditionsByWorkId("OL45804W");
for await (const edition of editions) {
console.log(`${edition.title} (${edition.publish_date})`);
console.log(` Publisher: ${edition.publishers?.join(", ")}`);
console.log(` ISBN: ${edition.isbn_13?.[0] || edition.isbn_10?.[0]}`);
}Working with Authors
Load an Author
Get author information:
const author = await client.loadAuthor("OL23919A");
if (author) {
console.log("Name:", author.name);
console.log("Birth:", author.birth_date);
console.log("Death:", author.death_date);
console.log("Bio:", author.bio);
console.log("Photo:", author.photos?.[0]);
}Search for Authors
Find authors by name:
// Simple search
for await (const author of client.searchAuthor("F. Scott Fitzgerald")) {
console.log(`${author.name} (${author.birth_date} - ${author.death_date})`);
console.log(`Works: ${author.work_count}`);
}
// Advanced search
const searchResults = client.searchAuthor({
name: "F. Scott Fitzgerald",
birth_date: { from: "1896-09-24", to: "1940-12-21" },
});Load Author’s Works
Get all works by an author:
const works = client.loadWorksByAuthorId("OL23919A");
for await (const work of works) {
console.log(`${work.title} (${work.first_publish_year})`);
}Additional Features
Load Work Ratings
Get community ratings for a work:
const ratings = await client.loadRatingsByWorkId("OL45804W");
if (ratings) {
console.log("Average:", ratings.average);
console.log("Count:", ratings.count);
console.log("Distribution:", ratings.distribution);
}Load Bookshelve Stats
Get reading statistics for a work:
const stats = await client.loadBookshelveStatsByWorkId("OL45804W");
if (stats) {
console.log("Want to read:", stats.want_to_read);
console.log("Currently reading:", stats.currently_reading);
console.log("Already read:", stats.already_read);
}Iteration and Pagination
Asynchronous Iteration
All search methods return asynchronous iterators for efficient streaming:
// Process results as they arrive
for await (const book of client.searchBook("science fiction")) {
console.log(book.title);
// Only loads the next page when needed
}
// Take only the first 10 results
let count = 0;
for await (const book of client.searchBook("science fiction")) {
console.log(book.title);
if (++count >= 10) break;
}Collect All Results
Convert the iterator to an array (loads all pages):
const allBooks = await Array.fromAsync(client.searchBook("The Great Gatsby"));
console.log(`Found ${allBooks.length} results`);Manual Pagination
Control pagination manually if needed:
// First page
const page1 = await Array.fromAsync(client.searchBook("javascript", { limit: 20, offset: 0 }));
// Second page
const page2 = await Array.fromAsync(client.searchBook("javascript", { limit: 20, offset: 20 }));Error Handling
Missing Items
Methods that load single items return null if not found:
const book = await client.loadEdition("OL12345678M");
if (book) {
console.log(book.title);
} else {
console.log("Book not found");
}Request Failures
The client throws errors on request failures:
try {
const book = await client.loadEdition("OL12345678M");
} catch (error) {
if (error.cause instanceof Response) {
const { status, statusText } = error.cause;
const body = await error.cause.text();
console.error("Failed to load book:", status, statusText, body);
} else {
console.error("An unexpected error occurred:", error);
}
}Cover Images
Cover URLs
The Open Library Covers API provides book cover images:
// Get cover URL from a book
const book = await client.loadEdition("OL7353617M");
if (book.covers?.[0]) {
const coverId = book.covers[0];
const coverUrl = `https://covers.openlibrary.org/b/id/${coverId}-L.jpg`;
console.log("Cover:", coverUrl);
}
// Cover sizes: S (small), M (medium), L (large)
const smallCover = `https://covers.openlibrary.org/b/id/${coverId}-S.jpg`;
const mediumCover = `https://covers.openlibrary.org/b/id/${coverId}-M.jpg`;
const largeCover = `https://covers.openlibrary.org/b/id/${coverId}-L.jpg`;Cover by ISBN
Get cover directly by ISBN:
const isbn = "9780141182636";
const coverUrl = `https://covers.openlibrary.org/b/isbn/${isbn}-L.jpg`;Rate Limiting and Best Practices
Rate Limits
Open Library has the following rate limits:
- Search API: 100 requests per 5 minutes
- Other APIs: Throttled to prevent abuse
Best Practices
Provide Contact Info: Always include your email in the client constructor
typescriptconst client = new Client("your-email@example.com");Cache Results: Store frequently accessed data locally
typescriptconst cache = new Map(); const cacheKey = `work:${workId}`; if (cache.has(cacheKey)) { return cache.get(cacheKey); } const work = await client.loadWork(workId); cache.set(cacheKey, work);Use Specific Fields: Request only needed fields to reduce bandwidth
typescriptclient.searchBook("query", { fields: ["title", "author_name", "isbn"], // Only what you need });Handle Errors Gracefully: Wrap API calls in try-catch blocks
Respect Pagination: Don’t load all results if you only need a few
typescript// Good: Take first 10 let count = 0; for await (const book of client.searchBook("query")) { if (++count > 10) break; // process book } // Bad: Load everything const allBooks = await Array.fromAsync(client.searchBook("query"));
TypeScript Support
Full TypeScript definitions included:
import type {
SearchBookResult,
Work,
Edition,
Author,
SearchAuthorResult,
BookQuery,
AuthorQuery,
SearchOptions,
} from "@colibri-hq/open-library-client";
// Type-safe queries
const query: BookQuery = {
title: "The Great Gatsby",
author: "Fitzgerald",
publish_year: { from: 1920 },
};
// Type-safe results
const book: SearchBookResult | null = await client.searchBook(query).next();Complete Example
A comprehensive example showing common workflows:
import { Client } from "@colibri-hq/open-library-client";
const client = new Client("your-email@example.com");
async function findBookAndAuthor(isbn: string) {
// 1. Find edition by ISBN
const edition = await client.loadEditionByIsbn(isbn);
if (!edition) {
console.log("Book not found");
return;
}
console.log("Book:", edition.title);
console.log("Publisher:", edition.publishers?.join(", "));
console.log("Published:", edition.publish_date);
// 2. Load the work
const workKey = edition.works?.[0]?.key;
if (workKey) {
const workId = workKey.split("/").pop();
const work = await client.loadWork(workId!);
if (work) {
console.log("\nWork details:");
console.log("Description:", work.description);
console.log("Subjects:", work.subjects?.slice(0, 5).join(", "));
// 3. Load ratings
const ratings = await client.loadRatingsByWorkId(workId!);
if (ratings) {
console.log("Rating:", ratings.average, "/", 5);
console.log("Ratings:", ratings.count);
}
// 4. Load author
const authorKey = work.authors?.[0]?.author.key;
if (authorKey) {
const authorId = authorKey.split("/").pop();
const author = await client.loadAuthor(authorId!);
if (author) {
console.log("\nAuthor:", author.name);
console.log("Born:", author.birth_date);
console.log("Bio:", author.bio);
// 5. Find other works by author
console.log("\nOther works:");
let count = 0;
for await (const otherWork of client.loadWorksByAuthorId(authorId!)) {
console.log(`- ${otherWork.title} (${otherWork.first_publish_year})`);
if (++count >= 5) break;
}
}
}
}
}
// 6. Get cover image
if (edition.covers?.[0]) {
const coverUrl = `https://covers.openlibrary.org/b/id/${edition.covers[0]}-L.jpg`;
console.log("\nCover:", coverUrl);
}
}
// Run the example
findBookAndAuthor("9780141182636");Related Documentation
- Metadata SDK - Multi-provider metadata aggregation
- Ingestion SDK - Complete ebook import pipeline
- Open Library API Docs - Official API documentation