Request a tool
All toolsAutomationsGuidesMCP serverRequest a toolPlatformsCategories
Goodreads Books Scraper icon

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.

15 runs on Apify $0.00114 per book ($1.14 / 1,000)
Run this in the cloudRun on Apify →

Developer & Research Tools

How it works

  1. 1
    Open it on Apify

    Hit Run on Apify — it opens the tool in the cloud, no install.

  2. 2
    Set the inputs

    Adjust queries, searchField, maxItems (sensible defaults are pre-filled).

  3. 3
    Click Run

    The tool runs on Apify’s cloud and collects the data for you.

  4. 4
    Export 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 forWhenPrice
Book scrapedOne 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

FieldWhat it doesType
queriesBook 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
searchFieldWhich 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
maxItemsTotal 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
proxyUrlsLeave 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:

querytitleauthoravgRatingratingsCountpublishedYearurlcoverUrleditionsCountbookIdsearchFieldrank

Export 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 iconDeveloper & Research Tools

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.

18 use cases

Stack Overflow / Stack Exchange Scraper iconDeveloper & Research Tools

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.

2 use cases

Package Registry Scraper (npm + PyPI) iconDeveloper & Research Tools

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.

2 use cases

arXiv Scraper iconDeveloper & Research Tools

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.

2 use cases

OpenAlex Scholarly Works Scraper iconDeveloper & Research Tools

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.

2 use cases

Crossref Scholarly Works Scraper iconDeveloper & Research Tools

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.

2 use cases

See all 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.

BooksTotal cost
100$0.1145
1,000$1.1405
10,000$11.4005
100,000$114.0005

What is actually charged

  • One book-scraped event 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
}
FieldWhat it does
queriesBook titles, author names or plain keywords. Up to 20 per run. Each one is searched in turn until the run reaches maxItems.
searchFieldWhere 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.
maxItemsTotal 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.
proxyUrlsLeave 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 maxItems is 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 ratingsCount to 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 coverUrl and avgRating back out.

Reading the output

Every run writes three kinds of row, and they are easy to tell apart:

  • Real rows carry "charged": true and "recordType": "book". One billed event each.
  • The sample row carries "_sample": true and "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": false and an errorCode you can switch on: NO_RESULTS when Goodreads has nothing matching a term, BLOCKED when the run could not get a search page for a term at all, ROW_INCOMPLETE when a listing had no rating to return, NETWORK when Goodreads could not be reached, TIME_BUDGET when the run ran out of time before reaching a term. Each carries a plain-English error and the query it 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.
  • publishedYear is 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 BLOCKED row 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.