Goodreads Books Scraper
Search Goodreads and get books as clean rows: title, author, average rating, ratings count, year, editions, cover image and link. No API key needed.
How it works
- 1Open it on Apify
Hit Run on Apify — it opens the tool in the cloud, no install.
- 2Set the inputs
Adjust
queries,searchField,maxItems(sensible defaults are pre-filled). - 3Click Run
The tool runs on Apify’s cloud and collects the data for you.
- 4Export the results
Download as JSON, CSV or Excel, or pipe straight into your app, Google Sheets, or an AI agent.
Pricing
$0.00114 per book = $1.14 per 1,000
| You are charged for | When | Price |
|---|---|---|
| Book scraped | One book with its rating and ratings count. Searches that match nothing are never charged. | $0.00114 |
Pay-per-event pricing: you are billed per result, not per subscription. Billing is handled by Apify on your own account. These are the live Apify store prices, in effect since 2026-09-20, and they are what you are actually charged.
Inputs
| Field | What it does | Type |
|---|---|---|
queries | Book titles, author names or plain keywords to search Goodreads for. Up to 20 per run. Each term is searched in turn until the run reaches the row limit below. | array |
searchField | Which part of a book to match on. 'Everything' is Goodreads' own default and mixes titles, authors and series. 'Title only' and 'Author only' return genuinely different sets - searching 'christie' as an author gives you Agatha Christie's novels, as a title it gives books with Christie in the name. | string |
maxItems | Total number of books to return across all search terms. The budget is shared evenly between the terms, so four terms and 40 rows gives you ten of each. Keep it low while you are testing - you pay per row. | integer |
proxyUrls | Leave this empty. By default the run rotates a large pool of addresses that cost you nothing per gigabyte. Fill it in only if you specifically want the traffic to leave through proxy servers you already pay for, in the form http://user:pass@host:port. | array |
What you get
A structured dataset — each result includes fields like:
querytitleauthoravgRatingratingsCountpublishedYearurlcoverUrleditionsCountbookIdsearchFieldrankExport every run as JSON, CSV or Excel, or send it to your app, a database, Google Sheets, or an AI agent.
Related tools in Developer & Research Tools
Other ready-to-run tools in the same category — all pay-per-use on the Apify cloud.
GitHub Scraper
Search GitHub repos and users: stars, forks, language, topics, licence, plus user bio, company and followers. No token needed. $0.90 per 1,000 rows.
Stack Overflow / Stack Exchange Scraper
Search Stack Overflow and Stack Exchange by keyword or tag. Score, answer count, views, reputation and body text. $2 per 1,000 questions.
Package Registry Scraper (npm + PyPI)
Get npm and PyPI package metadata as JSON. Version, license, author, repo, keywords and npm monthly downloads. $2 per 1,000 packages.
arXiv Scraper
Search arXiv papers by title, author, abstract or category. Get full abstracts, authors, categories, DOI, dates and PDF links. $2 per 1,000 papers.
OpenAlex Scholarly Works Scraper
Search 250M+ OpenAlex papers with no API key. Get titles, authors, venue, year, citations, DOI, OA links and full abstracts. $2.00 per 1,000 papers.
Crossref Scholarly Works Scraper
Search 150M+ papers on Crossref: DOI, title, authors, journal, publisher, date, citations and abstract. No API key. $1.00 per 1,000 works.
Where this tool sits
- Categories
- Developer & Research Tools
Goodreads Books Scraper
Type a title, an author or a phrase and get the Goodreads books that match, one row each: the title, who wrote it, the average rating, how many people rated it, the year, how many editions exist, the full-size cover image and the Goodreads link. No Goodreads account, no API key, no browser.
- Twenty books come back per page of results, and the run pages down until it has the number you asked for.
- Search several terms in one run. Books that match more than one term are returned once and charged once.
- You can point the search at titles only, authors only, or everything — the same three modes the site itself offers.
- A term that genuinely matches nothing says so in a free diagnostic row. It does not look like a failure, and it does not bill you.
- Cover links point at the full-size image Goodreads holds, not the 75-pixel thumbnail the results page draws.
Price
$1.14 per 1,000 books, plus a $0.0005 start fee per run.
This is a flat rate on every plan, free or paid. There are no volume tiers, no minimum spend, no subscription and no add-on fees. What you read here is what you pay on day one and on day four hundred.
| Books | Total cost |
|---|---|
| 100 | $0.1145 |
| 1,000 | $1.1405 |
| 10,000 | $11.4005 |
| 100,000 | $114.0005 |
What is actually charged
- One
book-scrapedevent per book row written to the dataset. Nothing else is metered per row. - Free: the sample row an empty run returns, and every diagnostic row — a blocked target, a dead URL, a search that matched nothing. Those rows all carry
"charged": false. - Books already returned earlier in the same run are dropped before they are charged, so a term that overlaps another one costs you nothing twice.
- A listing Goodreads shows without a rating is skipped rather than charged — you are paying for a rating, so a row without one is not a row.
- A run that finds nothing costs the start fee and nothing more.
- Rows never leave the dataset without a charge, and are never charged without a row. The billed event is a named one, so there is no price quietly attached to
apify-default-dataset-item— the trick that makes some scrapers bill you for their own error messages.
Input
{
"queries": [
"dune",
"project hail mary"
],
"searchField": "all",
"maxItems": 40
}
| Field | What it does |
|---|---|
queries | Book titles, author names or plain keywords. Up to 20 per run. Each one is searched in turn until the run reaches maxItems. |
searchField | Where to look: all (the default), title or author. Searching author for "christie" returns her novels; searching title returns books with Christie in the name. They are genuinely different result sets. |
maxItems | Total rows across all terms. The budget is split evenly, so four terms and 40 rows gives you ten of each. Default 20, hard ceiling 2,000. Keep it low while you are testing — you pay per row. |
proxyUrls | Leave empty. Fill it in only if you want the traffic to leave through proxy servers you already pay for, as http://user:pass@host:port. |
Run it with empty input and you get one clearly labelled sample row, free, so you can see the output shape before you spend anything.
Output
One row per book. A real row from a real run:
{
"ok": true,
"charged": true,
"recordType": "book",
"query": "dune",
"searchField": "all",
"rank": 1,
"bookId": "44767458",
"title": "Dune (Dune, #1)",
"author": "Frank Herbert",
"avgRating": 4.29,
"ratingsCount": 1710264,
"publishedYear": 1965,
"editionsCount": 34,
"url": "https://www.goodreads.com/book/show/44767458",
"coverUrl": "https://i.gr-assets.com/images/S/compressed.photo.goodreads.com/books/1555447414i/44767458.jpg",
"scrapedAt": "2026-09-20T10:14:02.771Z"
}
Field notes
title— exactly as Goodreads prints it, series name and number included — "Dune (Dune, #1)". Accents and non-Latin scripts come through intact.author— the first credited author. A book with a translator or an illustrator listed beside the author returns the author.avgRating— the Goodreads average at the moment the row was read, 1.00 to 5.00.ratingsCount— how many people have rated it. This is the number worth sorting on — a 4.8 from nine readers is not a 4.8 from nine hundred thousand.publishedYear— the year of the edition Goodreads chose to show. Often null, and that is the source, not the parse: box sets, study guides and many translated editions are listed with no year at all. On a 219-row run across six terms it was present on 140 of them, and it thins out the deeper you page.editionsCount— how many editions of the same work Goodreads knows about. A high number is a reliable sign you have found the canonical book rather than a reprint.url— the clean book page link. Goodreads appends per-request tracking parameters to its own links; those are stripped, so the same book has the same URL on every run.coverUrl— the full-size cover. The results page serves a 75-pixel thumbnail; the size token is removed so you get the original upload. Nothing is downloaded during the run — this is a link, and it costs you nothing.bookId— the Goodreads book id. Stable, and safe to use as a primary key across runs.rank— where the book sat in your results, counting from 1 across the whole run.
Every real row carries "charged": true. Sample rows carry "_sample": true and diagnostic rows carry "_diagnostic": true with an errorCode you can filter on, and neither is ever billed.
How it works
- It reads the public Goodreads search results directly — the same page you would see signed out — and pulls the book fields out of it. No login, no cookies, no headless browser.
- Requests leave through a large pool of rotating addresses. Goodreads throttles by address and answers a throttled request with a short challenge page rather than an error, so the run recognises that page, moves to another address and asks again. Three tries per page, then it gives up on that page and tells you.
- Each term is paged 20 books at a time until your
maxItemsis reached. Book ids already seen in the run are skipped, and when a whole page adds nothing new the term stops there rather than paging into repeats.
What people use it for
- Sizing up a genre before writing in it — pull 200 books on a theme and sort by
ratingsCountto see what readers actually turned up for, rather than what a bestseller list says. - Keeping a reading-app or bookshop catalogue honest: match your ISBNs by title and author, then refresh the rating and the cover link on a schedule.
- Checking an author's back catalogue in one pass —
searchField: "author"gives you every book credited to them with ratings attached. - Comp research for a book proposal. Agents ask what your book sits next to; a table of titles, years and rating counts answers it better than a paragraph.
- Filling in cover art and ratings for a list you already have — feed the titles in as queries and take
coverUrlandavgRatingback out.
Reading the output
Every run writes three kinds of row, and they are easy to tell apart:
- Real rows carry
"charged": trueand"recordType": "book". One billed event each. - The sample row carries
"_sample": trueand"charged": false. There is exactly one, it only appears when you ran with no search terms, and it is there so you can see the output shape before you spend anything. - Diagnostic rows carry
"_diagnostic": true,"charged": falseand anerrorCodeyou can switch on:NO_RESULTSwhen Goodreads has nothing matching a term,BLOCKEDwhen the run could not get a search page for a term at all,ROW_INCOMPLETEwhen a listing had no rating to return,NETWORKwhen Goodreads could not be reached,TIME_BUDGETwhen the run ran out of time before reaching a term. Each carries a plain-Englisherrorand thequeryit belongs to.
NO_RESULTS and BLOCKED are deliberately separate codes. The first means Goodreads looked and had nothing; the second means the run never got to look. Treating those as the same thing is how a scraper quietly tells you a book does not exist when really it was throttled.
If you only want the data, filter on charged == true. That count always equals the number of events you were billed for, so the dataset is its own invoice.
Limitations
- This does not read book descriptions, ISBNs, page counts, genres, series order, quotes or review text. It reads the search results, and those are the fields the search results carry.
- It does not collect reviewers, their names, their profiles or anything they wrote. Reviews are people's own writing and are out of scope here.
publishedYearis frequently null — Goodreads simply does not print a year on many listings. On a 219-row test run it came back on 140. Every other field was present on all 219.- Search relevance falls away with depth. The first two or three pages of a title search are the book and its editions; by page twenty you are into loose word matches, and on a short query like "dune" that means titles containing "d'une". Ask for a few hundred rows, not a few thousand, unless you intend to filter afterwards.
- Goodreads never says "that is the last page". Ask for page 900 and it hands back twenty books. The run stops when a page adds no book it has not already seen, which is the only honest end-of-results signal available.
- Ratings and rating counts are a snapshot at read time. They move.
- A term with fewer matches than its share of the budget returns fewer rows. The leftovers are not handed to the other terms.
- Goodreads throttles by address and the run retries three times per page. On a bad minute a page can still be lost; you get a
BLOCKEDrow naming the term, not a silently short result. - Hard ceilings: 2,000 rows and 20 terms per run, 50 pages per term. Split larger jobs across runs.
Questions
Do I need a Goodreads account or an API key?
No. Goodreads stopped issuing API keys years ago — the old developer endpoint answers "Invalid API key" to everyone now. This reads the public search pages instead, signed out, exactly as a visitor would.
What happens when a search matches nothing?
You get one uncharged diagnostic row with errorCode: "NO_RESULTS" for that term, and the run carries on to your other terms. You are never billed for a term that returned nothing.
How do I tell "no such book" from "the run got blocked"?
The errorCode. NO_RESULTS means Goodreads returned its search page and it was empty. BLOCKED means the run could not get the search page at all after three tries from different addresses. They are never conflated, and neither is charged.
Why is publishedYear empty on some rows?
Because Goodreads did not print one. Box sets, summaries, study guides and many translated editions are listed without a year. The field is null rather than guessed.
Can I search by author instead of title?
Yes — set searchField to author. The three modes are all, title and author, and they return genuinely different sets. Searching "christie" as an author gives Agatha Christie's novels; as a title it gives books with Christie in the name.
Will the run fail if Goodreads blocks it?
No. You get an uncharged diagnostic row explaining what happened and the run still finishes as succeeded. A failed run would still bill you the start fee, which would mean paying to be told something went wrong.
Can I run this on a schedule?
Yes. Nothing is held between runs, so the same input is safe to repeat. Use bookId to work out what is new and to spot rating changes.
How do I get exactly the rows I paid for?
Filter the dataset on "charged": true. Sample and diagnostic rows are always false, and the count of charged rows always equals the number of billed events.