# Goodreads Scraper — search & extract book data (`serene_trombone/goodreads-scraper`) Actor

Scrape Goodreads — the world's largest book platform. Search books, extract ratings, authors, genres, ISBN, publication details, series info, and more. Output as JSON, CSV, or Excel.

- **URL**: https://apify.com/serene\_trombone/goodreads-scraper.md
- **Developed by:** [Steven Bennett](https://apify.com/serene_trombone) (community)
- **Categories:** Social media
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

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

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
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.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use 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

## Goodreads Scraper

> Extract book data from [Goodreads](https://www.goodreads.com/) — the world's largest book recommendation platform with **150M+ members**, **3.5B+ books cataloged**, and **90M+ reviews**.

**No existing Goodreads scraper on Apify Store.** First-mover advantage in this valuable niche.

![Goodreads Scraper demo](screenshot.jpg)

---

### Why Goodreads?

Goodreads is the definitive source for book metadata — ratings, reviews, genres, author info, series data, and more. Publishers, authors, marketers, librarians, and AI/ML teams all need this data:

- **Publishers** — track ratings and reviews for competitive titles
- **Authors** — monitor your book's performance and discoverability
- **Market researchers** — analyze genre trends, rating distributions, publication patterns
- **AI/ML teams** — build training datasets for recommendation systems, NLP models, book classifiers
- **Librarians** — enrich catalog metadata with crowdsourced ratings and genres

### Features

- 🔍 **Search anything** — books by title, author, or keyword
- 📚 **Rich book data** — title, author, rating, ratings count, reviews count, ISBN, ISBN-13, pages, publisher, publication year, format
- 🏷️ **Genres & shelves** — extract all genre tags/categories for each book
- 🏆 **Awards** — see which awards each book has won
- 📖 **Full descriptions** — get the complete book description text
- 🔗 **Series data** — identify series and series position
- 📊 **Rating analytics** — average rating and total rating counts
- 🔄 **Smart sorting** — by relevance, highest rated, or newest first
- 📄 **Automatic pagination** — paginates through all search results up to your max
- 🛡️ **Anti-bot resistant** — headless browser with stealth techniques
- 💰 **Pay-per-event pricing** — you only pay for what you use
- 🔌 **API-first** — call via REST API, Apify SDK, Python, or Zapier

### Pricing

| Event | Price |
|-------|-------|
| **Run start** | $0.50 per run |
| **Per book scraped** | $0.001 per book |

A typical search for "science fiction" (1,000 results) would cost **~$1.50**. A small search (50 books) costs **~$0.55**.

### Quick Start

#### Via Apify Console

1. Go to [Goodreads Scraper](https://console.apify.com/actors/~goodreads-scraper) in Apify Console
2. Click **Run** and fill in the fields:
   - `search` — what to look for (e.g. "Dune", "Stephen King", "fantasy")
   - `maxItems` — how many books to scrape (default: 50, max: 1000)
   - `sort` — sort order: relevance (default), highest rated, or newest first
3. Wait for the run to complete
4. Download results as JSON, CSV, or Excel

#### Via API

```bash
curl -X POST "https://api.apify.com/v2/acts/~goodreads-scraper/runs?token=YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "search": "The Name of the Wind",
    "maxItems": 50,
    "sort": "relevance"
  }'
````

#### Via Apify SDK (Node.js)

```javascript
import { Actor } from 'apify';

const run = await Actor.call('~goodreads-scraper', {
  search: 'The Name of the Wind',
  maxItems: 50,
  sort: 'rating',
});

const { items } = await Actor.openDataset(run.defaultDatasetId);
console.log(items);
```

#### Via Python

```python
import requests

response = requests.post(
    'https://api.apify.com/v2/acts/~goodreads-scraper/runs',
    params={'token': 'YOUR_API_TOKEN'},
    json={
        'search': 'The Name of the Wind',
        'maxItems': 50,
        'sort': 'relevance'
    }
)
run = response.json()
print(f'Run ID: {run["data"]["id"]}')
```

#### Via Webhook/Zapier

Configure a webhook in Apify Console to send data directly to Zapier, Make (formerly Integromat), Slack, Google Sheets, or any HTTP endpoint.

### Input Parameters

| Parameter | Type     | Default      | Description                                         |
|-----------|----------|--------------|-----------------------------------------------------|
| `search`  | `string` | **required** | Search keyword, title, or author name               |
| `maxItems`| `number` | `50`         | Max books to scrape (1–1000)                        |
| `sort`    | `string` | `relevance`  | Sort order: `relevance`, `rating`, `date`           |
| `proxyConfiguration` | `object` | Apify proxy | Proxy configuration (recommended for large runs) |

### Output Schema

Each scraped book returns the following fields:

| #  | Field            | Type              | Description                         |
|----|------------------|-------------------|-------------------------------------|
| 1  | `id`             | `number`          | Goodreads book ID                   |
| 2  | `title`          | `string \| null`  | Book title                          |
| 3  | `author`         | `string \| null`  | Author name                         |
| 4  | `authorUrl`      | `string \| null`  | Author profile URL                  |
| 5  | `rating`         | `number \| null`  | Average rating (0–5)                |
| 6  | `ratingsCount`   | `number \| null`  | Number of ratings                   |
| 7  | `reviewsCount`   | `number \| null`  | Number of text reviews              |
| 8  | `isbn`           | `string \| null`  | ISBN-10 identifier                  |
| 9  | `isbn13`         | `string \| null`  | ISBN-13 identifier                  |
| 10 | `pages`          | `number \| null`  | Number of pages                     |
| 11 | `publicationYear`| `number \| null`  | Year of publication                 |
| 12 | `publisher`      | `string \| null`  | Publisher name                      |
| 13 | `description`    | `string \| null`  | Full book description               |
| 14 | `genres`         | `string[]`        | Genre/category tags (up to 30)      |
| 15 | `awards`         | `string[]`        | Awards the book has won             |
| 16 | `imageUrl`       | `string \| null`  | Book cover image URL                |
| 17 | `format`         | `string \| null`  | Book format (Paperback, Hardcover)  |
| 18 | `series`         | `string \| null`  | Series name (if part of a series)   |
| 19 | `url`            | `string`          | Goodreads book page URL             |
| 20 | `price`          | `number \| null`  | Price (if available)                |
| 21 | `scrapedAt`      | `string`          | ISO 8601 timestamp of scrape        |

### Sample Output

```json
{
  "id": 186074,
  "title": "The Name of the Wind",
  "author": "Patrick Rothfuss",
  "authorUrl": "https://www.goodreads.com/author/show/108713.Patrick_Rothfuss",
  "rating": 4.53,
  "ratingsCount": 1051940,
  "reviewsCount": 76291,
  "isbn": "0575081406",
  "isbn13": "9780575081406",
  "pages": 662,
  "publicationYear": 2007,
  "publisher": "Gollancz",
  "description": "Told in Kvothe's own voice, this is the tale of the magically gifted young man...",
  "genres": ["Fantasy", "Fiction", "High Fantasy", "Epic Fantasy", "Magic", "Adventure"],
  "awards": ["Quill Award for Science Fiction/Fantasy/Horror (2007)", "David Gemmell Legend Award (2008)"],
  "imageUrl": "https://images.gr-assets.com/books/1472067508m/186074.jpg",
  "format": "Paperback",
  "series": "The Kingkiller Chronicle (1)",
  "url": "https://www.goodreads.com/book/show/186074",
  "price": null,
  "scrapedAt": "2026-07-04T12:00:00.000Z"
}
```

### Use Cases

#### 📊 Publishing & Market Research

Analyze ratings, review sentiment, and genre trends across thousands of books. Identify emerging genres, track author popularity over time, and benchmark competitor titles.

#### 🤖 AI/ML Training Data

Build high-quality datasets for book recommendation systems, NLP models, sentiment analysis, text classification, and genre prediction. Goodreads data is ideal for:

- **Matrix factorization** — user-item rating matrices
- **Content-based filtering** — book metadata & genre features
- **NLP training** — authentic review text corpora
- **Book embeddings** — genre-tagged, rated collections

#### 📝 Author & Self-Publishing

Monitor your book's performance, track ratings and reviews, compare against similar titles, and identify keywords for better discoverability.

#### 🏫 Academic Research

Bibliometric studies, network analysis of reading patterns, genre evolution tracking, literary analysis at scale.

#### 📚 Library Science

Enrich library catalog metadata with crowdsourced ratings, shelf/genre tags, series information, and cover images.

### Rate Limits & Anti-Bot

Goodreads is a content platform and generally doesn't aggressively block scrapers. However, for reliability at scale, this Actor includes:

- ✅ Headless Playwright browser (full JS rendering)
- ✅ Apify proxy support for IP rotation
- ✅ Request retry logic with exponential backoff
- ✅ Browser stealth techniques (spoofed webdriver, plugins, languages)
- ✅ Polite delays between requests (human-like timing)

### Technical Details

#### Architecture

The scraper uses a two-phase approach:

1. **Search phase** — Crawls Goodreads search results pages, extracting basic info (title, author, rating, ratings count) and collecting book IDs
2. **Detail phase** — Visits each book's detail page, extracting rich metadata from both **JSON-LD structured data** and **DOM parsing** for comprehensive coverage

#### RSC Payload Support

Goodreads has been migrating pages to Next.js App Router. The scraper checks for **React Server Components (RSC)** streaming payloads (`self.__next_f.push`) first — if found, it extracts structured data directly without DOM parsing. This is:

- Faster (no DOM parsing overhead)
- More resilient (resists layout changes)
- More accurate (same data the app renders)

If RSC data isn't available (e.g. legacy pages), it falls back automatically to HTML/Cheerio parsing with JSON-LD extraction.

#### Stack

**Crawlee (PlaywrightCrawler)** · Node.js 20 · **Cheerio** for HTML fallback · **Apify SDK** · JSON-LD extraction · RSC payload parsing

### Changelog

#### v1.0 — 2026-07-04

- Initial release
- Book search with full pagination
- Full detail extraction (RSC → JSON-LD → DOM)
- Genre/category extraction
- Award tracking
- Series information
- Three sort options (relevance, rating, date)
- Proxy support with Apify proxy

***

*Built with 🛠️ by Meester Bot · Not affiliated with Goodreads or Amazon*

# Actor input Schema

## `search` (type: `string`):

What to search for on Goodreads — book title, author name, or keyword (e.g. 'Dune', 'Stephen King', 'science fiction')

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

Maximum number of books to scrape (1–1000)

## `sort` (type: `string`):

How to sort search results

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

Proxy configuration for scraping. Goodreads typically works with datacenter proxies.

## Actor input object example

```json
{
  "search": "The Name of the Wind",
  "maxItems": 50,
  "sort": "relevance"
}
```

# 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 = {
    "search": "The Name of the Wind"
};

// Run the Actor and wait for it to finish
const run = await client.actor("serene_trombone/goodreads-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 = { "search": "The Name of the Wind" }

# Run the Actor and wait for it to finish
run = client.actor("serene_trombone/goodreads-scraper").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 '{
  "search": "The Name of the Wind"
}' |
apify call serene_trombone/goodreads-scraper --silent --output-dataset

```

## MCP server setup

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

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Goodreads Scraper — search & extract book data",
        "description": "Scrape Goodreads — the world's largest book platform. Search books, extract ratings, authors, genres, ISBN, publication details, series info, and more. Output as JSON, CSV, or Excel.",
        "version": "1.0",
        "x-build-id": "c1NXWHMdiKaqkHMGF"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/serene_trombone~goodreads-scraper/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-serene_trombone-goodreads-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/serene_trombone~goodreads-scraper/runs": {
            "post": {
                "operationId": "runs-sync-serene_trombone-goodreads-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/serene_trombone~goodreads-scraper/run-sync": {
            "post": {
                "operationId": "run-sync-serene_trombone-goodreads-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "required": [
                    "search"
                ],
                "properties": {
                    "search": {
                        "title": "Search term",
                        "type": "string",
                        "description": "What to search for on Goodreads — book title, author name, or keyword (e.g. 'Dune', 'Stephen King', 'science fiction')"
                    },
                    "maxItems": {
                        "title": "Max items",
                        "minimum": 1,
                        "maximum": 1000,
                        "type": "integer",
                        "description": "Maximum number of books to scrape (1–1000)",
                        "default": 50
                    },
                    "sort": {
                        "title": "Sort order",
                        "enum": [
                            "relevance",
                            "rating",
                            "date"
                        ],
                        "type": "string",
                        "description": "How to sort search results",
                        "default": "relevance"
                    },
                    "proxyConfiguration": {
                        "title": "Proxy configuration",
                        "type": "object",
                        "description": "Proxy configuration for scraping. Goodreads typically works with datacenter proxies."
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
