# Goodreads Reviews and Book Details Scraper (`automation-lab/goodreads-reviews-book-details-scraper`) Actor

Extract publicly visible Goodreads reader reviews with book, author, ISBN, ratings, review text, likes, tags, and source links.

- **URL**: https://apify.com/automation-lab/goodreads-reviews-book-details-scraper.md
- **Developed by:** [Stas Persiianenko](https://apify.com/automation-lab) (community)
- **Categories:** Marketing
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
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 Reviews and Book Details Scraper

Extract publicly visible Goodreads reader reviews together with the book details needed to analyze them.

Give the Actor one or more Goodreads book URLs or numeric book IDs. It returns one normalized dataset row per visible review, with the book title, author, ISBN, aggregate rating, rating counts, reviewer, rating, date, text, likes, tags, and source links.

### What does Goodreads Reviews and Book Details Scraper do?

The Actor reads the server-rendered public Goodreads book page. It combines structured book metadata with the review cards visible on that page.

Use it to:

- collect reader feedback for selected titles;
- compare reviews across competing books;
- monitor the visible review set on a schedule;
- export review text for sentiment or theme analysis;
- enrich a title list with Goodreads rating context;
- build research datasets without copying and cleaning pages by hand.

The Actor is URL/ID driven. It does not search Goodreads by title or resolve ISBNs to book pages.

### Who is this Goodreads review scraper for?

#### Publishers and editors

Compare how readers describe books in a category and identify recurring praise, complaints, and expectations.

#### Authors and book marketers

Track visible reader response to a title or a small competitor set. Schedule runs and compare datasets over time.

#### Recommendation teams

Combine review text and ratings with internal catalog data for qualitative recommendation features.

#### Literary and market researchers

Export normalized public records for text analysis, coding, sampling, or reproducible research.

#### Developers and data teams

Call the Actor through the Apify API and send its default dataset to a database, spreadsheet, webhook, or analysis pipeline.

### Why use this Actor?

- **Review-level output:** each row is an individual reader review, not only a book search result.
- **Book context included:** every review repeats the fields needed for standalone CSV or JSON analysis.
- **No Goodreads login:** the supported workflow uses anonymous public book pages.
- **Full rendered text:** the Actor extracts the review text present in the server response, including expanded content rendered in the page HTML.
- **Stable deduplication:** Goodreads review IDs are preferred; a deterministic hash is used when no ID is available.
- **Low-overhead HTTP extraction:** no browser or paid residential proxy is enabled.
- **Safe limits:** each book produces at most the 30 review cards publicly rendered on its initial page.

### What Goodreads data can you extract?

| Field | Description |
| --- | --- |
| `bookId` | Goodreads numeric book identifier |
| `bookTitle` | Displayed book title |
| `bookAuthor` | Primary author |
| `authorUrl` | Public Goodreads author profile |
| `isbn` | ISBN of the displayed edition, when available |
| `description` | Visible book description |
| `aggregateRating` | Average Goodreads book rating |
| `ratingsCount` | Displayed total rating count |
| `reviewsCount` | Displayed total review count |
| `bookUrl` | Canonical fetched book URL |
| `reviewId` | Goodreads review ID or fallback identity hash |
| `reviewerName` | Public reviewer display name |
| `reviewerProfileUrl` | Public reviewer profile URL |
| `rating` | Reader rating from 1 to 5, when present |
| `reviewDate` | Date text displayed by Goodreads |
| `reviewText` | Rendered public review text |
| `likesCount` | Displayed review like count |
| `tags` | Public shelf tags attached to the review |
| `reviewUrl` | Direct review URL, when available |
| `fetchedAt` | ISO timestamp for the page fetch |

Fields that Goodreads omits are returned as `null` or an empty array where appropriate.

### How to scrape Goodreads reviews

1. Open the Actor in Apify Console.
2. Add one or more public `goodreads.com/book/show/...` URLs, numeric book IDs, or both.
3. Choose `maxReviewsPerBook` from 1 to 30.
4. Click **Start**.
5. Open the **Dataset** tab when the run succeeds.
6. Download JSON, CSV, Excel, XML, or another Apify dataset format.
7. For recurring monitoring, connect the task to an Apify schedule.

A quick input is:

```json
{
  "startUrls": [
    { "url": "https://www.goodreads.com/book/show/4671.The_Great_Gatsby" }
  ],
  "maxReviewsPerBook": 10
}
```

### Input parameters

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `startUrls` | array | sample book URL | Public Goodreads book URLs |
| `bookIds` | string array | `[]` | Numeric Goodreads book IDs |
| `maxReviewsPerBook` | integer | `30` | Maximum visible reviews saved per book, from 1 to 30 |

At least one URL or ID is required. Duplicate targets are fetched once.

Valid URLs begin with `https://www.goodreads.com/book/show/` followed by a numeric book ID. Locale, title slug, and query-string variations are normalized to the canonical book path.

### Output example

The default dataset contains one row per review. This shortened, anonymized example reflects the current output shape:

```json
{
  "bookId": "4671",
  "bookTitle": "The Great Gatsby",
  "bookAuthor": "F. Scott Fitzgerald",
  "authorUrl": "https://www.goodreads.com/author/show/3190.F_Scott_Fitzgerald",
  "isbn": "9780743273565",
  "aggregateRating": 3.93,
  "ratingsCount": 6094211,
  "reviewsCount": 137534,
  "bookUrl": "https://www.goodreads.com/book/show/4671",
  "reviewId": "101057684",
  "reviewerName": "Example Reader",
  "reviewerProfileUrl": "https://www.goodreads.com/user/show/123456-example-reader",
  "rating": 5,
  "reviewDate": "March 17, 2023",
  "reviewText": "An anonymized example of the rendered review text.",
  "likesCount": 2590,
  "tags": ["classics"],
  "reviewUrl": "https://www.goodreads.com/review/show/101057684",
  "fetchedAt": "2025-01-15T12:00:00.000Z"
}
```

Counts and page content change over time. Do not treat the example as a current Goodreads snapshot.

### How much does it cost to extract Goodreads reader reviews?

The Actor uses pay per event:

| Event | Price |
| --- | --- |
| Actor start | $0.005 per run |
| Review saved | tiered; $0.000035714 per review at the BRONZE tier |

At the BRONZE tier, approximate charges are:

- 10 reviews from one book: **$0.00536** including the start event;
- 30 reviews from one book: **$0.00607** including the start event;
- 150 reviews across five books: **$0.01036** including the start event.

Higher-volume tiers reduce the review event price. Failed, duplicate, or empty records are not charged as review items. Platform compute is included in pay-per-event billing.

### Limits and expected behavior

- The Actor extracts only the review cards server-rendered on the initial public book page.
- Goodreads currently exposes up to 30 such cards per book; the Actor does not promise all historical reviews.
- `maxReviewsPerBook` cannot exceed 30.
- The Actor does not search Goodreads or resolve a title, author, or ISBN.
- The displayed edition determines ISBN and description fields.
- Deleted or private reviews cannot be extracted.
- Review order is controlled by Goodreads and may change between runs.
- Public page markup can change and may require Actor maintenance.
- Requests are sequential and conservative; very large book lists take longer.

If a valid book has no publicly rendered review cards, the Actor logs a warning and saves no row for that book.

### Tips for reliable Goodreads datasets

- Store the stable `reviewId` as your database key.
- Use `bookId + reviewId` when joining runs across several books.
- Keep `fetchedAt` to distinguish scheduled snapshots.
- Compare review IDs between runs to identify additions or removals.
- Start with 5–10 reviews when testing an integration.
- Split very large target lists into scheduled tasks if you need isolated failure handling.
- Preserve the source URLs when publishing analysis so results remain auditable.

### Automate review monitoring

Create an Apify task with a fixed title list and connect it to a daily, weekly, or monthly schedule.

A typical monitoring workflow is:

1. run the Actor on a selected title set;
2. export the default dataset;
3. upsert rows by `reviewId`;
4. compare the current IDs with the previous snapshot;
5. send new review text to a sentiment or topic classifier;
6. notify a marketing or editorial team through a webhook.

Apify integrations can send run results to Google Sheets, Make, Zapier, Slack, or a custom webhook.

### API usage

Use the same input contract through cURL or the official JavaScript and Python clients.

#### cURL

Replace `YOUR_TOKEN` with an Apify API token:

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/automation-lab~goodreads-reviews-book-details-scraper/runs?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"bookIds":["4671"],"maxReviewsPerBook":10}'
```

After the run finishes, read `defaultDatasetId` from the run object and request its items endpoint.

#### JavaScript

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor(
  'automation-lab/goodreads-reviews-book-details-scraper'
).call({
  bookIds: ['4671', '1885'],
  maxReviewsPerBook: 10,
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(`Downloaded ${items.length} public reviews`);
```

#### Python

```python
import os
from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor(
    "automation-lab/goodreads-reviews-book-details-scraper"
).call(run_input={
    "startUrls": [
        {"url": "https://www.goodreads.com/book/show/4671.The_Great_Gatsby"}
    ],
    "maxReviewsPerBook": 10,
})

items = client.dataset(run["defaultDatasetId"]).list_items().items
print(f"Downloaded {len(items)} public reviews")
```

### Connect through Apify MCP

Add the Actor to Claude Code:

```bash
claude mcp add --transport http apify \
  "https://mcp.apify.com?tools=automation-lab/goodreads-reviews-book-details-scraper"
```

#### Claude Desktop, Cursor, and VS Code setup

Claude Desktop, Cursor, VS Code, and other MCP clients can use this equivalent JSON configuration:

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com?tools=automation-lab/goodreads-reviews-book-details-scraper"
    }
  }
}
```

Example prompts:

- “Extract the first 10 visible reviews for Goodreads book ID 4671.”
- “Compare the public reader reviews for book IDs 4671 and 1885.”
- “Save the output, then summarize repeated positive and negative themes.”

### Responsible and legal use

This Actor accesses publicly visible pages. You are responsible for complying with Goodreads terms, applicable laws, contractual restrictions, and the rights of reviewers.

Review text and profile data can be personal data. Collect only what your use case requires, apply an appropriate retention policy, secure exported datasets, honor deletion or access obligations, and avoid profiling or harmful automated decisions about individuals.

Do not use the Actor to bypass authentication, access private content, harass reviewers, republish copyrighted review text without a valid basis, or create spam.

### Troubleshooting

#### The run says the URL is unsupported

Use a public `goodreads.com/book/show/...` URL containing a numeric book ID. Search pages, author pages, lists, and non-Goodreads domains are rejected.

#### A valid book produced zero rows

Open the public page and confirm it currently displays review cards without a login. Books with no visible reader reviews legitimately produce no rows. If reviews are visible but output is empty, share the run URL with support so the parser can be checked.

#### The run received HTTP 429 or 5xx

The Actor retries transient responses automatically. Wait before retrying a persistently rate-limited target and reduce the number of books per run.

#### Why are there only 30 reviews?

The product intentionally extracts the initial server-rendered review set. It does not claim full historical pagination.

### Related Automation Lab Actors

- [Goodreads Scraper](https://apify.com/automation-lab/goodreads-scraper) — search Goodreads by keyword and export book-level search results when you do not already have book URLs.
- [Amazon Reviews Scraper](https://apify.com/automation-lab/amazon-reviews-scraper) — collect product review data from Amazon for cross-channel publishing research.

### FAQ

#### Does this Actor require a Goodreads account?

No. The supported workflow reads anonymous public Goodreads book pages.

#### Can I enter an ISBN?

No. Enter a Goodreads URL or numeric Goodreads book ID. ISBN resolution is not part of the current product.

#### Does one output row contain both book and review data?

Yes. Book context is repeated on every review row so CSV exports and downstream records remain self-contained.

#### Are duplicate URLs charged twice?

No. Targets normalize to their numeric Goodreads book ID and duplicate book targets are fetched once. Review rows are also deduplicated before charging.

#### Can I schedule this Actor?

Yes. Save the input as an Apify task, add a schedule, and use `reviewId` plus `fetchedAt` to compare snapshots.

#### Is every field always present?

No. Goodreads can omit an ISBN, rating, profile URL, tags, likes, or other fields. The dataset schema permits nullable values.

# Actor input Schema

## `startUrls` (type: `array`):

Public Goodreads URLs in the form https://www.goodreads.com/book/show/BOOK\_ID. URL query parameters and title slugs are accepted.

## `bookIds` (type: `array`):

Numeric Goodreads book IDs. You can combine these with book URLs; duplicate books are fetched once.

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

Maximum number of publicly rendered reader reviews to save from each supplied book page.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://www.goodreads.com/book/show/4671.The_Great_Gatsby"
    }
  ],
  "bookIds": [],
  "maxReviewsPerBook": 10
}
```

# Actor output Schema

## `overview` (type: `string`):

Default dataset containing one normalized row per publicly rendered reader review.

# 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 = {
    "startUrls": [
        {
            "url": "https://www.goodreads.com/book/show/4671.The_Great_Gatsby"
        }
    ],
    "maxReviewsPerBook": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation-lab/goodreads-reviews-book-details-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 = {
    "startUrls": [{ "url": "https://www.goodreads.com/book/show/4671.The_Great_Gatsby" }],
    "maxReviewsPerBook": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("automation-lab/goodreads-reviews-book-details-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 '{
  "startUrls": [
    {
      "url": "https://www.goodreads.com/book/show/4671.The_Great_Gatsby"
    }
  ],
  "maxReviewsPerBook": 10
}' |
apify call automation-lab/goodreads-reviews-book-details-scraper --silent --output-dataset

```

## MCP server setup

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

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/acts/9kz7noJqN03q8V6qj/builds/F75YcmDz57zkgFBso/openapi.json
