Skip to content

QiyangStudio/OpenLibraryKit

Repository files navigation

OpenLibraryKit

OpenLibraryKit is a Swift 6 package for the Open Library APIs.

It provides a typed, async/await-friendly client for the most commonly used public endpoints, plus local rate limiting aligned with Open Library's published guidance.

Features

  • Swift 6 native API built on async/await
  • Typed models for common Open Library responses
  • Coverage for search, works, editions, authors, subjects, lists, reading logs, recent changes, covers, legacy read lookups, and generic JSON resources
  • Local rate limiting based on Open Library's current documentation
  • Automatic User-Agent injection for identified requests
  • Mock-friendly URLSession-based implementation

Requirements

  • Swift 6.1+
  • macOS 12+, iOS 15+, tvOS 15+, watchOS 8+

Installation

Add OpenLibraryKit to your Package.swift:

dependencies: [
    .package(url: "https://github.com/wangqiyangX/OpenLibraryKit.git", branch: "main")
]

Then add the product to your target:

.target(
    name: "YourApp",
    dependencies: [
        .product(name: "OpenLibraryKit", package: "OpenLibraryKit")
    ]
)

Quick Start

Open Library asks clients to identify themselves and recommends these request limits:

  • Anonymous requests: 1 request/second
  • Identified requests with User-Agent and contact information: 3 requests/second

OpenLibraryKit adapts to that automatically.

import OpenLibraryKit

let client = OpenLibraryClient(
    identity: OpenLibraryIdentity(
        applicationName: "MyLibraryApp",
        contact: "contact@example.com"
    )
)

With an identity configured:

  • User-Agent is automatically set to MyLibraryApp (contact@example.com)
  • the local rate limiter automatically uses 3 req/s

Without an identity, the client defaults to 1 req/s.

Rate Limiting

You can customize rate limiting when needed:

let client = OpenLibraryClient(
    identity: OpenLibraryIdentity(
        applicationName: "MyLibraryApp",
        contact: "contact@example.com"
    ),
    rateLimitPolicy: .requestsPerSecond(2)
)

Available policies:

  • .automatic
  • .disabled
  • .requestsPerSecond(Double)

Covered APIs

The package currently covers these Open Library API areas:

  • Book Search API
  • Work & Edition APIs
  • Authors API
  • Subjects API
  • Covers API
  • Lists API, read-only operations
  • Your Books API, public reading log access
  • Recent Changes API
  • Legacy Read API / api/books
  • Generic .json resource loading for arbitrary Open Library paths
  • Search Inside API support for compatible JSON or JSONP responses

Examples

Search for books

let results = try await client.search(
    query: "The Hobbit",
    page: 1,
    limit: 10,
    fields: ["key", "title", "author_name", "cover_i"]
)

for doc in results.docs {
    print(doc.title ?? "Unknown title")
}

Load a work, edition, or author

let work = try await client.work("OL27448W")
let edition = try await client.edition("OL7353617M")
let author = try await client.author("OL26320A")

Fetch books by subject

let subject = try await client.subject(
    "science_fiction",
    details: true,
    ebooksOnly: false,
    limit: 20
)

print(subject.works.map(\.title))

Read public user lists

let lists = try await client.lists(forUser: "mekBot", limit: 10)
let detail = try await client.list(forUser: "mekBot", id: "OL1L")
let subjects = try await client.listSubjects(forUser: "mekBot", id: "OL1L", limit: 20)

Read a public reading log

let readingLog = try await client.readingLog(
    forUser: "mekBot",
    shelf: .wantToRead,
    page: 1,
    limit: 20
)

Access recent changes

let changes = try await client.recentChanges(.day(year: 2026, month: 3, day: 20), limit: 20)

Build cover URLs

let coverURL = try client.covers.url(
    for: .isbn("9780140328721"),
    size: .large
)

Use the legacy api/books endpoint

let books = try await client.books(
    [.isbn("9780140328721"), .olid("OL7353617M")],
    command: .data
)

Load any Open Library JSON resource

let work: WorkDetail = try await client.resource(at: "/works/OL27448W")

Error Handling

The client throws OpenLibraryError:

  • .invalidURLComponents
  • .invalidRequest(underlying:)
  • .transport(_)
  • .invalidResponse
  • .httpStatus(code:body:)
  • .decoding(_)

Example:

do {
    let work = try await client.work("OL27448W")
    print(work.title)
} catch let OpenLibraryError.httpStatus(code, body) {
    print("HTTP error:", code, String(decoding: body, as: UTF8.self))
} catch {
    print("Unexpected error:", error)
}

Notes

  • Lists write operations are not implemented yet.
  • Search Inside support depends on a compatible Archive.org host and is intentionally lightweight.
  • The package is designed for real-time, low-volume API access, not bulk harvesting.

Testing

Run the test suite with:

swift test

License

OpenLibraryKit is released under the MIT License. See LICENSE.

About

Swift 6 client for the Open Library API with async/await, typed models, and docs-aligned rate limiting.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages