# Goodreads Review Scraper (`fastdataapi/goodreads-review-scraper`) Actor

Goodreads Review Scraper collects public reviews from Goodreads book URLs or IDs: star rating, review text, reviewer profile, likes, comments, shelves and timestamps. Filter by language and sort order. No login or cookies. Export as JSON, CSV or Excel, or use the API.

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

## Pricing

from $1.21 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
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 Review Scraper — Ratings, Text and Reviewer Profiles

Goodreads Review Scraper pulls every public review from a Goodreads book page — star rating, full review text, reviewer profile, likes, comments, shelving, and both epoch and ISO-8601 timestamps. Reviews can be filtered by language and by edition scope before they reach your dataset. Every response is **structured JSON**, ready to pass to an LLM, load into a spreadsheet, or index for search. No Goodreads account or login is needed — only what any visitor already sees is read. Start with one book URL, add the rest of your list.

![Goodreads Review Scraper hero: every review, every rating, as JSON](https://api.apify.com/v2/key-value-stores/Lg9lp51Levfro6SrV/records/j-goodreads-reviews-hero.webp)

***

### What data can I extract with Goodreads Review Scraper?

Using this Goodreads reviews API, you will be able to extract the following Goodreads review data:

| 🆔 Review ID | ⭐ Star rating | 💬 Review text | 🚩 Spoiler flag |
| --- | --- | --- | --- |
| 👤 Reviewer profile (nested) | 👍 Like count | 💬 Comment count | 📅 Created date (epoch + ISO) |
| 📅 Updated date (epoch + ISO) | 📅 Last revision date (epoch + ISO) | 📚 Shelving & tags (if attached) | 🎯 Recommended for (if provided) |
| 📦 Pre-release source (rare, if applicable) | 🔗 Book URL | 🏷️ Record type label | 👁️ Viewer-liked flag (always null, anonymous) |

Pair star ratings with like and comment counts, and it works equally well as a Goodreads ratings scraper focused only on the numbers rather than full review text.

***

### How to use Goodreads Review Scraper

Anyone studying how readers actually respond to a book — a publisher, an AI/RAG engineer, or a market researcher — gets one typed JSON row per review, filtered by language and edition scope before it ever reaches the dataset, from a single run.

**Copy to your AI assistant:**

```text
Goodreads Review Scraper (Apify Actor ID: fastdataapi/goodreads-review-scraper, https://apify.com/fastdataapi/goodreads-review-scraper) is an Apify Actor that returns public Goodreads book reviews as structured JSON: star rating, full review text, reviewer name and profile link, like count, comment count, and both epoch and ISO-8601 timestamps. Use it whenever someone needs Goodreads review data without cookies, a login, or a browser - tracking how a title's reviews trend after launch, feeding review text into a sentiment model as grounded context, or building a versioned review dataset for research. Inputs: bookUrls (array, required, one or more Goodreads book URLs or bare numeric book IDs), maxItems (integer, default 20, minimum 1, maximum 100000, applies per book on paid plans; free-plan accounts are capped at 20 reviews per run in total across every book, and 200 per month), sortBy (string, one of "popular"|"newest"|"oldest", default "popular"), languageCode (string, "all" or one of 19 language codes, default "all"), Review_Edition (string, "ALL"|"only_this_book", default "ALL"), proxyConfiguration (object, proxy editor, used automatically if a request is blocked). A book that can't be resolved is skipped with a warning and the run continues with the rest of the list; the run only fails if zero reviews were collected across every requested book.
```

#### Goodreads Review Scraper MCP Server: use these Actors from ChatGPT, Claude or Cursor

Add the Apify MCP server and your AI assistant can find and call this Actor on its own — no glue code, no scraping logic in your prompts.

```bash
claude mcp add --transport http apify "https://mcp.apify.com?tools=fastdataapi/goodreads-review-scraper"
```

#### Step by step

1. [Create a free Apify account](https://console.apify.com/sign-up).
2. Open [**Goodreads Review Scraper**](https://apify.com/fastdataapi/goodreads-review-scraper) in the Apify Store.
3. Enter the Goodreads book URL or bare numeric book ID.
4. Click **Start**.
5. Download or stream your results in JSON, CSV or Excel.

#### How to scale Goodreads Review Scraper to bulk

Pass an array of book URLs or IDs in `bookUrls` — up to 4 books are processed concurrently within a run. On paid plans, `maxItems` applies separately to each book rather than to the run as a whole, so 50 in `maxItems` with 3 books can return up to 150 rows. Free-plan accounts are capped at 20 reviews per run in total across every book in the list, and 200 per month, no matter how high `maxItems` is set. A book that can't be reached after its own retries is skipped with a warning rather than aborting the run; the run only fails if zero reviews were collected across every requested book.

Used this way, it works as a bulk Goodreads review scraper: queue as many book URLs as you need and let per-book retries and concurrency handle the rest without babysitting individual runs.

***

### ✨ Features

Goodreads Review Scraper fetches clean, structured reviews from a Goodreads book page ⚡ with built-in language and edition-scope filtering. It's ideal for review-mining, sentiment analysis, publisher/author reception tracking, and building a versioned research corpus.

As a Goodreads book review extractor built for structured output rather than raw HTML, every row also carries the kind of reviewer detail a Goodreads reviewer data scraper needs — name, profile link, and engagement counts — nested under a single `creator` field.

It extracts, per review:

- **Review text and star rating** – the reviewer's own words and their 1–5 rating
- **Reviewer profile** – a nested `creator` object with the reviewer's name, profile link, and engagement counts
- **Engagement counts** – like count and comment count per review
- **Shelving and tags** – the shelf a reviewer filed the book under, plus any attached tags, when Goodreads has that data
- **Three timestamp fields** – created, updated, and last revision, each as both raw epoch milliseconds and an ISO-8601 string

⚡ Reviews are pushed to the dataset the moment each one is parsed, not buffered until a book or the run finishes, so results are visible in the Apify Console while a run is still going.

***

### Use cases 💡

- **Publisher or author** — track rating and like-count trends before and after a launch or cover redesign.
- **AI/RAG engineer** — feed review text and rating into a sentiment model as grounded, structured context.
- **Market researcher** — compare reader reception across several competing titles in one run.
- **Academic/data team** — build a versioned, reproducible review dataset keyed by review ID.
- **Reader-insight analyst** — run it as a Goodreads review scraper by language to study reception in one specific market.

Building an AI agent? [Connect Goodreads Review Scraper over MCP](#goodreads-review-scraper-mcp-server-use-these-actors-from-chatgpt-claude-or-cursor) and let it call the actor on its own.

***

### Why choose this actor?

1. **Every review streamed as it's found.** A recorded run requesting 3 reviews for one book returned 3 review rows, each with every documented field populated — pushed to the dataset as each one was parsed, not buffered until the book finished.
2. **19 documented fields per review, nested reviewer and shelving included.** Every row carries its own rating, text, and spoiler flag, plus a nested reviewer profile and — when Goodreads has the data — a nested shelf and tag object, with three timestamp fields each given in both epoch and ISO-8601 form.
3. **3 real query controls, applied server-side.** `sortBy`, `languageCode` (20 languages or all), and `Review_Edition` (whole work vs. one edition) are all applied by Goodreads before a row reaches your dataset — not filtered afterward.

***

### ⬇️ Input

![Goodreads Review Scraper input: book URLs, max reviews, sort order and language](https://api.apify.com/v2/key-value-stores/Lg9lp51Levfro6SrV/records/j-goodreads-reviews-input.webp)

The Actor accepts one or more Goodreads book URLs or bare numeric book IDs in `bookUrls` — the only required field.

| Parameter | Required | Type | Description | Example value |
| --- | --- | --- | --- | --- |
| `bookUrls` | **Yes** | array | One or more Goodreads book links. A bare numeric ID (e.g. `26032825`) is accepted and rewritten to a full book URL automatically. | `["https://www.goodreads.com/book/show/26032825"]` |
| `maxItems` | No | integer (default `20`, minimum `1`, maximum `100000`) | How many reviews to collect for each book. Applies per book on paid plans; free-plan accounts are capped at 20 reviews per run in total (all books together) and 200 per month, regardless of this value. | `50` |
| `sortBy` | No | string enum (default `"popular"`) | Review order: `popular` (most helpful first), `newest`, or `oldest`. | `"newest"` |
| `languageCode` | No | string enum (default `"all"`) | `all` for no filter, or one of 19 language codes. | `"es"` |
| `Review_Edition` | No | string enum (default `"ALL"`) | `ALL` for reviews across the whole work, or `only_this_book` for just the edition the URL points to. | `"only_this_book"` |
| `proxyConfiguration` | No | object (proxy editor) | Apify Proxy configuration. The Actor starts with a direct connection and only switches to a proxy if a request is blocked. | `{"useApifyProxy": true}` |

#### Example input

```json
{
  "bookUrls": [
    "https://www.goodreads.com/book/show/26032825",
    "18143977"
  ],
  "maxItems": 50,
  "sortBy": "newest",
  "languageCode": "en",
  "Review_Edition": "ALL",
  "proxyConfiguration": { "useApifyProxy": true }
}
```

#### Free plan limits

Free Apify accounts get up to **20 reviews per run** (every book in `bookUrls` combined, not 20 per book) and **200 reviews per month** in total, no matter how high `maxItems` is set. The run stops cleanly once either cap is hit, so you keep whatever was already collected and saved. Paid Apify plans have no limit — `maxItems` then applies per book exactly as described above.

***

### ⬆️ Output

![Goodreads Review Scraper output: one row per review with rating, text, likes and date](https://api.apify.com/v2/key-value-stores/Lg9lp51Levfro6SrV/records/j-goodreads-reviews-output.webp)

Every review is one typed, normalized JSON item with the same field set on every run, streamed to the dataset as each review is found. Results export as JSON, CSV, Excel, XML, or RSS from the Apify Console or API.

That makes it a straightforward Goodreads review data export pipeline — pick Goodreads reviews to CSV for a spreadsheet-ready file, or keep JSON for direct ingestion into code.

#### Example output

```json
{
  "bookUrl": "https://www.goodreads.com/book/show/26032825",
  "__typename": "Review",
  "id": "kca://review:goodreads/amzn1.gr.review:goodreads.v1.sdrC-ZRjzY6QMCANZHeBhQ",
  "creator": {
    "id": 5477612,
    "name": "Emily May",
    "webUrl": "https://www.goodreads.com/user/show/5477612-emily-may",
    "imageUrlSquare": "https://i.gr-assets.com/images/S/compressed.photo.goodreads.com/users/1583425448i/5477612._UY200_CR0,0,200,200_.jpg",
    "isAuthor": false,
    "followersCount": 323630,
    "textReviewsCount": 2305,
    "viewerRelationshipStatus": null,
    "contributor": null
  },
  "recommendFor": null,
  "updatedAt": 1452890265000,
  "updatedAtIso": "2016-01-15T20:37:45Z",
  "createdAt": 1452890265000,
  "createdAtIso": "2016-01-15T20:37:45Z",
  "spoilerStatus": false,
  "lastRevisionAt": 1452890265000,
  "lastRevisionAtIso": "2016-01-15T20:37:45Z",
  "text": "A gorgeous, cruel fairy tale that earns every bit of its darkness.",
  "rating": 5,
  "preReleaseBookSource": null,
  "shelving": {
    "shelf": {
      "name": "read",
      "displayName": "Read",
      "sortOrder": 2,
      "webUrl": "https://www.goodreads.com/review/list/5477612?shelf=read"
    },
    "taggings": [
      { "tag": { "name": "fantasy", "webUrl": "https://www.goodreads.com/review/list/5477612?shelf=fantasy" } }
    ]
  },
  "likeCount": 3120,
  "viewerHasLiked": null,
  "commentCount": 84
}
```

`viewerHasLiked` and `creator.viewerRelationshipStatus` are carried through on every row but are always `null` for this Actor's logged-out requests — real fields, just not populated outside a signed-in session. Fields Goodreads does not return for a given review — a missing `creator`, an unset `lastRevisionAt`, an empty `shelving` — come back as `null`, never a guessed value.

***

### How does it work?

The Actor reads each book's own public reviews page — the same one any visitor's browser loads — and resolves what it needs fresh on every run rather than relying on a value that can go stale. Requests go out through rotating proxies: each one starts on a direct connection and only escalates to a proxy, and eventually a residential one, if Goodreads blocks it, locking onto that tier for the rest of that book once escalation is needed so a block mid-page never causes a duplicate row. It returns only publicly visible reviews — no login, account, or cookie is used — and the output schema stays stable even as Goodreads changes its own page.

***

### How do you integrate Goodreads Review Scraper into your stack?

Goodreads Review Scraper works with what you already use: a REST API callable from any language, MCP for AI agents, and no-code automation tools.

#### No-code tools (n8n, Make, LangChain)

In **n8n**, use the Apify node's "Run an Actor" action, or the HTTP Request node pointed at the Actor's run-sync endpoint with your Apify token. In **Make**, use the Apify app's "Run an Actor" module followed by "Get Dataset Items" to pull the rows into your scenario. In **LangChain**, install `langchain-apify` and call `ApifyActorsTool()` once the Actor is in your account to let an agent run it directly.

#### MCP integration for AI agents

Add the Apify MCP server and your agent can search for, add, and call this Actor on its own:

```bash
claude mcp add --transport http apify "https://mcp.apify.com?tools=fastdataapi/goodreads-review-scraper"
```

This registers the Apify MCP server as a tool your agent can call directly. Compatible with Claude Code, Cursor, and Windsurf.

***

### Is it legal to scrape Goodreads reviews?

Scraping publicly accessible web data is generally lawful: courts in the U.S. have found that accessing data a website makes available without a login is not a CFAA violation (*hiQ Labs, Inc. v. LinkedIn Corp.*, 9th Cir., 2019). Goodreads Review Scraper returns only what any visitor can see on a book's reviews page while logged out — it does not access private accounts or gated content. Because each review includes reviewer personal data (a name, a profile URL, a follower count), processing it at scale may still bring it within GDPR or CCPA obligations for storage, retention, and data-subject rights. Consult legal counsel if your use case involves bulk storage of personal data.

***

### Frequently asked questions

#### What fields does Goodreads Review Scraper return?

The top fields are star rating, review text, reviewer profile, like count, and comment count — see [the data fields section](#what-data-can-i-extract-with-goodreads-review-scraper) for the full list.

#### Does Goodreads Review Scraper require a Goodreads account or login?

No. Only public review data is extracted — no credentials are needed, and the Actor never opens a session.

#### What happens if a book is private or doesn't exist?

A book that can't be resolved is skipped for that run with a clear warning, and the run continues with the rest of the requested books; the run only fails if zero reviews were collected across every book.

#### Can I scrape reviews for multiple books at once?

Yes — pass an array of book URLs or IDs in `bookUrls`; up to 4 books are processed concurrently, and on paid plans `maxItems` applies separately to each one. Free-plan accounts are capped at 20 reviews per run in total across every book, and 200 per month — see [Free plan limits](#free-plan-limits).

#### Does Goodreads Review Scraper work with Claude, ChatGPT, and other AI agent tools?

Yes — it's reachable through the Apify MCP server, which lets an AI assistant find and call it directly. Any agent that can call an HTTP endpoint can also use the REST API directly.

#### Does Goodreads Review Scraper return data in a format LLMs can use directly?

Yes. Typed, normalized JSON with consistent field names, no HTML parsing and no selectors. Pass review text and rating straight to an LLM, index them into a vector store, or feed them to an agent tool with no transformation step.

#### What happens when Goodreads changes its layout or updates its anti-bot system?

The maintaining team updates the Actor when Goodreads changes its page or defenses — your calls keep returning the same JSON schema with no changes on your end.

#### Can I use Goodreads Review Scraper without managing proxies or Goodreads credentials?

Yes. No Goodreads account, login, or API key is required, and proxy handling is automatic — a run starts on a direct connection and only reaches for a proxy if Goodreads blocks it.

#### Which Goodreads review fields work best for AI training data and RAG indexing?

For RAG: review text, paired with the reviewer's name and the shelf/tag data for category context. For training data: star rating, like count, and comment count. All fields are typed primitives — no normalization needed.

# Changelog

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

# Actor input Schema

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

Paste one or more Goodreads book links. **Tip:** you can paste just the number from the URL (e.g. `26032825`) — we'll fix the link for you.

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

How many reviews to collect for each book in your list. (Example: 50 here × 3 books = up to 150 rows.) Free plan: up to 20 reviews per run (all books together) and 200 per month; paid plans have no limit.

## `sortBy` (type: `string`):

**Popular** — most helpful first · **Newest** — newest first · **Oldest** — oldest first

## `languageCode` (type: `string`):

**All languages** — no filter · or choose one language and reviews are filtered to match.

## `Review_Edition` (type: `string`):

**All editions** — reviews for the whole work · **This book only** — reviews for this edition.

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

Apify Proxy configuration. Leave at the default — the Actor starts with a direct connection and only reaches for a proxy if a request is blocked.

## Actor input object example

```json
{
  "bookUrls": [
    "https://www.goodreads.com/book/show/26032825"
  ],
  "maxItems": 20,
  "sortBy": "popular",
  "languageCode": "all",
  "Review_Edition": "ALL",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

All scraped reviews in the Actor's default dataset.

# 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": [
        "https://www.goodreads.com/book/show/26032825"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("fastdataapi/goodreads-review-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": ["https://www.goodreads.com/book/show/26032825"],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("fastdataapi/goodreads-review-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 '{
  "bookUrls": [
    "https://www.goodreads.com/book/show/26032825"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call fastdataapi/goodreads-review-scraper --silent --output-dataset

```

## MCP server setup

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