# Goodreads Scraper — Books, Ratings & Reviews (`hyperbach/goodreads-scraper`) Actor

Scrape public Goodreads book metadata, 1–5 star rating distributions, review-language counts, and linked reader reviews from book URLs, ISBNs, or queries.

- **URL**: https://apify.com/hyperbach/goodreads-scraper.md
- **Developed by:** [Hyperbach](https://apify.com/hyperbach) (community)
- **Categories:** Social media, Other, E-commerce
- **Stats:** 3 total users, 2 monthly users, 33.3% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 results

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/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

## Goodreads Scraper — Books, Ratings & Reviews

**Book metadata, rating distributions, language counts, and public reader reviews from Goodreads.** Start from exact book URLs, ISBN-10/13 values, or search queries; no Goodreads login or API key is required.

### What this Actor covers — and what it does not

- **Exact-book detail, not broad catalog crawling.** The Actor accepts book URLs and ISBNs, plus the top five matches for each text query. It does not walk Goodreads shelves, lists, author catalogs, or every search page.
- **Distribution data in the base book record.** `ratingHistogram` carries the 1-to-5-star counts and `reviewsByLanguage` carries public text-review counts by language when Goodreads exposes its page state.
- **Book and review rows in one bounded run.** Set `maxItems` for the combined output cap and `maxReviewsPerBook` for the per-book review cap. Reviews link back through `bookId`.
- **Public pages only.** No account, cookies, private shelves, or authenticated reader data. Goodreads can omit fields or temporarily challenge automated requests, so unavailable values remain null or empty and blocked books are logged and skipped.

### Who it's for

- **Publishers and book marketers** — compare ratings, rating distributions, review volume, and public reader responses for exact titles.
- **Catalog and recommendation teams** — enrich ISBN/title records with Goodreads identifiers, genres, edition metadata, covers, and aggregate ratings.
- **Researchers and analysts** — collect bounded, reproducible public book and review samples with stable book IDs.

### Quick start

**One exact book**

```json
{
  "bookUrls": [
    "https://www.goodreads.com/book/show/3735293-clean-code"
  ],
  "includeReviews": false,
  "maxItems": 1
}
```

**ISBN with up to ten reviews**

```json
{
  "isbns": [
    "9780132350884"
  ],
  "includeReviews": true,
  "maxReviewsPerBook": 10,
  "maxItems": 11
}
```

**Search, filter, and cap**

```json
{
  "queries": [
    "clean code"
  ],
  "minRating": 4.0,
  "includeReviews": false,
  "maxItems": 5
}
```

### Output

The dataset contains `book` records and, when requested, linked `review` records. `itemType` identifies the shape and `bookId` joins reviews to books.

| field | type | meaning |
|---|---|---|
| `itemType` | `string` | Record discriminator: `book` or `review`. |
| `bookId` | `integer` | Goodreads' stable numeric identifier for the book; joins book and review records. |
| `url` | `string` | Canonical Goodreads URL for a book record. |
| `title` | `string` | Book title. |
| `titleComplete` | `string` | Complete edition title, including subtitle when Goodreads exposes it. |
| `authors` | `array` | Authors and their public Goodreads profile URLs. |
| `description` | `string` | Plain-text book description from the public edition page. |
| `genres` | `array` | Genre names associated with the book. |
| `coverUrl` | `string` | Book cover image URL published by Goodreads. |
| `averageRating` | `number` | Current Goodreads average rating for the book. |
| `ratingsCount` | `integer` | Current number of Goodreads ratings. |
| `reviewsCount` | `integer` | Current number of Goodreads text reviews. |
| `ratingHistogram` | `object` | Counts for 1-, 2-, 3-, 4-, and 5-star ratings, keyed by star value. |
| `reviewsByLanguage` | `array` | Public text-review counts grouped by ISO language code. |
| `isbn` | `string` | ISBN-10 for the edition, when available. |
| `isbn13` | `string` | ISBN-13 for the edition, when available. |
| `asin` | `string` | Amazon Standard Identification Number for the edition, when available. |
| `pages` | `integer` | Page count for the displayed edition. |
| `publisher` | `string` | Publisher for the displayed edition. |
| `language` | `string` | Language of the displayed edition. |
| `format` | `string` | Edition format, such as Paperback, Hardcover, or Kindle Edition. |
| `publishedAt` | `string` | Publication date of the displayed edition in YYYY-MM-DD form. |
| `originalPublishedAt` | `string` | Original work publication date in YYYY-MM-DD form, when available. |
| `originalTitle` | `string` | Original title of the work, when available. |
| `series` | `array` | Series memberships with title and the book's position. |
| `awards` | `array` | Awards recorded for the work, including year, category, and designation when available. |
| `workId` | `integer` | Goodreads' numeric identifier for the underlying work across editions. |
| `scrapedAt` | `string` | UTC timestamp when the record was scraped. |
| `reviewId` | `integer` | Goodreads' stable numeric identifier for the public review. |
| `bookTitle` | `string` | Book title repeated on a review record for export convenience. |
| `reviewerName` | `string` | Public display name of the reviewer. |
| `reviewerUrl` | `string` | Public Goodreads profile URL for the reviewer. |
| `rating` | `number` | Stars assigned by the reviewer, including half-stars where Goodreads exposes them. |
| `reviewText` | `string` | Visible public review text with markup removed. |
| `reviewDate` | `string` | Review date text as Goodreads displays it. |
| `reviewUrl` | `string` | Permalink to the public Goodreads review. |
| `likesCount` | `integer` | Number of likes displayed for the review. |
| `hasSpoiler` | `boolean` | Whether Goodreads marks the review as containing a spoiler. |

Live example from a bounded run:

```json
{
  "itemType": "book",
  "bookId": 3735293,
  "url": "https://www.goodreads.com/book/show/3735293-clean-code",
  "title": "Clean Code: A Handbook of Agile Software Craftsmanship",
  "titleComplete": "Clean Code: A Handbook of Agile Software Craftsmanship",
  "authors": [
    {
      "name": "Robert C. Martin",
      "url": "https://www.goodreads.com/author/show/45372.Robert_C_Martin"
    }
  ],
  "description": "Even bad code can function. But if code isn't clean, it can bring a development organization to its knees.",
  "genres": [
    "Programming",
    "Computer Science",
    "Technology",
    "Software"
  ],
  "coverUrl": "https://m.media-amazon.com/images/S/compressed.photo.goodreads.com/books/1436202607i/3735293.jpg",
  "averageRating": 4.35,
  "ratingsCount": 23742,
  "reviewsCount": 1494,
  "ratingHistogram": {
    "1": 159,
    "2": 447,
    "3": 2587,
    "4": 8317,
    "5": 12232
  },
  "reviewsByLanguage": [
    {
      "language": "en",
      "count": 1303
    },
    {
      "language": "es",
      "count": 46
    },
    {
      "language": "pt",
      "count": 28
    }
  ],
  "isbn": "0132350882",
  "isbn13": "9780132350884",
  "asin": "0132350882",
  "pages": 431,
  "publisher": "Prentice Hall",
  "language": "English",
  "format": "Paperback",
  "publishedAt": "2008-08-01",
  "originalPublishedAt": "2007-01-01",
  "originalTitle": "Clean Code: A Handbook of Agile Software Craftsmanship (Robert C. Martin Series)",
  "series": [
    {
      "title": "Robert C. Martin Series",
      "position": ""
    }
  ],
  "awards": [],
  "workId": 3779106,
  "scrapedAt": "2026-08-03T11:23:04Z"
}
```

### Pricing

**$0.004 per result** — pay only for book and review rows the run delivers, with **no start fee**. `maxItems` caps the combined output and the Actor also clamps delivery to the run's pay-per-event budget.

### Usage patterns

- **Start with exact identifiers** — Book URLs and ISBNs avoid search ambiguity. Text queries return at most the top five matches each.
- **Bound the first run** — Use a small `maxItems`; it counts book and review rows together and is also clamped to the run's pay-per-event budget.
- **Treat reviews as optional fan-out** — Keep `maxReviewsPerBook` small initially. Goodreads' legacy review pagination is best-effort; the Actor falls back to public review cards embedded in the book page.
- **Handle source blocking** — The managed Actor uses a shared cache that fetches direct first and falls back to residential egress after a source challenge. Supplying an Apify proxy configuration explicitly overrides that shared route; datacenter proxy addresses may themselves be challenged.

### Input reference

| field | type | default | what it does |
|---|---|---|---|
| `bookUrls` | `array` | `[]` | Goodreads book page URLs, e.g. https://www.goodreads.com/book/show/3735293-clean-code |
| `queries` | `array` | `[]` | Free-text searches (title, author, keyword). The top matching books are scraped. |
| `isbns` | `array` | `[]` | ISBN-10 or ISBN-13 codes. Each is resolved to the matching book page. |
| `includeReviews` | `boolean` | `true` | Collect public reader reviews for each book. |
| `maxReviewsPerBook` | `integer` | `10` | Cap on collected reviews per book. 0 disables review collection entirely. |
| `minRating` | `number` | `0` | Skip books with an average rating below this value (0 = no filtering). |
| `maxItems` | `integer` | `200` | Total dataset records cap (books + reviews). Default 200; set 0 for an unlimited run. |
| `delaySeconds` | `integer` | `2` | Politeness delay between HTTP requests. Higher values reduce the risk of rate-limiting. |
| `maxRequestsPerMinute` | `integer` | `20` | Hard rate limit across all requests. Lower values reduce the risk of IP blocking. |
| `proxyConfiguration` | `object` |  | Apify Proxy (optional). Defaults to off: requests come directly from the run's IP, which works for typical volumes. If Goodreads starts blocking you (HTTP 202/403), enable it and consider residential proxy groups. |

### FAQ

**Does this crawl shelves, lists, authors, or all Goodreads search pages?**

No. This Actor focuses on exact book detail and bounded discovery: direct book URLs, ISBNs, and the top five matches per text query. Use a broader catalog crawler when you need shelves, lists, author catalogs, or deep search pagination.

**How many reviews can it return?**

Up to `maxReviewsPerBook` for each accepted book, subject to the total `maxItems` cap. Deep review pagination uses a legacy Goodreads endpoint and is best-effort; when unavailable, the Actor falls back to visible public review cards from the book page.

**Why are some fields null or empty?**

Goodreads does not expose every field for every edition, and the richer histogram/language/series fields depend on page state that may be absent. The Actor returns observed values and does not invent missing data.

**What happens when Goodreads blocks a request?**

The managed Actor's shared fetch layer detects source challenges and retries through residential egress; cache hits avoid the source request altogether. If that service is unavailable, the Actor falls back to its own egress. An explicit proxy input always takes precedence. The client still rate-limits requests and backs off on 429/5xx responses.

**Is `maxItems` a book limit?**

No. It is the combined dataset-row limit: book rows plus review rows. With `maxItems: 1`, the Actor fetches and returns one book and does not make review requests.

### Integration

#### JavaScript

```javascript
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('hyperbach/goodreads-scraper').call({"bookUrls": ["https://www.goodreads.com/book/show/3735293-clean-code"], "includeReviews": false, "maxItems": 1});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

#### Python

```python
from apify_client import ApifyClient
client = ApifyClient('YOUR_TOKEN')
run = client.actor('hyperbach/goodreads-scraper').call(run_input={'bookUrls': ['https://www.goodreads.com/book/show/3735293-clean-code'], 'includeReviews': False, 'maxItems': 1})
items = client.dataset(run['defaultDatasetId']).list_items().items
```

#### CLI

```bash
apify call hyperbach/goodreads-scraper --input '{"bookUrls": ["https://www.goodreads.com/book/show/3735293-clean-code"], "includeReviews": false, "maxItems": 1}'
```

#### REST

```bash
curl -X POST "https://api.apify.com/v2/acts/hyperbach~goodreads-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H 'Content-Type: application/json' -d '{"bookUrls": ["https://www.goodreads.com/book/show/3735293-clean-code"], "includeReviews": false, "maxItems": 1}'
```

### Support

support@hyperbach.com

*This page is generated from `readme.toml`, the Actor schemas, and a live sample. Edit the sources and run `python gen_readme.py`; do not hand-edit README.md.*

# Actor input Schema

## `bookUrls` (type: `array`):

Goodreads book page URLs, e.g. https://www.goodreads.com/book/show/3735293-clean-code

## `queries` (type: `array`):

Free-text searches (title, author, keyword). The top matching books are scraped.

## `isbns` (type: `array`):

ISBN-10 or ISBN-13 codes. Each is resolved to the matching book page.

## `includeReviews` (type: `boolean`):

Collect public reader reviews for each book.

## `maxReviewsPerBook` (type: `integer`):

Cap on collected reviews per book. 0 disables review collection entirely.

## `minRating` (type: `number`):

Skip books with an average rating below this value (0 = no filtering).

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

Total dataset records cap (books + reviews). Default 200; set 0 for an unlimited run.

## `delaySeconds` (type: `integer`):

Politeness delay between HTTP requests. Higher values reduce the risk of rate-limiting.

## `maxRequestsPerMinute` (type: `integer`):

Hard rate limit across all requests. Lower values reduce the risk of IP blocking.

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

Apify Proxy (optional). Defaults to off: requests come directly from the run's IP, which works for typical volumes. If Goodreads starts blocking you (HTTP 202/403), enable it and consider residential proxy groups.

## Actor input object example

```json
{
  "bookUrls": [],
  "queries": [],
  "isbns": [],
  "includeReviews": true,
  "maxReviewsPerBook": 10,
  "minRating": 0,
  "maxItems": 200,
  "delaySeconds": 2,
  "maxRequestsPerMinute": 20,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

All scraped records (books and reviews) in the default dataset. The Overview table view shows the key book fields.

# 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 = {
    "bookUrls": [],
    "queries": [],
    "isbns": [],
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("hyperbach/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 = {
    "bookUrls": [],
    "queries": [],
    "isbns": [],
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("hyperbach/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 '{
  "bookUrls": [],
  "queries": [],
  "isbns": [],
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call hyperbach/goodreads-scraper --silent --output-dataset

```

## MCP server setup

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

```

## OpenAPI specification

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