# Goodreads Scraper - Books, Authors, Ratings, ISBN & Reviews (`makework36/goodreads-scraper`) Actor

Scrape Goodreads books, authors and lists. Title, ISBN, pages, format, language, rating, ratings count, reviews count, author. HTTP only, $5/1K.

- **URL**: https://apify.com/makework36/goodreads-scraper.md
- **Developed by:** [deusex machine](https://apify.com/makework36) (community)
- **Categories:** Lead generation, E-commerce, Other
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, NaN 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.

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

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
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.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use 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, Authors, Ratings, ISBN & Reviews

Scrape **Goodreads** — the world's largest book community with 150M members and 4 billion+ ratings — and extract complete book metadata, author profiles and curated book lists. **HTTP-only**, no browser, **$5 per 1,000 items** ($0.005 each).

> If you build an author platform, run a publishing house, sell book-discovery apps, analyze the literary market, write academic papers about reading trends, or train a recommendation model on book data, this Goodreads scraper turns the canonical book-ratings graph into a clean structured feed in seconds.

### Why use this Goodreads scraper

Goodreads is the **definitive book database** — every English-language book published since 2007 lives here, with millions of user ratings and reviews per major title. But Goodreads has no public API since 2020 (Amazon shut down the legacy API and never replaced it), and they actively block headless scraping.

This actor extracts data from the **canonical JSON-LD blocks** Goodreads ships on every book detail page. That means:

- ✅ **Stable selectors** — `<script type="application/ld+json">` is part of SEO and Goodreads cannot remove it without losing search engine ranking
- ✅ **Complete fields** — title, ISBN, ISBN-13, page count, format, language, description, cover, author(s) with URLs, average rating, ratings count, reviews count
- ✅ **No anti-bot encountered** on book, author and list pages
- ✅ **Fast** — typically 4–6 books per second per worker
- ✅ **Cheap** — $0.005 per record ($5 per 1,000), the lowest among book-database scrapers in the Apify Store

### What this Goodreads scraper extracts

#### Per book (`/book/show/...`)

| Field | Description | Example |
|---|---|---|
| `bookId` | Goodreads internal book ID | `16299` |
| `slug` | URL-safe title slug | `And_Then_There_Were_None` |
| `url` | Canonical book URL | `https://www.goodreads.com/book/show/16299...` |
| `title` | Title as displayed on Goodreads | `And Then There Were None` |
| `authors` | Array of `{name, url}` — supports multi-author books | `[{name: "Agatha Christie", url: "..."}]` |
| `isbn` | Goodreads-canonical ISBN | `9780312330873` |
| `numberOfPages` | Page count (integer) | `264` |
| `bookFormat` | Hardcover, Paperback, Kindle, Audible, etc | `Paperback` |
| `language` | Edition language | `English` |
| `description` | Marketing description (from OG tag) | `"First, there were ten—a curious assortment..."` |
| `coverImage` | High-resolution cover image URL | `https://m.media-amazon.com/images/...` |
| `rating` | Average rating (1.00–5.00) | `4.27` |
| `ratingsCount` | Total number of ratings | `1,662,794` |
| `reviewsCount` | Total written reviews | `86,031` |
| `scrapedAt` | ISO 8601 UTC timestamp | `2026-05-18T20:34:12+00:00` |

#### Per author (`/author/show/...`)

| Field | Example |
|---|---|
| `authorId` | `123715` |
| `name` | `Agatha Christie` |
| `born` / `died` | `September 15, 1890` / `January 12, 1976` |
| `website` / `twitter` | author's social presence |
| `genres` | `["Mystery", "Fiction", "Crime"]` |
| `avgRating` / `ratingsCount` | aggregate across all the author's books |
| `image` | author photo URL |
| `books` | up to 30 visible works `[{title, url}]` from the profile page |
| `booksCount` | length of `books` |

#### Per list (`/list/show/...`)

| Field | Example |
|---|---|
| `listId` / `slug` | `1` / `Best_Books_Ever` |
| `title` | `Best Books Ever` |
| `description` | Marketing copy of the list |
| `books` | `[{bookId, title, url}]` array (up to 100 by default) |
| `booksCount` | length of `books` |

If you enable `enrichBooksFromLists: true`, every book referenced in the list is also fetched individually and emitted as a separate `type: "book"` record with full metadata (ISBN, page count, rating, etc).

### Use cases for this Goodreads data API

#### 📚 Author platforms, book promotion, indie publishing

Tools like Reedsy, BookBub, BookFunnel and indie publishing platforms need fresh rating/review metrics for every book they promote. Schedule this scraper weekly to refresh your reviews-engine.

#### 🛒 Book discovery / recommendation apps

Train collaborative-filtering models on Goodreads ratings or build a "Books like X" feature by pulling all books from canonical lists ("Best Mystery", "Best of 2025") and ranking by `rating × ratingsCount`.

#### 🎓 Academic literary research

Researchers studying genre evolution, demographic reading patterns or literary canon formation use Goodreads as primary corpus. Bulk-extract one book per year per genre and feed into your analysis pipeline.

#### 📊 Publishing house competitive intelligence

Knowing the rating curve of every Stephen King vs every Dean Koontz vs every Lee Child release lets editors and marketing teams price advances, plan releases and pick mid-list bets.

#### 🤖 LLM training data + RAG pipelines

Build a book-aware AI assistant that knows ISBN, page count, average rating and category for every published title — and can recommend books based on user preferences with grounded data.

#### ✍️ Newsletter / content marketing

Subscriptions like "5-Bullet Book Brief" use book data to build reading lists for paid subscribers. This scraper feeds your CMS with consistent metadata.

#### 📈 Financial / market analysis

Hedge funds tracking the "audiobook revolution" or "Kindle Unlimited churn" use Goodreads engagement metrics (ratings velocity, review counts) as leading indicators for traditional publisher earnings.

### How to use this Goodreads scraper

Three input modes — combine them freely in a single run.

#### Mode 1: Book URLs

Pass canonical book URLs to extract one full record per book.

```json
{
  "bookUrls": [
    "https://www.goodreads.com/book/show/16299.And_Then_There_Were_None",
    "https://www.goodreads.com/book/show/40961427-educated"
  ]
}
````

#### Mode 2: Author URLs

Pass author profile URLs to extract author identity plus visible book list.

```json
{
  "authorUrls": [
    "https://www.goodreads.com/author/show/123715.Agatha_Christie",
    "https://www.goodreads.com/author/show/16667.Isaac_Asimov"
  ]
}
```

#### Mode 3: List URLs (with optional enrichment)

Lists are curated collections — "Best Books Ever", "Pulitzer Prize Winners", "Best Science Fiction of the Decade", etc. Each list yields ~100 books per page.

```json
{
  "listUrls": [
    "https://www.goodreads.com/list/show/1.Best_Books_Ever",
    "https://www.goodreads.com/list/show/2.Best_Books_of_the_Decade__2010s"
  ],
  "enrichBooksFromLists": true,
  "maxBooksPerList": 50
}
```

When `enrichBooksFromLists: true`, each list emits one `type: "list"` record plus one `type: "book"` record per enriched book. If you only need the URL references, leave it off and you'll get a much cheaper run.

### Step-by-step tutorial — your first Goodreads run in 2 minutes

1. **Click "Try for free"** on this actor's Apify Store page. New users get $5 in credit.
2. **Paste a starter input** for the most popular Goodreads list:
   ```json
   {
     "listUrls": ["https://www.goodreads.com/list/show/1.Best_Books_Ever"],
     "enrichBooksFromLists": true,
     "maxBooksPerList": 20,
     "maxTotalItems": 25
   }
   ```
3. **Click "Start"** and watch the live log.
4. **Download your dataset** as JSON, CSV, Excel, RSS or HTML.

You'll get one list record + 20 fully enriched book records (ISBN, ratings, page count) in ~30 seconds.

### Performance and cost

- **HTTP only** — no Playwright, no proxy, runs on minimal Apify compute units.
- **4–6 items per second** sustained, single worker, 256 MB memory.
- Pricing: **$0.005 per item** + $0.00005 per actor start.

#### Pricing scenarios

| Workload | Items | Cost |
|---|---|---|
| Try the actor | 5 books | $0.025 |
| One Apify free $5 credit | ~1,000 items | $5.00 |
| Full enrich of a 100-book list | 101 items | $0.51 |
| Top 10 lists × 100 books × enrich | 1,010 items | $5.05 |
| Author + their 30 visible books × 50 authors | 1,550 items | $7.75 |

### Output example (single book, JSON)

```json
{
  "type": "book",
  "bookId": "16299",
  "slug": "And_Then_There_Were_None",
  "url": "https://www.goodreads.com/book/show/16299.And_Then_There_Were_None",
  "title": "And Then There Were None",
  "authors": [
    {"name": "Agatha Christie", "url": "https://www.goodreads.com/author/show/123715.Agatha_Christie"}
  ],
  "isbn": "9780312330873",
  "numberOfPages": 264,
  "bookFormat": "Paperback",
  "language": "English",
  "description": "First, there were ten—a curious assortment of strangers...",
  "coverImage": "https://m.media-amazon.com/images/S/compressed.photo.goodreads.com/books/1638425885i/16299.jpg",
  "rating": 4.27,
  "ratingsCount": 1662794,
  "reviewsCount": 86031,
  "bestRating": 5.0,
  "worstRating": 1.0,
  "scrapedAt": "2026-05-18T20:34:12+00:00"
}
```

### How this Goodreads scraper compares

| Approach | Pros | Cons |
|---|---|---|
| **This actor** | Stable JSON-LD selectors, $5/1K, no proxy, 3 modes | No full review text extraction in v1 |
| Goodreads legacy API | Was free | **Shut down by Amazon in late 2020** — no longer accessible |
| Open Library API | Free | Sparse coverage, missing ratings, no Goodreads-specific metrics |
| Manual scraping with BeautifulSoup | Total flexibility | Selectors break with every Goodreads UI update; you maintain forever |
| Hiring a freelancer | Custom output | $300–$1,000 one-off; not maintained |

### How to call this Goodreads scraper from your code

#### Python

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_API_TOKEN")
run = client.actor("makework36/goodreads-scraper").call(run_input={
    "listUrls": ["https://www.goodreads.com/list/show/1.Best_Books_Ever"],
    "enrichBooksFromLists": True,
    "maxBooksPerList": 100,
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    if item["type"] == "book":
        print(item["title"], item["rating"], item["isbn"])
```

#### Node.js

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

const client = new ApifyClient({ token: 'YOUR_API_TOKEN' });
const run = await client.actor('makework36/goodreads-scraper').call({
  bookUrls: ['https://www.goodreads.com/book/show/16299.And_Then_There_Were_None'],
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach(b => console.log(b.title, b.rating, b.numberOfPages));
```

### Frequently Asked Questions about scraping Goodreads

#### Is scraping Goodreads legal?

This actor extracts metadata Goodreads itself renders publicly to every visitor on book, author and list pages, and that is embedded as JSON-LD specifically to help search engines and aggregators consume the same data. You are still responsible for how you use it — respect copyright (descriptions and cover art remain Amazon/publisher property), Goodreads' Terms of Service for derivative-product creation, and applicable data protection laws.

#### Why not use the official Goodreads API?

Amazon shut down the legacy Goodreads API in late 2020 and never published a replacement. JSON-LD scraping is currently the only programmatic path to fresh Goodreads data.

#### Does the scraper extract full review text?

Not in v1. Aggregate counts (`ratingsCount`, `reviewsCount`) plus average rating come from JSON-LD. Individual review text is on a separately rendered page; an enhancement is planned in v1.1.

#### How current is the rating data?

Live — every run hits Goodreads directly and reflects the rating as displayed at request time. There is no internal cache.

#### What if a book has no ISBN listed?

Goodreads stores the ISBN of the **default edition** for each work. Some older or self-published books have no canonical ISBN — the field returns `null`. ISBN-13 can usually be derived from the canonical edition URL slug.

#### Can I scrape every book by an author?

Pass the author URL, and the actor returns the **visible books on their profile page** (typically 30 entries). For deeper coverage, scrape the author's "Books" tab URL pattern: `https://www.goodreads.com/author/list/{authorId}` (coming in v1.1).

#### Does Goodreads block bots?

The book, author and list endpoints do not actively challenge bot traffic as of this release. The `/search?q=...` endpoint **does** return HTTP 202 to non-cookied requests, which is why this actor does not offer keyword search mode. Use lists or direct URLs instead.

#### How do I find list URLs?

Visit `https://www.goodreads.com/list` and browse by genre, decade, or theme. Copy the URL of any list. Popular starters: `/list/show/1.Best_Books_Ever`, `/list/show/2.Best_Books_of_the_Decade__2010s`, `/list/show/43.Best_Science_Fiction_Fantasy_Books`.

#### Can I schedule this scraper?

Yes. Use Apify's built-in scheduler to refresh your dataset daily, weekly or monthly, and push results directly to Google Sheets, BigQuery, Postgres or your CMS via Apify integrations.

#### Will my dataset have duplicates?

The actor deduplicates by URL within a single run. Across runs, build a primary key on `bookId` / `authorId` / `listId` to merge cleanly.

#### How accurate is the page count?

Page count reflects the default edition. A Kindle edition may show a different page count than the paperback. Use `bookFormat` to disambiguate.

#### Is there a free trial?

Apify gives every new user $5 in platform credit, enough to extract ~1,000 Goodreads items with this actor.

#### Can I use this data to build a recommendation engine?

Absolutely. Many recommender-system projects start with a Goodreads books CSV. Combining `bookId`, `authors`, `numberOfPages`, `language`, `rating`, `ratingsCount` and `description` gives you a rich feature matrix for collaborative filtering or content-based recommendations.

### 🔗 Other actors by makework36

Building a content, publishing or recommendation product? You'll also want these:

- [Shopify Products Scraper](https://apify.com/makework36/shopify-products-scraper) — full Shopify catalog: title, SKU, price, variants, inventory
- [IndiaMART Suppliers Scraper](https://apify.com/makework36/indiamart-suppliers-scraper) — India B2B suppliers with phone, GST verified & ratings
- [Email Finder Scraper](https://apify.com/makework36/email-finder-scraper) — verified business emails by domain
- [Reddit SaaS Leads Scraper](https://apify.com/makework36/reddit-leads-saas) — startup pain points & buyers
- [Trustpilot Reviews Scraper](https://apify.com/makework36/trustpilot-reviews-scraper) — customer reviews & ratings
- [YouTube Shorts Scraper](https://apify.com/makework36/youtube-shorts-scraper) — short-form video creator data

See all [actors by makework36](https://apify.com/makework36) on the Apify Store.

### Roadmap

- **v1.1**: full review text extraction per book, deeper author bibliography via `/author/list/{id}` pagination.
- **v1.2**: book genre/shelf hierarchy extraction.
- **v1.3**: ISBN-to-book reverse lookup via `/search/?q={isbn}&search_type=isbn`.
- **v2**: similar-books graph extraction (for recommendation pipelines).

### Disclaimer

This actor extracts public book and author metadata that Goodreads itself renders to every visitor and embeds as JSON-LD for SEO consumption. You are responsible for how you store, transform and redistribute the data. Cover images and book descriptions remain the property of their original publishers. This actor is not affiliated with Goodreads or Amazon.

> 🙏 **Ran this Goodreads scraper successfully?** [Leaving a review](https://apify.com/makework36/goodreads-scraper/reviews) helps the Apify algorithm surface this actor to other book platforms and publishing teams. Much appreciated.

# Actor input Schema

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

Goodreads book URLs (e.g. 'https://www.goodreads.com/book/show/16299.And\_Then\_There\_Were\_None'). Each URL yields one book record with full metadata.

## `authorUrls` (type: `array`):

Goodreads author URLs (e.g. 'https://www.goodreads.com/author/show/16667.Isaac\_Asimov'). Returns author profile plus list of visible books.

## `listUrls` (type: `array`):

Goodreads list URLs (e.g. 'https://www.goodreads.com/list/show/1.Best\_Books\_Ever'). Each list yields ~100 book references; enable enrichBooksFromLists to fetch full metadata for each.

## `enrichBooksFromLists` (type: `boolean`):

If true, each book discovered inside a list URL is also fetched individually to obtain JSON-LD metadata (ISBN, page count, rating, etc). Slower and adds one record per book.

## `maxBooksPerList` (type: `integer`):

Cap of books to keep from each list URL.

## `maxTotalItems` (type: `integer`):

Hard cap across all books + authors + lists combined.

## Actor input object example

```json
{
  "bookUrls": [
    "https://www.goodreads.com/book/show/16299.And_Then_There_Were_None"
  ],
  "authorUrls": [],
  "listUrls": [],
  "enrichBooksFromLists": false,
  "maxBooksPerList": 100,
  "maxTotalItems": 500
}
```

# Actor output Schema

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

No description

# 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/16299.And_Then_There_Were_None"
    ],
    "authorUrls": [],
    "listUrls": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("makework36/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": ["https://www.goodreads.com/book/show/16299.And_Then_There_Were_None"],
    "authorUrls": [],
    "listUrls": [],
}

# Run the Actor and wait for it to finish
run = client.actor("makework36/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": [
    "https://www.goodreads.com/book/show/16299.And_Then_There_Were_None"
  ],
  "authorUrls": [],
  "listUrls": []
}' |
apify call makework36/goodreads-scraper --silent --output-dataset

```

## MCP server setup

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

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Goodreads Scraper - Books, Authors, Ratings, ISBN & Reviews",
        "description": "Scrape Goodreads books, authors and lists. Title, ISBN, pages, format, language, rating, ratings count, reviews count, author. HTTP only, $5/1K.",
        "version": "0.1",
        "x-build-id": "DiUsoMGaZvUr8nCZW"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/makework36~goodreads-scraper/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-makework36-goodreads-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/makework36~goodreads-scraper/runs": {
            "post": {
                "operationId": "runs-sync-makework36-goodreads-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/makework36~goodreads-scraper/run-sync": {
            "post": {
                "operationId": "run-sync-makework36-goodreads-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "properties": {
                    "bookUrls": {
                        "title": "Book URLs",
                        "type": "array",
                        "description": "Goodreads book URLs (e.g. 'https://www.goodreads.com/book/show/16299.And_Then_There_Were_None'). Each URL yields one book record with full metadata.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "authorUrls": {
                        "title": "Author URLs",
                        "type": "array",
                        "description": "Goodreads author URLs (e.g. 'https://www.goodreads.com/author/show/16667.Isaac_Asimov'). Returns author profile plus list of visible books.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "listUrls": {
                        "title": "List URLs",
                        "type": "array",
                        "description": "Goodreads list URLs (e.g. 'https://www.goodreads.com/list/show/1.Best_Books_Ever'). Each list yields ~100 book references; enable enrichBooksFromLists to fetch full metadata for each.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "enrichBooksFromLists": {
                        "title": "Enrich list books with full metadata",
                        "type": "boolean",
                        "description": "If true, each book discovered inside a list URL is also fetched individually to obtain JSON-LD metadata (ISBN, page count, rating, etc). Slower and adds one record per book.",
                        "default": false
                    },
                    "maxBooksPerList": {
                        "title": "Max books per list",
                        "minimum": 1,
                        "maximum": 1000,
                        "type": "integer",
                        "description": "Cap of books to keep from each list URL.",
                        "default": 100
                    },
                    "maxTotalItems": {
                        "title": "Max total items (whole run)",
                        "minimum": 1,
                        "maximum": 10000,
                        "type": "integer",
                        "description": "Hard cap across all books + authors + lists combined.",
                        "default": 500
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
