# Books Scraper (`dami_studio/books-scraper`) Actor

Books scraper API for Open Library or Google Books, by keyword, title, subject or ISBN lookup. Each row holds the title, authors, publisher, year, ISBN, page count, subjects, rating, language and cover. Google Books adds description and price with your own free key. $1.00 per 1,000 books.

- **URL**: https://apify.com/dami_studio/books-scraper.md
- **Developed by:** [Dami's Studio](https://apify.com/dami_studio) (community)
- **Categories:** Integrations, AI, Other
- **Features:** Limited permissions
- **Stats:** 14 total users, 1 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.97 / 1,000 books

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

**Books scraper for Open Library or Google Books, by keyword, title, subject or ISBN, one row per book**:
title, subtitle, authors, publisher, year, ISBN, page count, subjects, rating, language and a cover
image. Both catalogues come out in the same shape, so you can switch between them without rewriting
anything downstream.

The two are not equivalent. Open Library answers with no key and no setup. Google Books carries the
description and the retail price, and to use it reliably you need a free Google key of your own,
which takes about two minutes to create.

| | |
|---|---|
| **Input** | A search query, or `isbn:9780132350884` |
| **Output** | One row per unique book: title, authors, publisher, year, ISBN, pages, subjects, rating, language, cover, link. Google adds description and price |
| **Ceiling** | 1,000 books per run |
| **Account needed** | None for Open Library. Google Books wants your own free key |
| **Price** | $1.00 per 1,000 books on the Free plan, a little less on paid plans. The free plan's $5 a month covers about 5,000 |

### 🔍 What Books Scraper does

It runs one search against the catalogue you pick and pages through the results until it has as many
unique books as you asked for.

**Open Library** is the default and the one to start with. No key, no account, nothing to set up.
Rows come back with the subjects list, the rating and vote count, the cover and the work page URL.
Open Library holds no blurb and no price, so `description` and `price` are `null` on every row from
it.

**Google Books** adds the publisher's description and, where the book is sold through Google, a
`price` object with the amount, currency and a buy link. Without a key those calls run on an
anonymous allowance shared by everyone using it, which is usually spent, so the run switches to Open
Library and writes one uncharged notice row telling you it did. Put your own key in `googleApiKey`
and the call runs on your own allowance instead.

Books arrive deduplicated: by ISBN where there is one, by title plus first author where there is
not. A record with no title is dropped before it is counted or charged.

### 📋 What data you get from each book

| What you get | Field |
|---|---|
| Which catalogue answered, and its id for the book | `source`, `sourceId` |
| Title, subtitle and authors | `title`, `subtitle`, `authors` |
| Publisher and publication date | `publisher`, `publishedDate`, `year` |
| ISBN and page count | `isbn`, `pageCount` |
| Subjects or categories | `categories` |
| Average rating and how many people rated it | `averageRating`, `ratingsCount` |
| Language | `language` |
| Cover image and the book's page | `coverImage`, `url` |
| Google only: the description and the price with a buy link | `description`, `price` |

### ▶️ How to scrape book data

1. Open [Books Scraper](https://apify.com/dami_studio/books-scraper) and click **Try for free**.
2. Leave **Source** on Open Library for your first run.
3. Type into **Search query**. A subject like `machine learning` works as well as a title.
4. Set **Max books**, then click **Start**.
5. Download the dataset as JSON, CSV, Excel or XML.

For Google Books, create a free API key in the Google Cloud console, switch on the Books API for that
project, and paste the key into **Google Books API key**.

### 💰 How much does it cost to scrape book data?

**$1.00 per 1,000 books** on the Free plan, and the same price whichever catalogue answered. Paid plans pay a little less: $0.99 on Starter, $0.98 on Scale and $0.97 on Business.
On the free plan, the $5 Apify gives you each month covers about 5,000 books.

You are billed per unique book row. Duplicates dropped along the way, records with no title, the
fallback notice row and every diagnostic row are not charged. Set a maximum cost on the run and it
stops when it gets there.

### 📥 What you give it

```json
{
  "source": "openlibrary",
  "query": "clean code",
  "maxItems": 50
}
```

| Field | Default | What it is |
|---|---|---|
| `query` | none, the form starts with `clean code` | Required. Keywords, a title, a subject, or `isbn:9780132350884`. An API call has to send its own. |
| `source` | `openlibrary` | `openlibrary` or `googlebooks`. Send it explicitly when you call from the API rather than relying on the form to fill it in. |
| `maxItems` | `100` | 1 to 1,000 unique books. Paging is automatic. |
| `googleApiKey` | none | Your own free Google Books key. Only read when `source` is `googlebooks`. Stored as a secret field and never written to the log. |
| `notionConnector` | none | Optional. Writes every delivered row into your Notion. |
| `notionParentId` | none | Optional. The Notion data source to write into. Leave it empty and the pages are created privately in your workspace. |
| `proxyConfiguration` | off | Optional network setting. Off is right for a normal run, and it makes no difference to the Google allowance. |

Never paste a key into a published Apify task. Task inputs are readable by anyone who opens the
page. Put it in the run input, or supply it per call.

### 📤 What you get back

A real row from a run on 2 October 2026:

```json
{
  "ok": true,
  "source": "openlibrary",
  "sourceId": "/works/OL17618370W",
  "title": "Clean Code",
  "subtitle": "A Handbook of Agile Software Craftsmanship",
  "authors": ["Robert C. Martin"],
  "publisher": "Prentice Hall",
  "publishedDate": "2008",
  "year": 2008,
  "isbn": "9780136083221",
  "pageCount": 444,
  "categories": ["Agile software development", "Reliability", "Computer software", "Computer software, development", "Coding theory"],
  "averageRating": 4.44,
  "ratingsCount": 43,
  "language": "eng",
  "description": null,
  "coverImage": "https://covers.openlibrary.org/b/id/8065615-L.jpg",
  "url": "https://openlibrary.org/works/OL17618370W",
  "price": null
}
```

| Field | How to read it |
|---|---|
| `source` | The catalogue that actually answered, which is not always the one you asked for. Read this rather than assuming. |
| `isbn` | ISBN-13 where the record has one, then ISBN-10, then whatever other identifier is listed. |
| `categories` | Open Library subjects, capped at 25 per book. Google returns its own shorter category list. |
| `averageRating`, `ratingsCount` | The catalogue's own ratings. `null` on a book nobody rated there. |
| `description` | Google only. Always `null` on an Open Library row. |
| `price` | Google only, and only when the book is on sale there: `{amount, currency, buyLink}`. Otherwise `null`. |
| `coverImage` | A cover URL, built rather than fetched, so an occasional one will not load. |

### 🧾 Reading the output

Real book rows carry `ok: true`. Anything with `ok: false` is a note or a problem, and none of those
are charged.

| Row | How to spot it | Billed |
|---|---|---|
| A book | `ok: true` and a `title` | yes |
| The fallback notice | `ok: false`, `errorCode: "RATE_LIMITED"`, first row of the dataset | no |
| A diagnostic | `ok: false` and any other `errorCode` | no |

| Code | What it means |
|---|---|
| `BAD_INPUT` | The query was blank, or `source` was not one of the two names. |
| `NO_RESULTS` | The search ran and the catalogue had nothing. The row carries `total` and `usedSource`. |
| `RATE_LIMITED` | Google Books was not available keyless, so the run finished on Open Library. Your books are in the rows after it. |
| `NOT_SERVED` | This book was not served this run. Re-run it later. Runs before 13 October 2026 wrote `BLOCKED` here, and runs from 13 to 17 October wrote `NETWORK`. |
| `TIME_LIMIT` | The run got close to its time limit and stopped on its own. The books delivered before that are in the dataset and charged as usual. Give the run more time, or ask for fewer books, to get the rest. |

A run still ends as succeeded, with an amber status message, even when the time limit stopped it before any book came back.

**A run that opens with what looks like an error is usually a run that worked**, because the
`RATE_LIMITED` notice is written before the books.

If Google Books returns nothing at all with a key set, suspect the key before the query. A key
Google will not accept reads back as a search with no matches, not as a key error.

### 💡 What people use it for

- Filling in a reading list or a library catalogue that only has titles, using ISBN as the join key.
- Pulling everything under a subject, like every book Open Library files under machine learning, to
  see what exists before buying.
- Building a price and description list for a shortlist of titles through Google Books.
- Checking page counts and publication years across a set of editions before choosing one.

### 🚧 What it does not do

- **No reviews and no review text.** Ratings and vote counts only.
- **No editions list.** One row per work, not one per printing, so the ISBN you get is a
  representative one rather than the specific edition on your shelf.
- **No full text, no excerpts, no tables of contents.**
- **Open Library rows have no description and no price.** That is the catalogue, not a setting you
  can change.
- **Google Books keyless is not dependable.** Expect the fallback unless you supply a key.
- **Covers are links, not files.** The image stays on the catalogue's servers.
- **Search quality is the catalogue's.** A loose query returns loosely related books, and those rows
  are charged like any other, so keep `maxItems` low while you find the right wording.
- It is not an official Open Library tool. Dami's Studio is independent and is not affiliated with or endorsed by Open Library, Google Books or Google, or by any other company named on this page.

### 🧭 Which reference scraper do you need?

| If you want | Use |
|---|---|
| Wikipedia search results or article text | [Wikipedia Scraper](https://apify.com/dami_studio/wikipedia-scraper) |
| Wikidata Q-ids, aliases and claims, by name or by ID | [Wikidata Scraper](https://apify.com/dami_studio/wikidata-scraper) |
| Book metadata by keyword, title or ISBN | This one |
| Goodreads ratings and rating counts from a book search | [Goodreads Books Scraper](https://apify.com/dami_studio/goodreads-books-scraper) |
| Books, audio, film and software held on archive.org | [Internet Archive Scraper](https://apify.com/dami_studio/internet-archive-scraper) |
| Papers with readable abstracts and citation counts | [OpenAlex Scraper](https://apify.com/dami_studio/openalex-scraper) |
| Wikipedia, arXiv and OpenAlex searches as tools for an AI agent | [Research MCP Server](https://apify.com/dami_studio/research-mcp-server) |

### ❓ Questions people ask

#### Do I need an API key to get book data?

Not for Open Library. For Google Books, yes in practice. It is free and takes a couple of minutes in
the Google Cloud console.

#### Can I search by ISBN?

Yes. Put `isbn:9780132350884` in the query. That works on Google Books.

#### Why does my row say openlibrary when I picked Google?

The run fell back, and the notice row at the top of the dataset says so. Add your own key to stay on
Google.

#### How do I avoid paying for the same book twice across runs?

Dedupe on `isbn`, falling back to `title` plus the first entry in `authors`, which is what the run
does internally.

#### Why did I get fewer books than I asked for?

The catalogue ran out of matches, or duplicates were removed. You pay for what arrived.

#### Can I call it from code or connect it to an AI assistant?

The [API tab](https://apify.com/dami_studio/books-scraper/api/python) has code for Python, JavaScript and the CLI. MCP
clients like Claude and ChatGPT use `https://mcp.apify.com/?tools=fetch-actor-details,dami_studio/books-scraper`;
coding assistants can load [our skill
file](https://api.apify.com/v2/key-value-stores/BTBHU3DdiuoF4JOsK/records/apify-social-communities-SKILL.md). All
three bill your Apify account at the same price.

#### Is it legal to scrape book data?

Open Library and Google Books both publish this data through open APIs meant to be read, and book
metadata is not personal data. Apify's write-up on
[the legality of web scraping](https://blog.apify.com/is-web-scraping-legal/) is a good starting
point, and we are not lawyers.

### 🆘 If something breaks

Open the **Issues** tab on the actor page. Send the query, the source you picked and the run ID. The
`errorCode` on the diagnostic row usually names the problem by itself.

# Actor input Schema

## `source` (type: `string`):

Which book catalog to search. Open Library is the reliable keyless source. Google Books returns descriptions and prices but its keyless mode shares one global Google project whose daily quota is usually exhausted (HTTP 429) - supply your own free API key below, or the actor falls back to Open Library automatically. Output is normalized identically for both.

## `query` (type: `string`):

Keywords to search for, e.g. "clean code", "the hobbit", or "isbn:9780132350884". Required.

## `maxItems` (type: `integer`):

Maximum number of unique books to return. Results are paginated automatically and deduplicated by ISBN (or title + author).

## `notionConnector` (type: `string`):

Optional. Write each result as a page into your Notion when the run finishes. Authorize a Notion connector once in Settings → API & Integrations → MCP connectors, then pick it here. Leave empty to skip (default), results are always saved to the dataset regardless.

## `notionParentId` (type: `string`):

Optional. The Notion data source ID of the database to write into (only used if a Notion connector is set). Leave empty to create the pages privately in your workspace instead.

## `googleApiKey` (type: `string`):

Optional. Only used when Source = Google Books. Keyless Google Books calls share one anonymous quota with the whole internet and it is usually already spent, so a free key of your own is the reliable way to use that source.

## `proxyConfiguration` (type: `object`):

Optional, and not needed for a normal run.

## Actor input object example

```json
{
  "source": "openlibrary",
  "query": "clean code",
  "maxItems": 100,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `results` (type: `string`):

Scraped rows are stored in the default dataset (one row per result). Blocked/empty/error runs return a single uncharged diagnostic row instead.

# 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 = {
    "query": "clean code"
};

// Run the Actor and wait for it to finish
const run = await client.actor("dami_studio/books-scraper").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 = { "query": "clean code" }

# Run the Actor and wait for it to finish
run = client.actor("dami_studio/books-scraper").call(run_input=run_input)

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

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

```

## CLI example

```bash
echo '{
  "query": "clean code"
}' |
apify call dami_studio/books-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,dami_studio/books-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/e0AftA7PIWNiM4C9T/builds/0g548KJzVUaSONnfF/openapi.json
