# ISBN Decoder API (`s-r/isbn-decoder`) Actor

Resolve any book title and/or author into every published edition with canonical ISBN-13, matching ISBN-10, retailer source attribution, and a confidence tier. Harvested from Amazon, Goodreads, OpenLibrary, WorldCat, Bol, AbeBooks.

- **URL**: https://apify.com/s-r/isbn-decoder.md
- **Developed by:** [SR](https://apify.com/s-r) (community)
- **Categories:** Developer tools, Business
- **Stats:** 7 total users, 1 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## ISBN Decoder API, by ISBN, OLID, title, or author

A book ISBN lookup API that takes whatever bibliographic input you have, a 13-digit ISBN, an Open Library identifier (`OL29832788M`), a book title, or an author name, and returns the canonical edition(s) with publisher, year, page count, language, cover, and Goodreads rating. Stitched from three free, no-auth, no-captcha public sources (Open Library, Goodreads autocomplete, Google SERP retailer harvest) routed through one of four input-shaped paths.

Different inputs take different paths automatically:

- **ISBN input** → direct Open Library lookup (Path A, captcha-immune, 1-8s)
- **OLID input** → direct Open Library by OLID (Path B, single API hit)
- **Title or author** → Google SERP retailer harvest (Path C) → Open Library search rescue if captchas (Path D)
- **Every path** → Goodreads autocomplete runs in parallel for the canonical "what you probably meant" hint with cover + rating

One actor call returns up to 10 editions for popular books, grouped per ISBN-13, deduplicated across retailers, ranked by source count, enriched with Open Library bibliographic data.

### What you get

- **Multi-edition coverage**, paperback, hardcover, deluxe, audiobook, regional translations all surface as separate editions with their own ISBN-13
- **Both identifiers**, ISBN-13 (canonical, checksum-validated) and the matching ISBN-10 (auto-converted from 978-prefix ISBN-13; null for 979-prefix audiobooks)
- **Open Library bibliographic data**, publisher, publish\_date (raw), publish\_year (extracted), pages, openlibrary\_id, languages (ISO 639-3)
- **Cleaned title**, extracted from the strongest retailer snippet, with format suffixes and retailer-name tails stripped
- **Normalized author**, resolved via Open Library's author key chase or extracted from SERP retailer patterns
- **Source attribution**, bare hostnames of every retailer + Open Library + Goodreads that confirmed the ISBN
- **Confidence tier**, `high` when 2+ independent sources confirm; `medium` for single-source hits
- **Goodreads top hint**, separate per-run field with title/author/cover/rating/ratings\_count from Goodreads autocomplete
- **Path attribution**, `path: A|B|C|D` tells you which route resolved each edition

### Why use this book ISBN lookup

Most ISBN lookup APIs accept only one input shape. Either you have an ISBN and want the metadata, OR you have a title and want the ISBNs. This actor routes intelligently based on input shape, feed it whatever you have.

The official ISBN registry at `isbn-international.org` is paid. Bowker's Books in Print is enterprise pricing. Open Library's direct API requires you to write the dispatch logic yourself (ISBN vs OLID vs search) and doesn't include cross-retailer SERP harvesting. Library APIs (Library of Congress, OpenLibrary direct) need OAuth for catalogs and miss every retail edition. Naive retailer scraping breaks on the 30% of book pages that render ISBNs as images.

This actor stitches Open Library's free no-auth API, Goodreads' autocomplete endpoint, and Google SERP retailer-page harvest into a single call with auto-dispatch. The Path D fallback handles SERP captcha, when Google blocks the retailer harvest, OpenLibrary search rescues automatically and the caller sees editions either way.

### Input

| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
| `title` | string | one-of | `Sapiens` | Book title (wrapped in quotes for exact-match SERP) |
| `author` | string | one-of | `Yuval Noah Harari` | Author name (wrapped in quotes); combine with title for best precision |
| `q` | string | one-of |, | Free-form query. If valid ISBN-10/13, short-circuits to Path A (direct OL) |
| `olid` | string | one-of |, | Open Library identifier like `OL29832788M` or full URL like `https://openlibrary.org/books/OL29832788M/Beach_Read`. Short-circuits to Path B |
| `country` | string | no | `us` | Google geo (gl). Affects which Amazon TLDs dominate the SERP on Path C |
| `language` | string | no | `en` | Google UI language (hl) |
| `enrich` | boolean | no | `true` | When false, skip Goodreads autocomplete and OL per-edition enrichment. Restores legacy SERP-only behavior |
| `hits` | boolean | no | `false` | Include raw SERP results in the first dataset item for debugging |

At least one of `title`, `author`, `q`, or `olid` is required.

### Output

One dataset item per edition. Multi-edition books produce multiple items per run. Example for `title=Sapiens, author=Yuval Noah Harari` (Path C):

```json
{
  "query": {"title": "Sapiens", "author": "Yuval Noah Harari"},
  "country": "us",
  "language": "en",
  "path": "C",
  "title": "Sapiens: A Brief History of Humankind",
  "author": "Yuval Noah Harari",
  "isbn13": "9780062316097",
  "isbn10": "0062316095",
  "publisher": "Harper",
  "publish_date": "2015",
  "publish_year": 2015,
  "pages": 443,
  "openlibrary_id": "OL27000666M",
  "languages": ["eng"],
  "sources": ["amazon.com", "openlibrary.org"],
  "source_count": 2,
  "confidence": "high",
  "goodreads_top": {
    "title": "Sapiens: A Brief History of Humankind",
    "author": "Yuval Noah Harari",
    "book_id": "23692271",
    "book_url": "https://www.goodreads.com/book/show/23692271-sapiens",
    "cover_url": "https://i.gr-assets.com/.../23692271._SX50_.jpg",
    "pages": 443,
    "avg_rating": "4.41",
    "ratings_count": 893421
  },
  "edition_rank": 1,
  "total_results": 10,
  "duration_seconds": 4.39,
  "errors": []
}
```

`goodreads_top` is denormalized on every edition item in the same run (Apify pattern, one record per dataset row). The Goodreads hint represents "what Goodreads thinks the query was about" and is NOT joined to specific ISBNs, it's diagnostic, not authoritative.

### Path dispatch, how input shape decides the route

```
        ┌───────────────────────────────────────┐
        │  q / title contains a valid ISBN?     │── yes ─► PATH A
        └─────────────────┬─────────────────────┘   direct OL by ISBN
                          │ no                       + Goodreads (parallel)
                          ▼
        ┌───────────────────────────────────────┐
        │  olid contains a valid OL...M token?  │── yes ─► PATH B
        └─────────────────┬─────────────────────┘   direct OL by OLID
                          │ no                       + author chase
                          ▼
        ┌───────────────────────────────────────┐
        │  title and/or author provided?        │── yes ─► PATH C
        └─────────────────┬─────────────────────┘   Google SERP + Goodreads
                          │                          + OL per-edition enrichment
                          │                            │
                          │                            │ 0 editions?
                          │                            ▼
                          │                          PATH D
                          │                          Open Library search rescue
                          │ no
                          ▼
                       HTTP 400
```

### Use cases

**Book inventory and catalog systems.** When a librarian or used-bookstore operator scans a barcode that returns ISBN-10 only (pre-2007 books), pass it as `q=<ISBN10>` to hit Path A. The actor validates the check digit, converts to ISBN-13, fetches the Open Library record, resolves the author key chain, and returns publisher + year + pages + language. One call replaces N retailer-specific lookups.

**Author bibliography enrichment.** For a book-blog or review-site backend, call with `author=Stephen King` for Path C → D. Path C tries Google SERP for retailer-indexed ISBNs; if SERP captchas, Path D rescues with Open Library search and returns 8-10 editions with publisher/year/pages already filled in. The Amazon URL-slug fallback recovers titles for SERP results where Google truncated the snippet.

**Cross-locale price comparison.** A Dutch-language book might be priced at €19.95 on Bol.com but €14.50 on Amazon.nl. To compare, you first need the same ISBN-13 across both retailers. This actor's per-edition `sources` array explicitly tells you which retailer carries which ISBN, feed the resulting ISBN-13 to your price-comparison logic. For Dutch books, call with `country=nl&language=nl` so Bol-dominated results dominate the Path C SERP.

**Edition validation from a publisher feed.** When a publisher sends you a list of ISBNs and you want to confirm each one resolves to a real cataloged book, pass each `q=<ISBN>` to Path A. The actor returns the canonical edition with publisher/year/pages, OR fails soft with `path=A, isbn13=null` for unmapped ISBNs. The `confidence` field tells you whether multiple sources agreed.

### How it compares

| Source | Coverage | Inputs accepted | Auth |
|---|---|---|---|
| **ISBN-International registry** | Authoritative; every published book | ISBN only | Paid subscription |
| **Open Library direct API** | Library-catalogued | ISBN, OLID, search | None, but you write the dispatch |
| **Goodreads autocomplete direct** | Goodreads-indexed | Free text only | None, but no ISBN in response |
| **Retailer-specific scrapers** | One retailer at a time | Search query | Often blocked / rate-limited |
| **This actor** | Open Library + Goodreads + 10 retailers via SERP, auto-dispatched | ISBN, OLID, title, author, free text | None |

This actor is for when you want INPUT-FLEXIBLE bibliographic resolution. The four-path dispatch means callers don't pre-classify inputs; you can pipe a barcode scan, a fuzzy title, or an OL identifier into the same endpoint.

### Pricing

Pay-per-event, one charge per edition successfully extracted. Path A returns 1 edition for 1 charge. Path C can return 3-10 editions, each charged separately. A run with zero matches (bad ISBN, captcha + no rescue) charges only the symbolic actor-start fee. No actor-start fee on result-only billing.

### Limits and gotchas

- **Synthetic / invalid ISBNs** fall through to SERP (Path C). The ISBN detector validates check digits before short-circuiting to Path A, bad check digits don't pollute the captcha-immune path with wrong matches.
- **Localized first edition**, Open Library's `isbn` array per record is not language-sorted, so the FIRST valid ISBN for `title=Sapiens` may be the French (Albin Michel 2022), Polish, or Thai edition rather than the English original. The `languages` field tells you which.
- **Goodreads ≠ editions.** `goodreads_top` represents what Goodreads' autocomplete returned for the query string. It is NOT joined to specific ISBNs, never assume `goodreads_top.title` describes any particular edition in the dataset. They can disagree (and that's diagnostic, not a bug).
- **SERP captcha**, Google captchas the underlying `/serp` endpoint fairly often. Path D (OL search rescue) handles this transparently but adds 15-25s of latency.
- **Path C OL enrichment is fail-soft**, when Open Library returns 5xx for a specific ISBN, that edition keeps its SERP-extracted title/author but loses the publisher/year/pages enrichment. Other editions in the same run are unaffected.
- **AbeBooks titles drop hyphens**, `Designing Data-Intensive Applications` becomes `Designing Data Intensive Applications`. The source-rank algorithm correctly prefers Amazon's cleaner snippet when both retailers surface the same ISBN.
- **979-prefix audiobooks have no ISBN-10.** The conversion only works for 978-prefix codes. Customers correctly see `isbn10: null` for audiobook-only editions.
- **OLID input bypasses Goodreads** when no title is resolved. Path B short-circuits to OL alone; Goodreads only fires after the OL record's title is known.

### FAQ

**Can I look up an ISBN without an API key?** Yes. Pass `q=9780374533557` and the actor takes Path A, direct Open Library lookup, no captchas, no third-party keys. Returns 1 edition with publisher/year/pages from OL plus the Goodreads autocomplete hint.

**Does it work for Open Library identifiers?** Yes, pass `olid=OL29832788M` (bare) or `olid=https://openlibrary.org/books/OL29832788M/Beach_Read` (full URL). Both forms work via Path B's regex extractor.

**What happens when Google captchas the SERP path?** Path D auto-rescues. The actor calls Open Library's `/search.json` with title + author, picks the first valid ISBN-13 from each returned work, and emits the editions with `path=D`. Total latency goes from 4-10s (Path C happy) to 18-35s (C → D rescue). The caller sees editions either way.

**Why is `author` null on some Path A returns?** Open Library stores authors as key references (`/authors/OL7410858A`). The actor chases the first author key into `/authors/{KEY}.json` to resolve the name. When OL returns 5xx for the author endpoint (which happens occasionally), the field stays null. The publisher/year/pages still populate from the main record.

**What's the difference between Path C and Path D?** Path C harvests ISBNs from retailer-indexed Google SERP results (Amazon, Goodreads, Bol, AbeBooks, etc.) and cross-confirms across multiple sources. Path D bypasses Google entirely and searches Open Library directly, fewer cross-source confirmations but immune to captchas. The actor picks C first and rescues with D only when C returns zero editions.

**How do I get just the SERP data without Open Library enrichment?** Pass `enrich=false`. This restores the legacy SERP-only behavior, Path C runs without the Open Library per-edition fan-out and without Goodreads autocomplete. Faster (~80ms cached) but no publisher/year/pages/languages fields populated.

### Related Actors

- [VIN Decoder](https://apify.com/s-r/vin-decoder), decode any 17-character VIN into year/make/model + recalls + safety
- [Phone Number Decoder](https://apify.com/s-r/phone-number-decoder), find every business phone for any domain
- [Backlinks Checker](https://apify.com/s-r/backlinks-checker), domain link profile and per-link records

# Actor input Schema

## `title` (type: `string`):

Book title (wrapped in quotes for exact-match search). At least one of title / author / q / olid is required.

## `author` (type: `string`):

Author name (wrapped in quotes). Combine with title for best precision.

## `q` (type: `string`):

Free-form query — accepts ISBN-10, ISBN-13, or a title. If it's a valid ISBN it short-circuits to the direct Open Library path (captcha-immune, ~1-4s).

## `olid` (type: `string`):

Open Library identifier like OL29832788M, or a full URL such as https://openlibrary.org/books/OL29832788M/Beach\_Read. Short-circuits SERP and Goodreads.

## `country` (type: `string`):

Google geo. Affects which Amazon TLDs dominate the SERP. Default: us

## `language` (type: `string`):

Google UI language. Default: en

## `enrich` (type: `boolean`):

When true (default), fan out Open Library per-edition enrichment and Goodreads autocomplete in parallel. Set false to restore SERP-only behavior.

## `hits` (type: `boolean`):

Include raw SERP results in the first dataset item for debugging extraction misses.

## Actor input object example

```json
{
  "title": "Sapiens",
  "author": "Yuval Noah Harari",
  "country": "us",
  "language": "en",
  "enrich": true,
  "hits": false
}
```

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "title": "Sapiens",
    "author": "Yuval Noah Harari"
};

// Run the Actor and wait for it to finish
const run = await client.actor("s-r/isbn-decoder").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "title": "Sapiens",
    "author": "Yuval Noah Harari",
}

# Run the Actor and wait for it to finish
run = client.actor("s-r/isbn-decoder").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "title": "Sapiens",
  "author": "Yuval Noah Harari"
}' |
apify call s-r/isbn-decoder --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=s-r/isbn-decoder",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/EiyfS1mSXpPHBst5j/builds/MfEb9yUJPNvSRJn0u/openapi.json
