# Goodreads Book Scraper — Author Bibliography & Series (`api-empire/goodreads-book-scraper`) Actor

Goodreads Book Scraper: Extract book titles, authors, ratings, reviews, publication dates, genres, series details, and author bibliographies. Discover complete reading lists, series order, related books, and structured data for book research and literary analysis.

- **URL**: https://apify.com/api-empire/goodreads-book-scraper.md
- **Developed by:** [API Empire](https://apify.com/api-empire) (community)
- **Categories:** Automation, Lead generation, E-commerce
- **Stats:** 6 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.99 / 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.

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

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

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

### Goodreads Book Scraper — Books, Author Bios & Series as JSON

Goodreads Book Scraper expands a Goodreads author into their full bibliography, or a keyword into a list of books, and returns typed JSON — no HTML, no selectors. Every book row carries ratings, publication data, the author profile (bio, followers, total works) and series placement, and every author target closes with one summary row rolling up titles collected, average rating, and best/worst rated title. Read on for the exact input parameters, every output field, and how the retry and enrichment logic behave.

### What is Goodreads Book Scraper?

Goodreads Book Scraper is an Apify Actor that turns a Goodreads author name, author link, or plain book search term into structured book data. It reads Goodreads' own listing pages and public book-data endpoint directly — no Goodreads account or login is required. Two lanes run independently in the same job: an **author lane** that walks an author's published titles and adds one rollup row per author, and a **keyword lane** that returns plain search results.

- Expand an author into their full bibliography
- Enrich every title with the author's profile (bio, followers, total works)
- Mark each title's series and its placement inside that series
- Close each author with one summary row (average rating, top/lowest/most-rated title, publication span, series covered)
- Run plain keyword or search-URL lookups in the same job
- Export as JSON, CSV, Excel or XML, or pull results straight from the Apify API

### What data does Goodreads Book Scraper collect?

Every run can return up to four kinds of rows, depending on which lanes and toggles are used.

| Data Type | Key Fields | JSON Field Names |
| --- | --- | --- |
| Books | title, listed author, rating, ratings count, published year, editions, cover, URL | `title`, `author`, `rating`, `ratingValue`, `ratingsCount`, `ratingsCountValue`, `published`, `publishedYear`, `editions`, `url`, `coverUrl`, `bookId` |
| Author profile (per book) | resolved author name, id, page URL, photo, bio, followers, total works, contributor role | `authorId`, `authorName`, `authorUrl`, `authorProfileImageUrl`, `authorBio`, `authorFollowersCount`, `authorWorksCount`, `authorRole`, `contributors` |
| Series placement (per book) | is it part of a series, series title, URL, placement number, how many series it's in, full series list | `isPartOfSeries`, `seriesTitle`, `seriesUrl`, `seriesPlacement`, `seriesCount`, `allSeries` |
| Author summary (one per author target) | titles collected, average rating, total ratings, top/lowest/most-rated title, publication span, series covered | `booksCollected`, `avgRatingOfCollectedBooks`, `totalRatingsOfCollectedBooks`, `topRatedTitle`, `topRatedRating`, `lowestRatedTitle`, `lowestRatedRating`, `mostRatedTitle`, `mostRatedRatingsCount`, `earliestPublishedYear`, `latestPublishedYear`, `distinctSeriesCount`, `seriesTitles` |

### How does Goodreads Book Scraper differ from the official Goodreads API?

**Why not build this yourself?** Goodreads is not a realistic target for a "just call the official API" approach. Goodreads [stopped issuing new developer API keys on December 8, 2020](https://www.goodreads.com/topic/show/21788520-api-deprecation) and has not reopened public developer access since — so there is no current official API a new integration can register for.

Building a replacement yourself means maintaining an HTML parser against a page Goodreads can restyle at any time, handling the listing-page interstitials Goodreads serves on some requests instead of the real results, running your own proxy pool to avoid IP blocks, and separately reverse-engineering the book-data endpoint used for author and series enrichment. Goodreads Book Scraper already does all four: it retries an interstitial instead of accepting it as "no more results," rotates through Apify Proxy on failure, and ships with the enrichment endpoint wired in.

If you only need one or two books, reading the page by hand is faster. For a repeatable pipeline — an author catalog, a series index, or a scheduled book-discovery feed — a maintained scraper removes the parsing and retry logic you would otherwise own.

### Why do developers and teams scrape Goodreads?

#### For AI engineers and agent builders

Author bibliographies with a stable JSON shape are a clean source for RAG indexing or recommendation-agent tooling: pass `authorTargets` for a set of authors, and each returned book row already carries `title`, `ratingValue`, `publishedYear`, and `seriesTitle` for grounding an agent's answers about an author's body of work, without writing an HTML parser into the agent's tool loop.

#### For researchers and analysts

Rating and publication-year fields (`ratingValue`, `ratingsCountValue`, `earliestPublishedYear`, `latestPublishedYear`) support publication-trend or reception studies across an author's career, using only data Goodreads already publishes on public pages — no login, no private data.

#### For developers building data products

The author summary row (`booksCollected`, `avgRatingOfCollectedBooks`, `distinctSeriesCount`) is a ready-made rollup for a book-discovery feed or an author-comparison dashboard, so the aggregation doesn't have to be recomputed downstream from raw book rows.

#### For publishing and content teams

Series placement (`isPartOfSeries`, `seriesPlacement`, `allSeries`) makes it possible to reconstruct reading order for a series or check whether a backlist title has been correctly credited to its author on Goodreads.

### How to scrape Goodreads (step by step)

1. Open **Goodreads Book Scraper** on its Apify Store page and click **Try for free** (or **Run**, if you already have it saved).
2. Provide at least one input: an author in **`authorTargets`** (name, author link, or bare author id) or a term in **`bookSearchTerms`**. The run raises an error if both are left empty.
3. Set **`booksPerAuthor`** / **`resultsPerSearch`** to cap how many titles come back per target, and toggle **`includeAuthorProfile`** and **`includeSeriesInfo`** for the columns you want.
4. Click **Start** and watch the run log — it reports each author resolved and each page collected.
5. Download the dataset as JSON, CSV, Excel or XML from the **Storage** tab, or pull it with the Apify API / `apify-client`.

#### What to do when Goodreads changes its structure

The Actor is maintained, and the output schema is designed to stay stable — field names and types don't change on the reader's end even when Goodreads' page markup does. No specific turnaround time is promised for a fix after a breaking change.

### ⬇️ Input

Every parameter below is read directly from `.actor/actor.json`. None are required individually, but the run needs at least one entry in `authorTargets` or `bookSearchTerms` — providing neither raises an error at start.

| Parameter | Required | Type | Constraints | Description |
| --- | --- | --- | --- | --- |
| `authorTargets` | No | array of strings | one per line | ✍️ Authors to expand. One author per line. Accepts an author name (e.g. `jane austen`), a Goodreads author link, or a bare author id. Each entry is expanded into that author's published titles. |
| `booksPerAuthor` | No | integer | min 1, max 5000, default 10 | 📖 Titles per author. How many titles to take from each author's bibliography. Example: 2 authors × 10 = up to 20 book rows plus 2 author summary rows. Defaults to 10 when left empty. |
| `bookSearchTerms` | No | array of strings | one per line | 🔎 Book search terms (optional). Optional second lane: book search phrases (e.g. `gothic romance`) or full Goodreads search links, one per line. These produce book rows only — author summary rows are made for the author lane above. |
| `resultsPerSearch` | No | integer | min 1, max 10000, default 10 | 📊 Books per search term. Target number of books for each search term above. Pages are read until this count is reached or the results run out. Defaults to 10 when left empty. |
| `includeAuthorProfile` | No | boolean | default `true` | 👤 Add author profile + summary row. ON: every book row gains `authorId`, `authorName`, `authorUrl`, author photo, bio, follower count and total works count, and each author lane closes with one summary row. OFF: those columns stay absent from the row, and no summary row is produced. |
| `includeSeriesInfo` | No | boolean | default `true` | 📚 Add series placement. ON: every book row gains `isPartOfSeries`, `seriesTitle`, `seriesUrl`, `seriesPlacement`, `seriesCount` and the full `allSeries` list. Standalone titles honestly return `isPartOfSeries=false` with empty series columns. |
| `pageDelayMs` | No | integer | min 0, max 60000, default 1000 | ⏱️ Pause between listing pages (ms). Milliseconds to wait between listing pages. Example: `1000` = one second. Defaults to 1000 when left empty. |
| `urls` | No | array of strings | one per line | 🧩 Legacy search terms. Compatibility field. Older input files that used this key keep working — the values are read as book search terms when the keyword lane above (`bookSearchTerms`) is empty. |
| `resultsPerQuery` | No | integer | min 1, max 10000 | 🧩 Legacy books per search. Compatibility field. Used as the per-search target when the keyword lane's own count above (`resultsPerSearch`) is empty. |
| `proxyConfiguration` | No | object (`proxy` editor) | prefill `{"useApifyProxy": false}` | 🌐 Network settings. Optional. Leave off for the fastest start; switch it on to route requests through Apify Proxy and pick a country if you want results from a specific region. |

**Example input:**

```json
{
  "authorTargets": ["jane austen", "https://www.goodreads.com/author/show/1265.Brandon_Sanderson"],
  "booksPerAuthor": 25,
  "bookSearchTerms": ["gothic romance"],
  "resultsPerSearch": 15,
  "includeAuthorProfile": true,
  "includeSeriesInfo": true,
  "pageDelayMs": 1000,
  "proxyConfiguration": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"] }
}
```

**Common pitfall:** leaving `authorTargets` and `bookSearchTerms` both empty fails the run immediately — at least one is required. A second, quieter pitfall: `proxyConfiguration` defaults to off, but the run still falls back to Apify Proxy automatically if a direct request fails, so turning it on manually mainly controls whether the *first* request is proxied, not whether proxying is available at all.

### ⬆️ Output

Results are pushed to the Actor's default dataset as one JSON object per row, downloadable as JSON, CSV, Excel or XML. Book rows and author summary rows are pushed to the same dataset and are distinguished by the `type` field (`"book"` or `"author"`); a summary row also sets `isChild: true`. Fields that don't apply to a row (for example, book-only fields on an author summary row) are simply absent from that row rather than set to `null`.

#### Scraped book row

```json
{
  "title": "Pride and Prejudice",
  "author": "Jane Austen",
  "rating": "4.29",
  "ratingsCount": "3,876,542",
  "published": "1813",
  "editions": "8,412",
  "url": "https://www.goodreads.com/book/show/1885.Pride_and_Prejudice",
  "coverUrl": "https://images-na.ssl-images-amazon.com/images/S/compressed.photo.goodreads.com/books/example.jpg",
  "bookId": 1885,
  "ratingValue": 4.29,
  "ratingsCountValue": 3876542,
  "publishedYear": 1813,
  "authorId": 1265,
  "authorName": "Jane Austen",
  "authorUrl": "https://www.goodreads.com/author/show/1265.Jane_Austen",
  "authorProfileImageUrl": "https://images.gr-assets.com/authors/example.jpg",
  "authorBio": "Jane Austen was an English novelist known primarily for her six major novels...",
  "authorFollowersCount": 142000,
  "authorWorksCount": 361,
  "authorRole": null,
  "contributors": null,
  "isPartOfSeries": false,
  "seriesTitle": null,
  "seriesUrl": null,
  "seriesPlacement": null,
  "seriesCount": 0,
  "allSeries": null,
  "type": "book",
  "isChild": false,
  "sourceType": "author_bibliography",
  "sourceInput": "jane austen",
  "sourceRank": 1,
  "scrapedAt": "2026-08-15T09:12:41Z"
}
```

#### Scraped author summary row

```json
{
  "type": "author",
  "isChild": true,
  "booksCollected": 25,
  "avgRatingOfCollectedBooks": 4.112,
  "totalRatingsOfCollectedBooks": 9482310,
  "topRatedTitle": "Pride and Prejudice",
  "topRatedUrl": "https://www.goodreads.com/book/show/1885.Pride_and_Prejudice",
  "topRatedRating": 4.29,
  "lowestRatedTitle": "Lady Susan",
  "lowestRatedUrl": "https://www.goodreads.com/book/show/12969.Lady_Susan",
  "lowestRatedRating": 3.66,
  "mostRatedTitle": "Pride and Prejudice",
  "mostRatedUrl": "https://www.goodreads.com/book/show/1885.Pride_and_Prejudice",
  "mostRatedRatingsCount": 3876542,
  "earliestPublishedYear": 1811,
  "latestPublishedYear": 1817,
  "distinctSeriesCount": 0,
  "seriesTitles": null,
  "sourceType": "author_bibliography",
  "sourceInput": "jane austen",
  "scrapedAt": "2026-08-15T09:12:55Z",
  "authorId": 1265,
  "authorName": "Jane Austen",
  "authorUrl": "https://www.goodreads.com/author/show/1265.Jane_Austen",
  "authorProfileImageUrl": "https://images.gr-assets.com/authors/example.jpg",
  "authorBio": "Jane Austen was an English novelist known primarily for her six major novels...",
  "authorFollowersCount": 142000,
  "authorWorksCount": 361
}
```

### How does Goodreads Book Scraper compare to other Goodreads scrapers?

| Feature | Goodreads Book Scraper | Generic alternative |
| --- | --- | --- |
| Author-to-bibliography expansion | ✅ Give a name or link, get the full list | ❌ Usually keyword search only |
| Author summary rollup | ✅ One row per author (avg rating, top/lowest/most-rated, series covered) | ❌ Left to the user to compute downstream |
| Series placement per title | ✅ `isPartOfSeries`, `seriesPlacement`, full `allSeries` list | ❌ Often not extracted |
| Output format | ✅ Typed JSON (also CSV, Excel, XML) | ❌ Frequently raw HTML or ad hoc fields |
| Legacy input compatibility | ✅ `urls` / `resultsPerQuery` still accepted | ❌ Breaking changes on schema updates are common |

If you're building an AI agent or RAG pipeline, the output format row is the decision-maker — parsing HTML inside an agent loop is a reliability failure mode, not a feature.

### How many results can you scrape with Goodreads Book Scraper?

`booksPerAuthor` accepts 1–5000 titles per author (default 10), and `resultsPerSearch` accepts 1–10000 books per search term (default 10) — these input maximums are the real ceiling on a single target. Internally, the author-bibliography lane reads 30 titles per listing page and stops after 500 pages for one target as a safety limit, well above the 5,000-title input maximum, so the input value — not the page safety limit — is what actually caps a run in practice. Both lanes also stop early if Goodreads runs out of results before the target count is reached. There is no documented Goodreads-side rate limit to cite; the Actor's own retry and delay settings (`pageDelayMs`, up to five retry attempts per request) are the practical throttle.

### Integrate Goodreads Book Scraper and automate your workflow

Goodreads Book Scraper works with any language or tool that can send an HTTP request, through the Apify API and official Apify client libraries.

#### REST API integration

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_API_TOKEN>")

run = client.actor("API-Empire/goodreads-book-scraper-author-bibliography-series").call(
    run_input={
        "authorTargets": ["jane austen"],
        "booksPerAuthor": 25,
        "includeAuthorProfile": True,
        "includeSeriesInfo": True,
    }
)

for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["type"], item.get("title") or item.get("authorName"))
```

Works in Python, Node.js, Go, Ruby, cURL — any client that can call the Apify API. Runs can also be triggered on a schedule or from a webhook through the Apify Console, with no code required beyond the initial setup.

### Is it legal to scrape Goodreads?

Scraping publicly accessible Goodreads pages is generally legal — this Actor reads only what's already visible without logging in. Two data types are returned here and they carry different considerations: book and series data (title, rating, edition count, series membership) is business/catalogue data, governed by Goodreads' Terms of Service and database-rights law rather than privacy law. Author profile fields (name, bio, photo, follower count) identify a specific person, so if you store or reuse them, treat that as personal data subject to GDPR/CCPA — have a lawful basis for holding it, and avoid using it in ways an author wouldn't reasonably expect from their own public profile. Consult legal counsel for commercial use cases involving bulk personal data.

### Frequently asked questions

#### Does Goodreads Book Scraper work without a Goodreads account?

Yes. Every request is unauthenticated — the Actor reads public listing pages and Goodreads' own public book-data endpoint with a browser-style header. No login, cookie, or API key is required from the user.

#### How often is the scraped data updated?

Every run fetches live from Goodreads at the moment it executes — there is no cached snapshot. Run the Actor again (or on a schedule) to get current ratings, rankings, and bibliography changes.

#### What happens if an author can't be matched or a search returns nothing?

If `authorTargets` doesn't match a real Goodreads author, that target is skipped with a warning in the run log and the run continues with the remaining targets — no row is produced for it. If a listing page runs out of titles before the requested count is reached, the Actor stops collecting for that target early rather than erroring; the dataset simply has fewer rows than `booksPerAuthor` or `resultsPerSearch` requested.

#### Can I scrape private or login-gated Goodreads content?

No. Only publicly visible listing pages and book data are read. There is no login step in the Actor, so shelves, reviews, or profile details a user has restricted to signed-in visitors are not accessible.

#### Does Goodreads Book Scraper work for AI agent workflows and LLM pipelines?

Yes, as an HTTP-callable Apify Actor — call it through the Apify API or `apify-client` from any agent framework. Every response is typed JSON with stable field names (`title`, `ratingValue`, `seriesTitle`, `authorBio`, and so on), so it can be passed directly into an LLM context window or indexed into a vector store without a parsing step.

#### How does Goodreads Book Scraper handle Goodreads' anti-bot defenses?

It retries failed requests up to five times with a short backoff between attempts, and for listing pages specifically, it treats a short "interstitial" response (below roughly 20KB and missing the results table marker) as a failed attempt rather than as "no more results," retrying it on a fresh Apify Proxy address instead of silently truncating the listing. Apify Proxy (residential group by default) is used as an automatic fallback on failure even when the `proxyConfiguration` input is left off.

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

Yes. Output is typed, normalized JSON with stable field names — no HTML, no CSS selectors. Pass it directly into an LLM prompt, a RAG index, or an agent tool call.

#### Can I use Goodreads Book Scraper without managing proxies?

Yes. Apify Proxy is wired in as a retry fallback automatically; you only need to configure `proxyConfiguration` yourself if you want to force proxy use from the first request or pick a specific country.

#### What happens when Goodreads changes its structure or blocks the scraper?

The Actor is maintained, and the output schema stays stable on the reader's end — field names and types don't change even when Goodreads' page markup does. No numeric turnaround time is promised for a given fix.

### 💬 Your feedback

Found a bug, or missing a field you need in the book, author, or series data? We want to know — open an issue through the Actor's page on the Apify Store or message API-Empire directly through Apify Console. Reports like this are how the field list above stays accurate as Goodreads' own pages change.

# Actor input Schema

## `authorTargets` (type: `array`):

One author per line. Accepts an author name (e.g. jane austen), a Goodreads author link, or a bare author id. Each entry is expanded into that author's published titles.

## `booksPerAuthor` (type: `integer`):

How many titles to take from each author's bibliography. Example: 2 authors x 10 = up to 20 book rows plus 2 author summary rows. Defaults to 10 when left empty.

## `bookSearchTerms` (type: `array`):

Optional second lane: book search phrases (e.g. gothic romance) or full Goodreads search links, one per line. These produce book rows only - author summary rows are made for the author lane above.

## `resultsPerSearch` (type: `integer`):

Target number of books for each search term above. Pages are read until this count is reached or the results run out. Defaults to 10 when left empty.

## `includeAuthorProfile` (type: `boolean`):

ON: every book row gains authorId, authorName, authorUrl, author photo, bio, follower count and total works count, and each author lane closes with one summary row (titles collected, average rating, highest/lowest rated title, most rated title, publication span, series covered). OFF: those columns stay empty and no summary row is produced.

## `includeSeriesInfo` (type: `boolean`):

ON: every book row gains isPartOfSeries, seriesTitle, seriesUrl, seriesPlacement (the number inside the series), seriesCount and the full allSeries list. Standalone titles honestly return isPartOfSeries=false with empty series columns.

## `pageDelayMs` (type: `integer`):

Milliseconds to wait between listing pages. Example: 1000 = one second. Defaults to 1000 when left empty.

## `urls` (type: `array`):

Compatibility field. Older input files that used this key keep working - the values are read as book search terms when the keyword lane above is empty.

## `resultsPerQuery` (type: `integer`):

Compatibility field. Used as the per-search target when the keyword lane's own count above is empty.

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

Optional. Leave off for the fastest start; switch it on to route requests through Apify Proxy and pick a country if you want results from a specific region.

## Actor input object example

```json
{
  "authorTargets": [
    "jane austen"
  ],
  "booksPerAuthor": 10,
  "resultsPerSearch": 10,
  "includeAuthorProfile": true,
  "includeSeriesInfo": true,
  "pageDelayMs": 1000,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

All scraped items in the Actor's default dataset: book rows and, when author-profile enrichment is on, one rollup summary row per author.

## `authorSummaries` (type: `string`):

The same dataset filtered to just the per-author rollup rows (average rating, best/worst rated title, series covered).

## `runSummary` (type: `string`):

One JSON record with run-level totals: books collected, author summaries produced, and titles found in a series.

# 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 = {
    "authorTargets": [
        "jane austen"
    ],
    "booksPerAuthor": 10,
    "resultsPerSearch": 10,
    "pageDelayMs": 1000,
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("api-empire/goodreads-book-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 = {
    "authorTargets": ["jane austen"],
    "booksPerAuthor": 10,
    "resultsPerSearch": 10,
    "pageDelayMs": 1000,
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("api-empire/goodreads-book-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 '{
  "authorTargets": [
    "jane austen"
  ],
  "booksPerAuthor": 10,
  "resultsPerSearch": 10,
  "pageDelayMs": 1000,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call api-empire/goodreads-book-scraper --silent --output-dataset

```

## MCP server setup

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