# Goodreads Book Review Export (`automation-lab/goodreads-book-review-export`) Actor

Export the initial publicly embedded Goodreads book reviews with text, ratings, timestamps and book provenance. A bounded sample for recurring reader-feedback research, not exhaustive or newest reviews.

- **URL**: https://apify.com/automation-lab/goodreads-book-review-export.md
- **Developed by:** [Automation Lab](https://apify.com/automation-lab) (community)
- **Categories:** Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.91 / 1,000 item extracteds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Goodreads Book Review Export

Export **Goodreads book reviews** from the initial public sample embedded in supplied book pages. Get plain-text review content, star ratings, source timestamps and book provenance for reader-feedback research. This is a bounded snapshot, not an exhaustive or newest-review export.

### Who is it for?

Authors, publishers, book-club researchers and analysts who already know which Goodreads books they want to examine. Compare themes in a visible review sample, prepare a feedback worksheet or build a repeatable research pipeline without copying reviews by hand.

### Why use it?

- One flat dataset row per distinct written review.
- Stable opaque source review IDs support deduplication between snapshots.
- Creation and update dates are separate from the extraction date.
- Book and reviewed-edition provenance help avoid confusing editions.
- No Goodreads login, user cookies or API key required.
- Explicit coverage markers prevent mistaking the sample for all reader opinions.

### Getting started

1. Open the Actor and enter one or more Goodreads book URLs.
2. Set a global maximum review count, such as 30.
3. Start the run and wait for completion.
4. Inspect the default dataset and export JSON, CSV, Excel or another Apify-supported format.
5. Keep the run status alongside your export: a failed run can contain partial rows.

Example input:

```json
{
  "startUrls": [
    {"url": "https://www.goodreads.com/book/show/2767052-the-hunger-games"}
  ],
  "maxItems": 20
}
```

### Input parameters

| Field | Default | Behavior |
| --- | --- | --- |
| `startUrls` | Required | 1–100 public HTTPS Goodreads `/book/show/<id>` URLs, as URL objects. |
| `maxItems` | 20 | Global distinct written-review cap, integer 1–3000. Zero/unlimited is not supported. |

Books are processed serially in input order. URL query strings and fragments are discarded; repeated IDs are fetched once. Only `goodreads.com` and `www.goodreads.com` are accepted. Search, profile, shelf and list URLs are unsupported. The Actor does not interpret sort/filter parameters on a URL. Entry options other than `url` are unsupported: non-GET methods or nonempty payload, headers and userData are rejected.

The cap applies after empty-text removal and review-ID deduplication. It does not request more pages to fill a target. If the first book fills the cap, later books are not fetched. A platform charge limit can stop output earlier.

### What data is returned?

| Field | Meaning |
| --- | --- |
| `reviewId` | Opaque Goodreads review identifier; not necessarily a numeric review-page ID. |
| `text` | Embedded written review with HTML removed; may contain spoilers. |
| `rating` | Source rating from 0–5; zero means unrated. |
| `createdAt`, `updatedAt` | Source timestamps normalized to UTC, or null. |
| `spoiler` | Source review-level spoiler flag. False does not guarantee spoiler-free text. |
| `likeCount`, `commentCount` | Embedded engagement counts, or null; comments are not collected. |
| `requestedUrl` | Normalized supplied URL. |
| `bookId`, `bookTitle`, `bookAuthor`, `bookUrl` | Resolved book-page provenance; author may be null. |
| `reviewedEditionId` | The review's shelving edition ID when embedded; may differ from the supplied edition. |
| `sampleSize` | Initial embedded edge count before filtering and limits, not the book's total review count. |
| `coverage` | Always `initial_embedded_sample`. |
| `scrapedAt` | UTC extraction timestamp. |

All 17 output fields are described in the dataset schema. Reviewer names, profiles, emails and follower data are not collected. Review bodies themselves are public user-generated content and may mention people.

### Output example

A shortened, anonymized example of the actual row shape:

```json
{
  "reviewId": "kca://review:goodreads/example",
  "text": "An engaging story with memorable characters.",
  "rating": 4,
  "createdAt": "2025-01-15T12:00:00.000Z",
  "updatedAt": "2025-01-16T12:00:00.000Z",
  "spoiler": false,
  "likeCount": 5,
  "commentCount": 1,
  "requestedUrl": "https://www.goodreads.com/book/show/123",
  "bookId": "123",
  "bookTitle": "Example Book",
  "bookAuthor": "Example Author",
  "bookUrl": "https://www.goodreads.com/book/show/123",
  "reviewedEditionId": "456",
  "sampleSize": 30,
  "coverage": "initial_embedded_sample",
  "scrapedAt": "2025-01-17T12:00:00.000Z"
}
```

### Coverage and limits

This Actor reads one public book page per supplied ID. Goodreads often embeds about 30 reviews, but the number can vary. The source chooses the sample; it is not random, exhaustive, newest-first or guaranteed to change between scheduled runs. Review text is only what the page embeds; no separate expansion or history retrieval is performed.

Goodreads can show reviews across editions of the same work. `bookId` identifies the resolved page and `reviewedEditionId` identifies the review's edition when available. Do not treat every row as an edition-specific opinion. No book discovery, rating filters, language filters, pagination, profile enrichment or sentiment scoring is provided.

Repeated runs are snapshots, not monitoring alerts. Compare `reviewId` and `updatedAt` downstream, and expect repeated rows across runs. A valid book with no embedded written reviews may return zero rows. A challenge or missing structured state fails instead of silently returning a verified empty result.

### How much does it cost to export Goodreads book reviews?

Pricing is pay per event: a one-time start event plus one item event for each written review delivered. Duplicate reviews and empty text do not produce an item event. The configuration is $0.005 per run plus $0.001748 (FREE), $0.00152 (BRONZE), $0.0011856 (SILVER), or $0.000912 (GOLD/PLATINUM/DIAMOND) per delivered review. At BRONZE, estimated totals including the start event are $0.00652 for 1 review, $0.0506 for 30 reviews, and $0.157 for 100 reviews across enough supplied books. A 100-review example is not a promise of 100 reviews from one book. Spend tiers follow qualifying aggregate monthly Apify Store spend, not volume in this Actor alone. The pricing panel is authoritative for an active run; estimates are not guaranteed invoices.

### Integrations

- Export CSV or Excel for a reader-feedback worksheet.
- Feed JSON rows to your own sentiment or topic-analysis pipeline; preserve the coverage marker.
- Schedule periodic snapshots in Apify and join them by `reviewId` in a database. This does not guarantee discovery of new reviews.
- Use Apify webhooks, Make or Zapier to move completed datasets into a downstream workflow. Respect each service's limits and your rights to use review content.

### API usage

Use an Apify API token in an authorization header. Starting a run is a paid operation.

```bash
curl -X POST 'https://api.apify.com/v2/acts/automation-lab~goodreads-book-review-export/runs' \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"startUrls":[{"url":"https://www.goodreads.com/book/show/2767052"}],"maxItems":30}'
```

JavaScript:

```javascript
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('automation-lab/goodreads-book-review-export').call({
  startUrls: [{ url: 'https://www.goodreads.com/book/show/2767052' }], maxItems: 30
});
const { items } = await client.dataset(run.defaultDatasetId).listItems({ limit: 30 });
console.log(items);
```

Python:

```python
import os
from apify_client import ApifyClient
client = ApifyClient(os.environ['APIFY_TOKEN'])
run = client.actor('automation-lab/goodreads-book-review-export').call(run_input={
    'startUrls': [{'url': 'https://www.goodreads.com/book/show/2767052'}], 'maxItems': 30
})
print(client.dataset(run['defaultDatasetId']).list_items(limit=30).items)
```

### MCP usage

Configure the Actor-specific hosted endpoint; availability requires access to the Actor:

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

Claude Desktop, Cursor, and VS Code configuration:

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

Discover actual tool names and input schemas with scoped `tools/list`. Actor selection also exposes run/data helper tools; it is not exactly one tool. Example prompt: “Export the initial Goodreads review sample for this book, with maxItems 30; do not describe it as newest or exhaustive.”

For an authorized invocation, start once and retain the returned run ID and storage IDs: those are metadata, not rows. A `call-actor` wait can be at most 45 seconds (default 30). If still running, check that same run with bounded backoff (2, 4, 8 seconds, capped at 10) and a 120-second total deadline. Set the client timeout above the server wait plus transport margin. On timeout, recover the known run rather than starting another; if the ID is unknown, report uncertainty. At the deadline report pending and stop. Failed-run rows are partial.

After success, read `get-dataset-items` with explicit offset, limit and fields. A reasonable consumer budget is 20 source rows per page, at most 100 rows and 64 KiB serialized UTF-8 content admitted to model context, whichever comes first. Enforce bytes host-side before injecting content; disclose omitted oversized rows. If the client cannot intercept responses, do not claim a hard byte limit. Track source offsets, report truncation and continuation offset, and leave full exports outside model context. These consumer limits do not reduce the Actor's collection cap. Tool discovery transport bytes do not prove model token savings.

### Troubleshooting and failure behavior

**Why fewer than maxItems?** The initial sample may be smaller, duplicate review IDs may span supplied editions, or the platform charge limit may stop output. More reviews require a different product, not a larger cap alone.

**Why did the run fail?** Invalid URLs, inaccessible books, redirects, source blocks, missing embedded state and exhausted transient errors fail the run. Earlier rows remain partial. Fix the input or retry later when appropriate; do not assume a failed run is a complete export.

**Does it use proxies?** No. It uses direct HTTP with at most three transient attempts per book, conservative serial requests and no automatic premium fallback. Stable blocks are not blindly retried.

### Legality and diagnostics

Use only public pages and content you are entitled to process. Respect Goodreads terms, intellectual property and privacy requirements. Public availability does not automatically grant permission to republish reviews or use them for model training. This Actor is not affiliated with Goodreads.

The Actor uses no AI model and makes no paid third-party data API calls. Run datasets and logs remain in your Apify storage under your account retention settings until deleted through Apify; there is no separate cross-run database or persistent cache in this Actor. You control dataset deletion. For help, use the Actor's Apify Issues tab.

Failed operations send sanitized diagnostic input, exceptions and Actor/build/run IDs to our private GlitchTip service for repair. Secret fields and URL queries are removed; reports are retained for 30 days. Never put credentials or private user content in book URLs.

### Frequently asked questions

**Can I get the newest reviews?** No. This product exports only the initially embedded public sample; it has no newest-first mode.

**Can I request all reviews?** No. Raising the global cap only allows more initial samples across supplied books, not deeper pagination.

**Can I compare editions?** Keep the resolved page ID and reviewed-edition ID separate. Goodreads can share reviews across editions; a missing reviewed-edition ID remains null.

### Related workflows

This is a standalone reader-feedback sample export. Use Apify dataset exports and integrations for downstream analysis; no other Actor is required. It does not generate book recommendations, infer audience demographics or make representativeness claims.

# Changelog

This Actor's version history is a separate document: https://apify.com/automation-lab/goodreads-book-review-export/changelog.md

# Actor input Schema

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

Supply 1–100 public HTTPS Goodreads /book/show/<id> URLs. URLs are normalized by book ID; query parameters and fragments are discarded. Duplicate IDs are fetched once, in input order. Reviews may span editions of the same work. No search, profile or list URLs. Each entry supports only url; non-GET methods or nonempty payload, headers and userData are rejected.

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

Global cap on distinct nonempty-text reviews across all books, processed in input order. Default 20; allowed 1–3000. Zero/unlimited is not supported. Each book supplies only its initial embedded sample (often 30), so the result may be smaller. No additional pages are requested. The platform charge limit can stop output earlier.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://www.goodreads.com/book/show/2767052-the-hunger-games"
    }
  ],
  "maxItems": 20
}
```

# Actor output Schema

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

Typed review sample records; no exhaustive coverage claim.

# 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/2767052-the-hunger-games"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation-lab/goodreads-book-review-export").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/2767052-the-hunger-games" }] }

# Run the Actor and wait for it to finish
run = client.actor("automation-lab/goodreads-book-review-export").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 '{
  "startUrls": [
    {
      "url": "https://www.goodreads.com/book/show/2767052-the-hunger-games"
    }
  ]
}' |
apify call automation-lab/goodreads-book-review-export --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automation-lab/goodreads-book-review-export"
        }
    }
}
```

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/bx5Oc9rjeUAW9DL9i/builds/fq2mfzm1XSDXeDXaC/openapi.json
