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

Goodreads scraper and API: search books or paste URLs to export title, author, rating, ISBN, pages, genres, series, description and full-text reviews to JSON/CSV/Excel.

- **URL**: https://apify.com/innolabs/goodreads-scraper.md
- **Developed by:** [Tristan Leroux](https://apify.com/innolabs) (community)
- **Categories:** Education, Other
- **Security:** Limited permissions
- **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/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 Scraper — Books, Authors & Reviews

Export Goodreads books by keyword, URL or ID: title, author, rating, ratings count, ISBN, pages, publisher, series, genres, description and full-text reader reviews. No Goodreads account, login or API key needed. Download the results as JSON, CSV, Excel or HTML, or pull them through the Apify API.

### What does Goodreads Scraper do?

Goodreads Scraper turns public Goodreads pages into a clean, structured dataset:

- **Search** Goodreads with any keyword (title, author, ISBN) and expand every result into a full book record.
- **Look up books directly** by Goodreads book URL or numeric book ID.
- **Collect reviews** (optional): reviewer name and profile, star rating, date, likes and the complete review text (not the "…more" teaser).
- Duplicate books across queries are stored and billed only once.

### Use cases

- **Publishers and booksellers**: enrich a catalogue with ratings, genres, series and descriptions.
- **Market researchers**: compare ratings and review volume across titles, authors or genres.
- **Data scientists**: build recommendation or sentiment datasets from ratings and full-text reviews.
- **Authors and marketers**: monitor reader feedback on your own and competing titles.
- **AI / LLM builders**: ground chatbots and agents in up-to-date book metadata and reader opinions.

### How much does it cost?

You pay only for results stored in your dataset (pay-per-event). Duplicates are free.

| Event | What you pay for | Free plan | Paid plans (from) |
| --- | --- | --- | --- |
| `book` | One book record stored | $2.00 per 1,000 | $1.50 per 1,000 (Gold+) |
| `review` | One review stored | $0.50 per 1,000 | $0.30 per 1,000 (Gold+) |

Examples (Free plan prices):

- **100 books, no reviews** → 100 × $0.002 = **$0.20**.
- **50 books with 30 reviews each** → 50 × $0.002 + 1,500 × $0.0005 = $0.10 + $0.75 = **$0.85**.

A tiny platform start fee ($0.00005 per run) also applies. The Apify Free plan includes $5 monthly credit, enough for about 2,500 books.

### How to use Goodreads Scraper

**In Apify Console**

1. Open Goodreads Scraper and click **Try for free**.
2. Enter one or more **Search queries** (e.g. `dune`), or paste **Book URLs** / **Book IDs**.
3. Optionally turn on **Include reviews** and set limits.
4. Click **Start** and wait for the run to finish.
5. Download the dataset as JSON, CSV, Excel or HTML, or connect it to your tools.

**Via API**

1. Get your API token in Apify Console → Settings → Integrations.
2. Call the actor with the same input JSON (see [Integrations and API](#integrations-and-api)).

### Input

Provide at least one of `queries`, `bookUrls` or `bookIds`. Books you ask for directly are processed before search results.

| Field | Type | Default | Description |
| --- | --- | --- | --- |
| `queries` | array of strings | — | Goodreads search terms, e.g. `"dune"`. |
| `bookUrls` | array of strings | — | Book URLs, e.g. `https://www.goodreads.com/book/show/44767458-dune`. |
| `bookIds` | array of strings | — | Numeric book IDs, e.g. `"44767458"`. |
| `includeReviews` | boolean | `false` | Also store text reviews for every book. |
| `maxReviewsPerBook` | integer | `30` | Reviews per book (0–500). |
| `maxBooksPerQuery` | integer | `10` | Search results expanded per query (1–50). |
| `maxItems` | integer | `1000` | Hard cap on stored items (books + reviews). |
| `proxyConfiguration` | object | Apify Proxy | Proxy settings; residential recommended for large runs. |
| `debug` | boolean | `false` | Verbose logs. |

Example:

```json
{
  "queries": ["dune", "project hail mary"],
  "bookUrls": ["https://www.goodreads.com/book/show/186074"],
  "includeReviews": true,
  "maxReviewsPerBook": 20,
  "maxBooksPerQuery": 5,
  "proxyConfiguration": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"] }
}
```

### Output

Each dataset item has a `type`: `book` or `review`. Book record from a real run with `{"queries": ["dune"]}`:

```json
{
  "type": "book",
  "bookId": "44767458",
  "workId": "3634639",
  "title": "Dune",
  "author": "Frank Herbert",
  "authorId": "58",
  "rating": 4.29,
  "ratingsCount": 1724548,
  "reviewsCount": 90556,
  "pages": 658,
  "publishedAt": "2019-10-01",
  "publisher": "Ace",
  "language": "English",
  "isbn13": "9780593099322",
  "format": "Hardcover",
  "series": { "name": "Dune", "position": "1", "url": "https://www.goodreads.com/series/45935-dune" },
  "genres": ["Science Fiction", "Fiction", "Fantasy", "Classics", "Audiobook", "Science Fiction Fantasy", "Space Opera", "Novels", "Adventure", "Book Club"],
  "description": "Set on the desert planet Arrakis, Dune is the story of the boy Paul Atreides, heir to a noble family tasked with ruling an inhospitable world …",
  "coverUrl": "https://m.media-amazon.com/images/S/compressed.photo.goodreads.com/books/1555447414i/44767458.jpg",
  "url": "https://www.goodreads.com/book/show/44767458-dune",
  "fetchedAt": "2026-11-02T13:41:21Z"
}
```

Review record (with `includeReviews: true`):

```json
{
  "type": "review",
  "bookId": "44767458",
  "reviewId": "156120010",
  "reviewer": "Rajat Ubhaykar",
  "reviewerId": "4686064",
  "reviewerUrl": "https://www.goodreads.com/user/show/4686064-rajat-ubhaykar",
  "rating": 5,
  "text": "In my head, the purpose of this review is very clear. It is to convince YOU to read this book. …",
  "date": "2011-03-22",
  "likes": 3081,
  "url": "https://www.goodreads.com/review/show/156120010"
}
```

Field notes:

- `genres` is the full Goodreads genre list in page order.
- Fields an edition does not have (publisher, ISBN, language, series, pages) are `null` or `[]`; the book is still returned.
- `text` is the complete review. Rating-only entries without text are not returned.

### Integrations and API

**Apify API (curl)** — run synchronously and get the items back:

```bash
curl -X POST "https://api.apify.com/v2/acts/<username>~goodreads-scraper/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"queries": ["dune"], "maxBooksPerQuery": 5}'
```

**Python (`apify-client`)**:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_TOKEN>")
run = client.actor("<username>/goodreads-scraper").call(run_input={"queries": ["dune"], "includeReviews": True})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["type"], item.get("title") or item.get("reviewer"))
```

- **Make, Zapier, n8n**: use the Apify app/node to start this actor and pass the dataset to Google Sheets, Airtable, Slack, a database or a CRM.
- **Scheduling**: create a Schedule in Apify Console to refresh ratings and reviews daily or weekly.
- **Webhooks**: get notified when a run finishes.
- **MCP / AI agents**: connect Claude, Cursor or any MCP client to the Apify MCP server at https://mcp.apify.com and call Goodreads Scraper as a tool.

### FAQ

#### Is it legal to scrape Goodreads?

The actor reads only publicly visible pages and does not log in. Scraping public data is generally allowed, but reviews can contain personal data (reviewer names). Make sure your use complies with GDPR and other applicable laws, and consult a lawyer if unsure.

#### Do I need a proxy?

Yes, for anything beyond small runs a **residential proxy is recommended**. Goodreads rate-limits datacenter and cloud IPs. In the **Proxy configuration** field select Apify Proxy and the **RESIDENTIAL** group, or pass `"proxyConfiguration": {"useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"]}` via API. The actor retries blocked requests with a fresh IP.

#### How many books and reviews can I get?

Each query returns up to about 20 search results (`maxBooksPerQuery` up to 50 caps this), and up to 500 reviews per book. Use more queries or direct book URLs/IDs to cover more titles. `maxItems` caps the total.

#### Can I get only new reviews or books?

Within one run, duplicates are never stored or billed twice. To track changes over time, schedule the actor and compare datasets by `bookId` / `reviewId` (for example in Make, n8n or your database).

#### What happens if a book fails?

Failures are isolated: one broken book or review page is logged and skipped, and the rest of the run continues. You are not charged for items that were not stored.

#### Do I need a Goodreads account or API key?

No. Goodreads closed its public API; this actor works without any account, cookie or key.

### Limits

- Only public data: private shelves, friends-only content and login-gated pages are not available.
- Search reads the first results page per query (about 20 books).
- Reviews are text reviews only, up to 500 per book; rating-only entries are skipped.
- Author profiles and full edition lists are not scraped (`authorId` and `workId` are provided).
- Large runs without a residential proxy may be slowed down by Goodreads rate limiting.

# Changelog

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

# Actor input Schema

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

Goodreads search terms (title, author, ISBN…). Every result (up to Max books per query) is expanded into a full book record. Provide at least one of Search queries, Book URLs or Book IDs.

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

Goodreads book URLs, e.g. https://www.goodreads.com/book/show/186074. Slugged URLs are accepted.

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

Numeric Goodreads book IDs, e.g. 186074. At least one of queries, bookUrls or bookIds is required.

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

Fetch text reviews for every processed book and store them as separate dataset items.

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

Upper bound on reviews stored per book. 0 disables reviews even when Include reviews is on.

## `maxBooksPerQuery` (type: `integer`):

How many search results to expand per query. Lower it to cut run time and proxy traffic.

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

Hard cap on dataset items (books + reviews). The run stops cleanly when reached.

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

Goodreads rate-limits datacenter/cloud IPs. A residential Apify Proxy is recommended for larger runs.

## `debug` (type: `boolean`):

Verbose logs for development.

## Actor input object example

```json
{
  "queries": [
    "dune"
  ],
  "includeReviews": false,
  "maxReviewsPerBook": 30,
  "maxBooksPerQuery": 10,
  "maxItems": 1000,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "debug": false
}
```

# Actor output Schema

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

All items produced by the run, one JSON object per record.

# 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 = {
    "queries": [
        "dune"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("innolabs/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 = { "queries": ["dune"] }

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

```

## MCP server setup

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