# Goodreads Book Scraper (`parsebird/goodreads-book-scraper`) Actor

Scrape Goodreads search results by keyword: book titles, series, authors, ratings, rating counts, publication year, editions, and cover images. Multiple keywords per run, export to JSON, CSV, or Excel.

- **URL**: https://apify.com/parsebird/goodreads-book-scraper.md
- **Developed by:** [ParseBird](https://apify.com/parsebird) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.99 / 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

### Goodreads Book Scraper

Goodreads Book Scraper searches **Goodreads by keyword, title, author, or ISBN** and saves every matching book with its series, authors, average rating, rating count, publication year, number of editions, and cover image. Enter one or more search words and get one clean row per book.

<table><tr>
<td style="border-left:4px solid #1C1917;padding:12px 16px;font-weight:600">
Turn any Goodreads search into a spreadsheet of books, with series and translator credits split into their own fields, run dozens of keywords at once, and go past the 2,000-book limit of a single Goodreads search.
</td>
</tr></table>

##### Copy to your AI assistant

Copy this block into ChatGPT, Claude, Cursor, or any LLM to start using this actor.

```
Actor: parsebird/goodreads-book-scraper — scrapes Goodreads book search results. Input: {"keywords": ["shaker"] (array of strings, required; titles, author names, topics, or ISBNs, one search per keyword), "maxItems": 30 (books per keyword, 1-10000; Goodreads shows max 2,000 per search, above that the actor adds title-only and author-only searches and skips duplicates), "searchField": "all"|"title"|"author" (default "all"), "proxyConfiguration": {"useApifyProxy": false} (optional; the actor switches to Apify Proxy by itself when Goodreads rate-limits)}. Run via ApifyClient("YOUR_TOKEN").actor("parsebird/goodreads-book-scraper").call(run_input=input). Output, one row per book: keyword, rank, bookId, workId, title (with series), titleWithoutSeries, series[] {name, number}, url, author {name, url, isGoodreadsAuthor}, authors[] {name, url, isGoodreadsAuthor, role}, cover, coverLarge, rating {average, count}, publishYear, editions, editionsUrl. Pricing: pay per book saved. Get your API token at https://console.apify.com/account#/integrations. Full API spec: https://apify.com/parsebird/goodreads-book-scraper/api
```

### What is Goodreads Book Scraper?

Goodreads Book Scraper is a **Goodreads scraping tool** that runs the same book search you see on [Goodreads.com](https://www.goodreads.com/search) and turns the results into structured data. Goodreads stopped issuing new developer API keys in December 2020, so this scraper works as a **Goodreads API alternative** for readers, publishers, authors, booksellers, and data teams.

The easiest way to try it: keep the prefilled keyword `shaker`, click **Start**, and open the dataset. You get 30 books with authors, ratings, rating counts, publication years, and edition counts in a few seconds.

No Goodreads account or API key is needed.

### What can Goodreads Book Scraper do?

- 🔎 **Search by anything** — book titles, author names, topics, or ISBNs. An ISBN search returns the exact book.
- 📚 **Many keywords per run** — add a list of keywords and each one runs as its own search. Every row records its `keyword` and `rank`.
- 🎯 **Search in titles or authors only** — narrow the match to the title or the author name, just like the Goodreads search filter.
- 📈 **More than 2,000 books per keyword** — Goodreads stops a search at 100 pages. When you ask for more, the scraper also runs a title search and an author search for the same keyword and adds the books it has not saved yet.
- 🧩 **Clean, split fields** — the series name and number come out of the title, every contributor gets a role (author, translator, editor, illustrator), and cover links come in thumbnail and full size.
- 🛡️ **Rate-limit handling** — when Goodreads starts refusing requests, the scraper retries on a new connection and moves to Apify Proxy without you changing anything.

Because Goodreads Book Scraper runs on the Apify platform, you also get:

- ⏰ [Scheduling](https://docs.apify.com/platform/schedules) to track ratings and new books daily, weekly, or monthly.
- 🔌 [API access](https://docs.apify.com/api/v2) and client libraries for Python and JavaScript.
- 🔗 [Integrations](https://apify.com/integrations) with Make, Zapier, Google Sheets, Slack, webhooks, and more.
- 📦 Export to JSON, CSV, Excel, XML, or HTML from the Storage tab or the API.

### What data can you extract from Goodreads?

| Field | Description |
|-------|-------------|
| `keyword` / `rank` | The search keyword and the book's position among the books saved for it |
| `bookId` / `workId` | Goodreads book (edition) ID and work ID, which groups all editions |
| `title` | Full title as Goodreads shows it, including the series, e.g. `Shaker (Blackwings MC, #5)` |
| `titleWithoutSeries` | The title on its own, e.g. `Shaker` |
| `series` | List of series with `name` and `number` (number can be `5.5` or `1-3`) |
| `url` | Book page URL on Goodreads |
| `author` | First listed author: `name`, `url`, and `isGoodreadsAuthor` |
| `authors` | Every contributor with `name`, `url`, `isGoodreadsAuthor`, and `role` (Author, Translator, Editor, Illustrator, ...) |
| `cover` / `coverLarge` | Thumbnail cover URL and full-size cover URL (`coverLarge` is `null` when Goodreads has no cover) |
| `rating.average` / `rating.count` | Average star rating (out of 5, `null` for books with no ratings) and number of ratings |
| `publishYear` | Year the work was first published (negative for BC, e.g. `-800` for Homer) |
| `editions` / `editionsUrl` | Number of editions of the work and the link to its editions page |

### How to scrape Goodreads books

1. Open [Goodreads Book Scraper](https://apify.com/parsebird/goodreads-book-scraper) in Apify Console and click **Try for free**.
2. Under **Search keywords**, enter one or more words: a title (`the hobbit`), an author (`Brandon Sanderson`), a topic (`stoicism`), or an ISBN (`9780439023481`).
3. Set **Max books per keyword**, for example 100.
4. Optionally choose **Search in** → **Title** or **Author** to narrow the match.
5. Click **Start** and wait for the run to finish.
6. Open the **Output** tab to preview books, or export them as JSON, CSV, Excel, XML, or HTML.

### Input parameters

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `keywords` | array of strings | **Yes** | `["shaker"]` | Words to search for on Goodreads: titles, author names, topics, or ISBNs. Each keyword is a separate search. |
| `maxItems` | integer | No | 30 | Maximum books to save per keyword (1 to 10,000). |
| `searchField` | string | No | `all` | Where Goodreads matches the keyword: `all` (title, author, ISBN), `title`, or `author`. |
| `proxyConfiguration` | object | No | `{"useApifyProxy": false}` | Optional proxy. Not needed in most runs; the scraper moves to Apify Proxy on its own when Goodreads rate-limits. |

Example input:

```json
{
    "keywords": ["shaker"],
    "maxItems": 50
}
```

### Output example

Each book is one dataset item. Download the results in JSON, CSV, Excel, XML, or HTML.

```json
[
    {
        "keyword": "shaker",
        "rank": 1,
        "bookId": 43999679,
        "workId": 68403363,
        "title": "Shaker (Blackwings MC, #5)",
        "titleWithoutSeries": "Shaker",
        "series": [{ "name": "Blackwings MC", "number": "5" }],
        "url": "https://www.goodreads.com/book/show/43999679-shaker",
        "author": {
            "name": "Teagan Brooks",
            "url": "https://www.goodreads.com/author/show/18042726.Teagan_Brooks",
            "isGoodreadsAuthor": true
        },
        "authors": [
            {
                "name": "Teagan Brooks",
                "url": "https://www.goodreads.com/author/show/18042726.Teagan_Brooks",
                "isGoodreadsAuthor": true,
                "role": "Author"
            }
        ],
        "cover": "https://i.gr-assets.com/images/S/compressed.photo.goodreads.com/books/1550046473i/43999679._SY75_.jpg",
        "coverLarge": "https://i.gr-assets.com/images/S/compressed.photo.goodreads.com/books/1550046473i/43999679.jpg",
        "rating": { "average": 4.49, "count": 3493 },
        "publishYear": 2019,
        "editions": 6,
        "editionsUrl": "https://www.goodreads.com/work/editions/68403363-shaker-blackwings-mc-5"
    },
    {
        "keyword": "odyssey homer",
        "rank": 8,
        "bookId": 1375,
        "workId": 1474309,
        "title": "The Iliad / The Odyssey",
        "titleWithoutSeries": "The Iliad / The Odyssey",
        "series": [],
        "url": "https://www.goodreads.com/book/show/1375.The_Iliad_The_Odyssey",
        "author": {
            "name": "Homer",
            "url": "https://www.goodreads.com/author/show/903.Homer",
            "isGoodreadsAuthor": false
        },
        "authors": [
            { "name": "Homer", "url": "https://www.goodreads.com/author/show/903.Homer", "isGoodreadsAuthor": false, "role": "Author" },
            { "name": "Robert Fagles", "url": "https://www.goodreads.com/author/show/1005.Robert_Fagles", "isGoodreadsAuthor": false, "role": "Translator" },
            { "name": "Bernard Knox", "url": "https://www.goodreads.com/author/show/280215.Bernard_Knox", "isGoodreadsAuthor": false, "role": "Introduction" }
        ],
        "cover": "https://i.gr-assets.com/images/S/compressed.photo.goodreads.com/books/1388181863i/1375._SX50_.jpg",
        "coverLarge": "https://i.gr-assets.com/images/S/compressed.photo.goodreads.com/books/1388181863i/1375.jpg",
        "rating": { "average": 4.08, "count": 87207 },
        "publishYear": -800,
        "editions": 132,
        "editionsUrl": "https://www.goodreads.com/work/editions/1474309"
    }
]
```

### Use cases

- **Book market research** — see which titles, series, and authors dominate a genre or topic, and how many ratings they have.
- **Publishing and acquisitions** — compare ratings and edition counts of comparable titles before signing or pricing a book.
- **Author and series tracking** — schedule a run for an author name and watch rating counts grow over time.
- **Bookstore and library catalogs** — match ISBNs or titles to Goodreads IDs, ratings, and cover images.
- **Reading apps and recommendation engines** — seed a database with book IDs, series, and ratings for many keywords at once.
- **Data science and AI** — build datasets of books, authors, and ratings for analysis, dashboards, or model training.

### How it works

1. For each keyword, Goodreads Book Scraper loads the Goodreads search results page by page (20 books per page), several pages in parallel.
2. Each result row is read into structured fields: the series is split out of the title, every contributor gets a role, and the cover link is also given in full size.
3. Goodreads serves at most 100 pages (2,000 books) per search. If you ask for more books and the keyword has more results, the scraper also searches the keyword in titles only and in author names only, and saves the books that were not in the first search. Each book is saved once per keyword.
4. Goodreads' search pages are not stable: the same search can repeat some books on later pages and skip others. When a search has fewer results than you asked for, the scraper reads it again (up to two more times) and adds the books it missed, stopping early when a pass finds nothing new.
5. When a search for an ISBN or book ID opens a book page directly, the scraper reads that book and saves it as one row.
6. Goodreads limits how many requests one IP address can send. When it starts refusing, the scraper retries on a new connection and switches to Apify Proxy, first datacenter and then residential if needed.

### How much does it cost to scrape Goodreads?

Goodreads Book Scraper uses pay-per-event pricing. You pay per book saved to the dataset. Compute and proxy costs are included in the price, so there are no extra platform usage charges.

| Event | Free plan | Bronze | Silver | Gold |
|-------|-----------|--------|--------|------|
| `book-scraped` — per book saved | $0.00299 | $0.00259 | $0.00229 | $0.00199 |
| Per 1,000 books | **$2.99** | **$2.59** | **$2.29** | **$1.99** |

For example, the prefilled run (30 books) costs about $0.09 on the Free plan, and 1,000 books cost $2.99. Use **Max books per keyword** or a [spending limit](https://docs.apify.com/platform/actors/running/usage-and-resources) to cap a run; the scraper stops as soon as the limit is reached. Apify's Free plan includes monthly platform credits you can use to try the scraper.

### Use Goodreads Book Scraper with the API

**Python**

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("parsebird/goodreads-book-scraper").call(run_input={
    "keywords": ["Brandon Sanderson", "Ursula K. Le Guin"],
    "searchField": "author",
    "maxItems": 100,
})
for book in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(book["keyword"], book["title"], book["rating"]["average"], book["rating"]["count"])
```

**JavaScript**

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });
const run = await client.actor('parsebird/goodreads-book-scraper').call({
    keywords: ['stoicism', 'habit building'],
    maxItems: 200,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
const topRated = items
    .filter((b) => b.rating.count >= 1000)
    .sort((a, b) => b.rating.average - a.rating.average)
    .slice(0, 10);
console.log(topRated.map((b) => `${b.rating.average} ${b.title} by ${b.author?.name}`));
```

See the [API tab](https://apify.com/parsebird/goodreads-book-scraper/api) for all endpoints and the [Apify API client docs](https://docs.apify.com/api/client/python) for more options.

### Is it legal to scrape Goodreads?

Goodreads Book Scraper only reads book search results that Goodreads shows publicly to logged-out visitors: book titles, author names, ratings, and edition counts. It does not log in or collect private user data. Make sure your use complies with Goodreads' terms, copyright rules, data protection laws such as GDPR, and any other rules that apply to you, and consult a lawyer if in doubt. Read more in Apify's post [Is web scraping legal?](https://blog.apify.com/is-web-scraping-legal/)

### Other content and media scrapers

- [Substack Scraper](https://apify.com/parsebird/substack-scraper) — newsletters, posts, and authors from Substack.
- [Genius.com Scraper](https://apify.com/parsebird/genius-scraper) — song lyrics and metadata from Genius.
- [Podchaser Podcast Scraper](https://apify.com/parsebird/podchaser-scraper) — podcast and episode data from Podchaser.
- [Coursera Scraper](https://apify.com/parsebird/coursera-scraper) — online courses, ratings, and instructors from Coursera.
- [Kickstarter Scraper](https://apify.com/parsebird/kickstarter-scraper) — crowdfunding projects, including book and publishing campaigns.
- [Patreon Scraper](https://apify.com/parsebird/patreon-scraper) — creator pages and membership tiers from Patreon.

### FAQ

**Do I need a Goodreads account or API key?**
No. Goodreads Book Scraper reads the public search results without logging in. Goodreads stopped issuing developer API keys in 2020, so no key is involved.

**How many books can I get per keyword?**
Goodreads shows at most 100 pages of 20 books (2,000 books) for one search. Above that, the scraper adds a title-only and an author-only search for the same keyword. In testing, the keyword `shaker` returned 2,145 unique books this way. Broad keywords have far more matches than Goodreads will list, so split them into narrower keywords to go further.

**Why does the result count in the log not match the number of books?**
Goodreads shows an estimated total ("about 151 results"), and its result pages overlap: some books appear twice and others are skipped. The scraper saves each book once and re-reads small searches to fill the gaps, so you get close to, but not always exactly, the estimated total. In testing, the keyword `shaker heights` (about 151 results) returned 136 to 145 unique books per run.

**What does `publishYear` mean?**
It is the year the work was first published, as shown in the Goodreads search results, not the year of the specific edition. Ancient works have negative years, e.g. `-800` for The Odyssey. Books without a known year return `null`.

**Can I search by ISBN?**
Yes. Put the ISBN-10 or ISBN-13 in **Search keywords**. Goodreads opens the matching book directly and the scraper saves it as one row. The `editions` count is `null` for these rows because the book page doesn't show it.

**Does it scrape reviews, descriptions, or genres?**
No. This actor returns the data shown in Goodreads search results. Use the `url` field to open each book page for more details.

**Do I need a proxy?**
Usually not. Goodreads limits how many requests one IP address can send in a short time. When that happens, the scraper switches to Apify Proxy by itself. You only need to set a proxy if you want to force one for every request.

**Can I schedule runs or connect the results to other tools?**
Yes. Use [Apify schedules](https://docs.apify.com/platform/schedules) for recurring runs and [integrations](https://apify.com/integrations) to send results to Google Sheets, Slack, Zapier, Make, or a webhook. Results are also available through the [API](https://apify.com/parsebird/goodreads-book-scraper/api).

**Something doesn't work. What should I do?**
Open an issue on the [Issues tab](https://apify.com/parsebird/goodreads-book-scraper/issues) with your input and run link.

# Changelog

This Actor's version history is a separate document: https://apify.com/parsebird/goodreads-book-scraper/changelog.md

# Actor input Schema

## `keywords` (type: `array`):

Enter words to search for on Goodreads, one per line. Each keyword runs as its own search, the same as typing it into the Goodreads search box. Titles, author names, topics, and ISBNs all work.

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

Stop after saving this many books for each keyword. Goodreads shows at most 100 pages (2,000 books) per search; when you ask for more, the scraper also searches in titles and in author names and adds the books it has not seen yet.

## `searchField` (type: `string`):

Choose which part of the book Goodreads matches your keyword against: everything, the title only, or the author name only.

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

Goodreads usually works without a proxy. If Goodreads starts limiting requests, the scraper switches to Apify Proxy on its own. Set a proxy here only if you want to force one.

## Actor input object example

```json
{
  "keywords": [
    "shaker"
  ],
  "maxItems": 30,
  "searchField": "all",
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `dataset` (type: `string`):

No description

# 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 = {
    "keywords": [
        "shaker"
    ],
    "maxItems": 30
};

// Run the Actor and wait for it to finish
const run = await client.actor("parsebird/goodreads-book-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 = {
    "keywords": ["shaker"],
    "maxItems": 30,
}

# Run the Actor and wait for it to finish
run = client.actor("parsebird/goodreads-book-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 '{
  "keywords": [
    "shaker"
  ],
  "maxItems": 30
}' |
apify call parsebird/goodreads-book-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,parsebird/goodreads-book-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/ks0NLdo6QeHb03h33/builds/7esQXLIxxBeLBoekK/openapi.json
