Goodreads Review Scraper By Star Rating & Spoiler Filter
Pricing
from $2.99 / 1,000 results
Goodreads Review Scraper By Star Rating & Spoiler Filter
π Goodreads Review Scraper extracts ratings, reviews, dates, reviewers & metadata from Goodreads book pages at scale. π Export clean data to CSV/JSON for sentiment, market research & content analysis. π Ideal for authors, publishers, researchers & SEO teams.
Pricing
from $2.99 / 1,000 results
Rating
0.0
(0)
Developer
SimpleAPI
Maintained by CommunityActor stats
0
Bookmarked
1
Total users
0
Monthly active users
9 days ago
Last modified
Categories
Share
Goodreads Review Scraper β Star Ratings, Spoilers and Likes
Goodreads Review Scraper pulls public book reviews straight from Goodreads' own review feed and returns only the ones that match your criteria: a star-rating range, spoiler-free or spoilers-only, a minimum number of likes or comments, and a minimum review length. Every row carries the reviewer's name and profile, the full review text, rating, spoiler flag, engagement counts and absolute ISO timestamps. Publishers, book marketers, reader-sentiment researchers and AI teams building book-recommendation tools use it to pull exactly the reviews they need instead of downloading a book's entire review history and filtering it afterward.
π What is Goodreads Review Scraper?
Goodreads Review Scraper is an Apify Actor that reads a Goodreads book's public review list and returns a filtered, typed JSON dataset β one row per matching review. No Goodreads account or login is required; every field it returns is already visible to any anonymous visitor on the book's reviews page.
- Discovery by book URL or bare book ID β paste a full Goodreads book link, or just the numeric ID (e.g.
26032825), and the Actor builds the URL for you - Star-rating range filter β
minStars/maxStarsnarrow the result to any 0β5β band - Spoiler filtering on two layers β Goodreads' own spoiler flag, plus an optional AI-based second pass
- Engagement and length thresholds β minimum likes, minimum comments, minimum review length, and a text-only toggle
- Absolute ISO timestamps β
createdAtIso,updatedAtIso,lastRevisionAtIsoandscrapedAtalongside the raw epoch-millisecond fields - Sort, language and edition controls β popular / newest / oldest ordering, a review-language filter, and whole-work vs. this-edition-only scope
What data can I extract with Goodreads Review Scraper?
Every matching review is one dataset row carrying the review's own content, its spoiler and engagement signals, the reviewer's profile, and derived timestamps.
| Field | Example value | Use case |
|---|---|---|
bookUrl | "https://www.goodreads.com/book/show/26032825" | Joins every row back to the book it was scraped from |
id | "5972134816" | Unique review identifier |
rating | 4 | The star rating the review left; what minStars/maxStars filter on |
spoilerStatus | true | Goodreads' own raw spoiler flag on the review |
isSpoiler | true | Decorated boolean mirror of spoilerStatus |
aiIsSpoiler | true / false / null | AI-detected spoiler flag; null until aiSpoilerDetection runs |
aiSpoilerConfidence | 0.93 / null | The AI model's confidence in aiIsSpoiler, 0.0β1.0 |
text | "This book completely wrecked meβ¦" | Full review text β sentiment mining, quote pulls |
textLength | 812 | Character count of text; what minTextLength/textOnly filter on |
likeCount | 143 | Likes on the review; what minLikeCount filters on |
commentCount | 12 | Comments on the review; what minCommentCount filters on |
recommendFor | null | Goodreads' own "recommended for" tag on the review, when set |
createdAt | 1610013600000 | Raw creation time, epoch milliseconds |
updatedAt | 1612000000000 | Raw last-update time, epoch milliseconds |
lastRevisionAt | 1612086400000 | Raw last-text-edit time, epoch milliseconds |
createdAtIso | "2021-01-07T10:00:00Z" | Absolute ISO creation date, sortable without conversion |
updatedAtIso | "2021-01-30T09:06:40Z" | Absolute ISO last-update date |
lastRevisionAtIso | "2021-01-31T09:06:40Z" | Absolute ISO last-text-edit date |
shelving | { "shelf": {...}, "taggings": [...], "webUrl": "..." } | The reader's shelf and any reader-added tags attached to this review |
creator | { "name": "...", "webUrl": "...", ... } | The reviewer's public profile β see below |
__typename | "Review" | GraphQL type name, passed through as-is |
scrapedAt | "2026-07-26T12:00:00Z" | When the Actor captured this row |
creator is a nested object: id, name, webUrl, imageUrlSquare, isAuthor, followersCount, textReviewsCount, __typename, and contributor (id, works.totalCount, __typename). shelving nests shelf (name, webUrl, __typename) and taggings (an array of { tag: { name, webUrl, __typename } }).
Two Goodreads fields are dropped rather than returned as dead null columns: the top-level viewerHasLiked and creator.viewerRelationshipStatus. Both are always empty for a logged-out (anonymous) request, so the Actor omits them instead of shipping a column that is never anything but null.
Spoiler and engagement signals
isSpoiler (Goodreads' own flag) and aiIsSpoiler (the optional AI layer) combine into a single spoiler verdict that spoilerFilter acts on β a review counts as a spoiler if either flag says so. likeCount and commentCount are the community's own signal of which reviews are worth reading; a marketing team pulling quotes for a book page typically wants spoilerFilter: "exclude" plus a minLikeCount floor, so only well-liked, spoiler-free reviews come back.
Star rating and length-targeting fields
rating, textLength and spoilerStatus are the three fields the input schema turns into filters β minStars/maxStars on rating, minTextLength/textOnly on length, and spoilerFilter on the spoiler flags. createdAtIso lets you scope a run to a launch window or a specific print run without touching epoch math.
β οΈ Why not build this yourself?
Goodreads previously ran a public developer API for third parties, but Amazon, which owns Goodreads, stopped issuing new API keys years ago, and the program is no longer open to new developers β so building a fresh integration against an official Goodreads API is not an option today.
The practical alternative β scraping the site directly β has its own obstacle. Goodreads puts its book HTML pages behind an AWS WAF JavaScript challenge: a plain server-side request gets back an HTTP 202 with an x-amzn-waf-action: challenge header instead of a page, so a request never reaches the reviews at all.
This Actor works around that without touching the WAF-protected HTML page. It resolves each book's internal work/review resource ID through Goodreads' own public GraphQL endpoint β the same AWS AppSync endpoint and public apiKey value that Goodreads' website ships in its own client-side JS bundle to every visitor β which sits outside the WAF. If that stable key ever stops working, the Actor falls back to re-extracting a fresh one straight from the page's JS bundles. Maintaining that discovery step, plus a direct β Apify datacenter β Apify residential proxy escalation ladder for when a request is blocked outright, is the ongoing work this Actor absorbs so you don't have to.
What can you do with Goodreads review data?
- π£ A publisher's marketing team launching a title pulls
spoilerFilter: "exclude",minStars: 4and aminLikeCountfloor to source spoiler-free, well-liked quotes for the book's sales page β no manual read-through of hundreds of reviews. - π¬ An author or literary agent researching reader reception sets
minStars: 1,maxStars: 2and aminTextLengthfloor to surface substantive complaints rather than one-line pans, ahead of a sequel or a revised edition. - π A market or reading-trend researcher runs the same filter set across many
urlsin one call and comparesratingdistributions andtextLengthacross a genre or an author's backlist. - π€ An AI engineer building a book-recommendation or sentiment model indexes
text,ratingandisSpoilerper book into a vector store or training set, withaiSpoilerDetectioncatching plot spoilers Goodreads' own flag missed before the text reaches readers.
β¬οΈ Input
Only urls is required; every filter below defaults to "keep everything."
| Parameter | Required | Type | Description | Example value |
|---|---|---|---|---|
urls | Yes | array | Goodreads book links, or bare numeric book IDs β the Actor auto-corrects an ID into a full URL. | ["26032825"] |
maxItems | No | integer | Matching reviews to save per book. Min 1, max 10000, default 20. The Actor paginates until it collects this many matches or the review list ends. | 50 |
minStars | No | integer | Keep reviews rated at least this many stars. Min 0, max 5, default 0. 0 = no minimum (also keeps unrated reviews). | 1 |
maxStars | No | integer | Keep reviews rated at most this many stars. Min 0, max 5, default 5. | 2 |
spoilerFilter | No | string (enum: all, exclude, only) | all β every review Β· exclude β spoiler-free only Β· only β spoilers only. Uses Goodreads' own spoiler flag, combined with AI detection when enabled. Default all. | "exclude" |
minLikeCount | No | integer | Keep reviews with at least this many likes. Min 0, default 0. | 10 |
minCommentCount | No | integer | Keep reviews with at least this many comments. Min 0, default 0. | 0 |
minTextLength | No | integer | Keep reviews whose text is at least this many characters. Min 0, default 0. | 200 |
textOnly | No | boolean | Skip rating-only reviews that have no written text. Default false. | true |
aiSpoilerDetection | No | boolean | Off by default. When on, an AI model flags plot spoilers Goodreads' own flag missed; result lands in aiIsSpoiler. Requires aiModel and aiApiKey. | false |
aiModel | No | string (enum, 26 values β see below) | The AI model to use for spoiler detection. Default "claude-haiku-4-5". | "gpt-4o-mini" |
aiApiKey | No | string (secret) | API key for the chosen provider. Falls back to the provider's own environment variable if left blank. | "sk-..." |
filtersAndOptions | No | object | Sort order, review language and edition scope β see sub-fields below. | { "sortBy": "newest" } |
proxyConfiguration | No | object | Apify Proxy configuration. Prefilled {"useApifyProxy": false}; the Actor escalates automatically if a request is blocked. | {"useApifyProxy": true} |
aiModel enum (26 values): claude-haiku-4-5 (Claude Haiku 4.5 β Anthropic, cheapest), claude-sonnet-5 (Claude Sonnet 5 β Anthropic), claude-opus-4-8 (Claude Opus 4.8 β Anthropic, best), claude-fable-5 (Claude Fable 5 β Anthropic, most capable), gpt-4o-mini (GPT-4o mini β OpenAI, cheap), gpt-4o (GPT-4o β OpenAI), gpt-4.1-mini (GPT-4.1 mini β OpenAI), gpt-4.1 (GPT-4.1 β OpenAI), o3-mini (o3-mini β OpenAI, reasoning), o1 (o1 β OpenAI, reasoning), gemini-2.0-flash-lite (Gemini 2.0 Flash-Lite β Google, cheapest), gemini-2.0-flash (Gemini 2.0 Flash β Google, cheap), gemini-1.5-pro (Gemini 1.5 Pro β Google), gemini-2.5-flash (Gemini 2.5 Flash β Google), gemini-2.5-pro (Gemini 2.5 Pro β Google, best), grok-2-latest (Grok 2 latest β xAI), grok-3-mini (Grok 3 mini β xAI, cheap), grok-3 (Grok 3 β xAI), grok-beta (Grok Beta β xAI), deepseek-chat (DeepSeek Chat/V3, cheap), deepseek-reasoner (DeepSeek Reasoner/R1), sonar (Sonar β Perplexity, cheap), sonar-pro (Sonar Pro β Perplexity), sonar-reasoning (Sonar Reasoning β Perplexity), mistral-small-latest (Mistral Small, cheap), mistral-large-latest (Mistral Large).
filtersAndOptions sub-fields:
| Sub-field | Type | Enum | Default |
|---|---|---|---|
sortBy | string | popular, newest, oldest | popular |
languageCode | string | all, en, bn, fr, de, es, it, pt, ru, ja, ko, zh, ar, hi, nl, pl, tr, vi, id, th | all |
reviewEdition | string | ALL (whole work), only_this_book (this edition only) | ALL |
Bring-your-own AI key, always. aiSpoilerDetection is a second, optional detection layer on top of Goodreads' own spoiler flag β it does not ship with a bundled AI credential for any provider. To use it you supply your own aiApiKey for the provider matching your chosen aiModel (or the Actor falls back to reading that provider's own environment variable, e.g. ANTHROPIC_API_KEY, OPENAI_API_KEY, GEMINI_API_KEY, XAI_API_KEY, DEEPSEEK_API_KEY, PERPLEXITY_API_KEY, MISTRAL_API_KEY, if you've set one on the Actor's environment). No provider in the list above is bundled, endorsed, or required β it is simply the set of models the Actor knows how to call. If no usable key is found, the run continues and aiIsSpoiler/aiSpoilerConfidence are left null rather than failing.
Example input
{"urls": ["26032825"],"maxItems": 50,"minStars": 1,"maxStars": 2,"spoilerFilter": "exclude","minLikeCount": 10,"minTextLength": 200,"filtersAndOptions": { "sortBy": "newest", "languageCode": "en" }}
Common pitfall: maxItems counts matching reviews, not reviews scanned. A narrow combination β say minStars: 5, spoilerFilter: "only" and a high minLikeCount β can mean the Actor pages through most of a book's review list without ever reaching your maxItems target, simply because too few reviews satisfy every condition at once. That is expected behavior, not a bug: the run stops once it reaches maxItems matches, the review list ends, or an internal safety cap of 1,500 pages per book is hit (a ceiling this Actor imposes on itself so an extremely strict filter can't loop forever β it is not a limit Goodreads itself documents).
β¬οΈ Output
Typed, normalized JSON with the same 22 keys on every row. Rows are pushed to the dataset live as each matching review is found. Export as JSON, CSV or Excel, or read the dataset through the Apify API.
Example output
{"bookUrl": "https://www.goodreads.com/book/show/26032825","__typename": "Review","id": "5972134816","creator": {"id": "12345678","imageUrlSquare": "https://images.gr-assets.com/users/1234567890p2/12345678.jpg","isAuthor": false,"followersCount": 214,"__typename": "User","textReviewsCount": 88,"name": "Alex Rivera","webUrl": "https://www.goodreads.com/user/show/12345678-alex-rivera","contributor": null},"recommendFor": null,"updatedAt": 1612000000000,"createdAt": 1610013600000,"spoilerStatus": false,"lastRevisionAt": 1612086400000,"text": "This book completely wrecked me in the best way. The pacing in the back half is relentless and the ending earns every bit of its weight.","rating": 5,"shelving": {"shelf": { "name": "read", "webUrl": "https://www.goodreads.com/review/list/12345678?shelf=read", "__typename": "Shelf" },"taggings": [],"webUrl": "https://www.goodreads.com/review/list/12345678?shelf=favorites","__typename": "Shelving"},"likeCount": 143,"commentCount": 12,"createdAtIso": "2021-01-07T10:00:00Z","updatedAtIso": "2021-01-30T09:06:40Z","lastRevisionAtIso": "2021-01-31T09:06:40Z","textLength": 138,"isSpoiler": false,"aiIsSpoiler": false,"aiSpoilerConfidence": 0.04,"scrapedAt": "2026-07-26T12:00:00Z"}
π― How do you filter and target specific reviews?
Filtering happens after the Actor fetches the review list for the book(s) named in urls β there is no site-wide Goodreads search here, so completeness depends on listing every book you care about.
Within a book's review list, four filter axes stack: star range (minStars/maxStars, both 0β5; minStars: 0 explicitly also keeps unrated reviews, since an unrated review is treated as a 0 for this comparison), spoiler mode (spoilerFilter: all/exclude/only, evaluated against the combined Goodreads-flag-or-AI-flag verdict), quality thresholds (minLikeCount, minCommentCount, minTextLength, and textOnly β note textOnly and minTextLength are independent, ANDed conditions: textOnly alone only requires non-zero length, while any minTextLength above 0 already excludes zero-length reviews on its own, so setting both is only useful when you want a length floor above zero), and volume (maxItems, counted post-filter, per book).
Three real filter combinations:
{ "urls": ["26032825"], "minStars": 1, "maxStars": 2, "minTextLength": 200 }
Complaint mining β 1β2β reviews with at least 200 characters of substance.
{ "urls": ["26032825"], "minStars": 5, "spoilerFilter": "exclude", "minLikeCount": 20, "textOnly": true }
Spoiler-free marketing quotes β 5β , well-liked, text reviews only.
{ "urls": ["26032825"], "spoilerFilter": "only", "aiSpoilerDetection": true, "aiModel": "claude-haiku-4-5", "aiApiKey": "<your-key>" }
Spoiler audit β every review Goodreads' own flag or the AI layer considers a spoiler.
How to extract Goodreads review data programmatically
Goodreads Review Scraper runs on Apify. Start it from the Apify Console, or call it through the Apify API using apify_client and your Apify token.
Python example
from apify_client import ApifyClientclient = ApifyClient("<YOUR_APIFY_TOKEN>")run = client.actor("<YOUR_USERNAME>/goodreads-review-scraper-by-star-rating-spoiler-filter").call(run_input={"urls": ["26032825"],"maxItems": 50,"minStars": 4,"spoilerFilter": "exclude",})for review in client.dataset(run["defaultDatasetId"]).iterate_items():print(review["rating"], review["likeCount"], review["text"][:80])
Works in Go, Ruby, Node.js, cURL β any language that can make an HTTP request.
Export to spreadsheets or CRM
Export the dataset as CSV directly from the Apify Console or API. Flat fields (rating, text, likeCount, commentCount, createdAtIso) map straight to spreadsheet columns; nested fields like creator and shelving are flattened into dotted sub-columns (e.g. creator/name, creator/webUrl) so a reviewer's name and profile link still land in their own cells.
Is it legal to scrape Goodreads reviews?
Scraping publicly accessible review data is broadly treated as permissible where no login or paywall is bypassed, and Goodreads Review Scraper reads only what Goodreads already shows any anonymous visitor on a book's reviews page.
Goodreads reviews sit somewhere between two categories: they are attached to a public product catalogue (the book), and each review is authored by a Goodreads username, not a private individual acting outside a public platform context. Because the primary output here is book and review data β not a directory of private people β the obligations that mainly apply are Goodreads' terms of service and database-and-reuse rules around a compiled catalogue, rather than a personal-data regime. If you plan to retain reviewer names or profile links at scale for anything beyond internal research, weigh that against applicable privacy law for the jurisdictions your users are in.
Consult legal counsel for commercial applications involving bulk storage or republication of review data.
β Frequently asked questions
What if a book has zero reviews matching my star and spoiler filters?
The run simply returns zero rows for that book and moves on to the next url β no error, no placeholder row. The Actor logs how many reviews it scanned versus how many matched, so you can tell "no reviews exist" apart from "none passed the filter" by loosening the filter and re-running.
Can I get likes and comments along with the review text?
Yes. Every row already includes likeCount and commentCount alongside text β there's no separate call or extra field to request; minLikeCount and minCommentCount let you threshold on them directly.
How accurate is the star rating and spoiler data?
The Actor returns the rating and spoiler flag exactly as they exist on Goodreads at request time β it doesn't recompute or estimate either. The optional AI spoiler layer (aiIsSpoiler) is a probabilistic second opinion on top of that, not a replacement for it, and ships its own aiSpoilerConfidence score so you can judge how much to trust a given call.
How many reviews can I get per run?
maxItems (default 20, max 10000) sets the matching-review target per book, and urls can hold as many books as you want in one run. The Actor paginates each book's review list until it hits that many matches, the list ends, or its own internal safety cap of 1,500 pages for that book is reached β whichever comes first. No Goodreads-side cap on reviews per book was found in the scraping logic itself.
How exactly does the star-rating filter work?
minStars and maxStars (0β5) form an inclusive range applied to every review's rating. The one nuance: minStars: 0 (the default) is not just "no minimum" β it explicitly also keeps reviews with no star rating at all, since an unrated review is treated as 0 for the comparison. Raise minStars above 0 to exclude unrated reviews along with anything below your floor.
Does the AI spoiler detection use my own AI account?
Yes, entirely. aiSpoilerDetection is off by default, and turning it on requires you to supply aiApiKey for whichever provider your chosen aiModel belongs to (or have that provider's API key set as an environment variable on the Actor). The Actor does not include or resell access to any AI provider β it only knows how to format the request once you bring the credential.
Does Goodreads Review Scraper work with Claude, ChatGPT and AI agent frameworks?
Yes, on two levels: the Actor itself is callable as a standard HTTP endpoint through the Apify API by any agent framework, and separately, its optional aiSpoilerDetection feature can call Claude, GPT, Gemini, Grok, DeepSeek, Perplexity or Mistral models directly, using whichever key you provide.
How does Goodreads Review Scraper compare to other review scrapers on Apify?
Checked on the Apify Store on 2026-07-26, none of the nearest-ranked review scrapers target Goodreads specifically β memo23/trustpilot-scraper-ppe, zen-studio/trustpilot-review-scraper and zen-studio/capterra-reviews-scraper all cover other review platforms with similar star-rating filter mechanics. Compared on filter design, this Actor's distinguishing point is combining Goodreads' own spoiler flag with an optional, user-supplied AI spoiler-detection layer β a filter axis the Trustpilot and Capterra scrapers above don't document at all, since spoilers aren't a concept on those platforms.
Can I use Goodreads Review Scraper without a Goodreads account or API key?
Yes. No Goodreads login, cookie or developer API key is required β the Actor reads only public review data. The one credential you need is your own Apify token to run the Actor; an AI provider key is only needed if you turn on the optional aiSpoilerDetection feature.
Conclusion
Goodreads Review Scraper turns a book's public review list into a filtered, typed dataset β star-rating range, spoiler mode, engagement thresholds and review length applied before a single row reaches you, with an optional AI layer for the spoilers Goodreads' own flag misses. It's built for publishers, marketers, researchers and AI teams who need a precise subset of reviews, not a full history to filter by hand. Start it from the Apify Console with a book URL or ID and let the matching reviews stream into your dataset.