kestrel actors › Guest & customer reviews
Indeed Company Reviews Scraper — Employee Reviews
This Indeed company reviews scraper turns any company page on Indeed into a clean dataset of employee reviews: the 1-5 star rating, the five sub-ratings, the headline and the full text, pros and cons, the reviewer's job title and location, whether they still work there, the date, the helpful votes and the employer's public reply. Give it company slugs or /cmp/ URLs and it walks every page for you. It is a plain HTTP scraper — no API key, no login, no browser — so a run of company reviews costs a fraction of what a headless-browser scraper costs, and the same job runs the same way whether you want employer reviews for one company or for a thousand.
Run Indeed Company Reviews Scraper on Apify →
Indeed company reviews scraper pricing: what one row costs
| Event | What it is | Price | Charged for |
|---|---|---|---|
review | Review row | $0.002 | One Indeed employee review: the 1-5 star rating with its five sub-ratings (work-life balance, compensation and benefits, job security and advancement, management, culture), the full text, the headline, pros and cons, job title, location, current/former status, the publication date, helpful and unhelpful votes, the employer's reply and a direct link. Companies with no reviews, unknown companies, company rows, status rows and filtered reviews are free. |
$0.002 per review row, and nothing else. There is no monthly rental and no per-run fee.
| You want | Input | Reviews | Cost |
|---|---|---|---|
| A quick look at one company | maxReviewsPerCompany: 100 | 100 | $0.20 |
| One company, everything the US domain lists | countries: ["us"], maxReviewsPerCompany: 0 | ~4,000 | ~$8.00 |
| One company, everything worldwide | countries: ["all"], maxReviewsPerCompany: 0 | ~6,250 | ~$12.50 |
| Daily monitoring of 20 employers | sinceDate: "1 day" | ~40 | $0.08 |
| A complaints-only feed | sort: "rating_asc", maxRating: 2, maxReviewsPerCompany: 200 | 200 | $0.40 |
Free, always: the company row, the status rows, a company that has no reviews, an unknown company, and every review a filter dropped before billing. The run bills for exactly the review rows it delivers — that invariant is asserted in the test suite across every input mode and every spending limit.
Indeed company reviews scraper input reference
Every field of Indeed Company Reviews, straight from the actor's input schema.
| Field | Type | Default | What it does |
|---|---|---|---|
companies | array | [] | Indeed company slugs or company URLs, e.g. Google, Home-Depot, https://www.indeed.com/cmp/Starbucks/reviews or https://ca.indeed.com/cmp/Google. The slug is the part after /cmp/ on any Indeed company page. A URL that names a country domain (ca.indeed.com, uk.indeed.com) is read on that domain only; a bare slug uses the countries below. At least one. |
countries | array | ["us"] | Which Indeed domain to read each company on. "us" is www.indeed.com, which shows only the US-visible reviews (4,044 of Google's 6,258). "all" is www.indeed.com with Indeed's own fcountry=ALL filter and returns nearly every review in one walk (6,257 of 6,258) — the cheapest way to get everything. Per-country codes read that domain's own subset in that country's language: ca, uk, ie, au, nz, in, sg, ph, za, ae, hk, id, th, vn, lu, ch, be, de, at, fr, nl, es, it, pt, br, mx, ar, cl, co, pe, pl, se, dk, no, fi, tr, gr, hu, cz, ro, ua, jp, tw. Several domains together multiply the catalogue; reviews served by more than one domain are delivered and charged once. |
maxReviewsPerCompany | integer | 100 | 0 = every review the chosen domains list (Indeed pages them 20 at a time; a large employer runs to tens of thousands). N = the first N in the order below, counted across all the countries you asked for. The main cost control. Range 0-100000. |
sort | string (enum) | "newest" | The order Indeed returns reviews in. newest = most recent first (Indeed's default). helpfulness = the reviews other people voted most helpful. rating_desc = highest rated first. rating_asc = lowest rated first, which puts the complaints on page one. These four are the only values Indeed honours — every other string is silently ignored and served as newest first, so this is an enum. One of: newest, helpfulness, rating_desc, rating_asc. |
minRating | integer | 0 | Indeed rates each review 1-5 stars. 0 = no floor, 4 keeps only the positive ones. Combine with sort = highest rated first and the walk stops as soon as a whole page falls below the floor. Filtered reviews are never charged. Range 0-5. |
maxRating | integer | 0 | The other end of the same 1-5 star scale: 0 = no ceiling, 2 gives a complaints feed. Combine with sort = lowest rated first so the worst reviews arrive on page one and the walk stops once a whole page is above the ceiling. Filtered reviews are never charged. Range 0-5. |
requireText | boolean | false | Drop rating-only reviews before billing. Indeed reviews almost always carry a paragraph, so this rarely removes much. |
jobTitleContains | string | "" | Case-insensitive substring of the reviewer's job title, e.g. "engineer", "nurse", "driver". Empty = every job title. Indeed's own fjobtitle filter needs an exact normalised title, so this one runs in the actor: the pages are read either way and the reviews it drops are simply never charged. |
locationContains | string | "" | Case-insensitive substring of the review's location, e.g. "Mountain View", "TX", "London". Empty = every location. Like the job-title filter this runs in the actor, before billing. |
sinceDate | string | "" | YYYY-MM-DD, or relative so schedules never go stale: "30 days", "2 weeks", "6 months". With the default newest-first order the walk stops once a whole page predates the date, so a daily schedule reads one page per company. Empty = no date limit. |
includeCompanyRow | boolean | true | Also emit one free row per company with Indeed's own overall rating, the total review count, the 1-5 star histogram, the five category averages, the happiness score, the topic breakdown, the pros and cons Indeed extracts, and — from the company overview page, one extra free request — industry, size, headquarters, website, founded year, revenue band and CEO. Never charged. |
sessions | integer | 2 | How many proxy sessions (egress IPs) run in parallel. More is faster; each is paced separately. Indeed refuses roughly one in three fresh sessions with a Cloudflare check, so the actor rotates and retries — a few sessions ride that out comfortably. Range 1-16. |
perIp | number | 1 | Pace for each session. Indeed answered 10 of 10 consecutive review pages at 1 req/s from one datacenter IP with no throttling; 1 is comfortable. Range 0.1-5. |
proxyConfiguration | object | {"useApifyProxy": true} | Apify Proxy with the datacenter group is enough and is what this actor was measured on — 7 of 9 fresh datacenter sessions were served, and a session that answers once keeps answering. Residential works too and costs far more. Keep the default. |
Example input
{
"companies": ["Google", "https://www.indeed.com/cmp/Starbucks/reviews"],
"countries": ["us"],
"maxReviewsPerCompany": 200,
"sort": "newest",
"includeCompanyRow": true
}
{
"companies": ["Google"],
"countries": ["all"],
"maxReviewsPerCompany": 0,
"sort": "newest"
}
Indeed company reviews scraper output fields
One row per Indeed employee review (charged), plus a free company context row and a free status row per company and country.
Dataset views in the Apify Console: Overview, Reviews, Complaints, Companies, Status.
| Field | Type | What it holds |
|---|---|---|
type | string | review | company | status |
target | string | The input value (slug or /cmp/ URL) this row came from |
company | string | Indeed's company slug, the part after /cmp/ (e.g. Google, Home-Depot) |
company_name | string | Company name as Indeed displays it |
country | string | Which Indeed domain the row was read on: us, all, ca, uk, de, ... |
review_id | string | Indeed's stable encrypted review id, the id in the review URL |
rating | number | Overall rating: 1-5 stars on review rows, Indeed's average (e.g. 4.3) on company rows |
work_life_balance | integer | Reviewer's 1-5 rating for work/life balance; null when they did not rate it |
compensation_benefits | integer | Reviewer's 1-5 rating for compensation and benefits; null when they did not rate it |
job_security_advancement | integer | Reviewer's 1-5 rating for job security and advancement; null when they did not rate it |
management | integer | Reviewer's 1-5 rating for management; null when they did not rate it |
culture_values | integer | Reviewer's 1-5 rating for culture; null when they did not rate it |
title | string | The review headline the employee wrote |
text | string | The full review body, untruncated |
pros | string | The reviewer's Pros field, when the review was submitted with the pros/cons form |
cons | string | The reviewer's Cons field, when the review was submitted with the pros/cons form |
job_title | string | The reviewer's job title, e.g. "Software Engineer" |
location | string | Where the reviewer worked, e.g. "Mountain View, CA" |
review_country | string | ISO country code Indeed files the review under, e.g. US |
employment_status | string | current (still works there) | former |
review_date | string | Publication date as YYYY-MM-DD, parsed from the domain's own language |
review_date_text | string | The date exactly as Indeed printed it, e.g. "August 25, 2026" or "3. September 2025" |
helpful_count | integer | How many people voted the review helpful |
unhelpful_count | integer | How many people voted the review not helpful |
employer_reply | string | The employer's official public response to the review, if any |
employer_reply_date | string | Date of the employer response, YYYY-MM-DD |
url | string | Direct link to the review on Indeed |
review_count | integer | All reviews Indeed holds for the company worldwide (company rows) |
found_review_count | integer | Reviews this domain actually lists — the number the walk can reach (company and status rows) |
histogram | object | Star histogram: how many reviews gave 1, 2, 3, 4 and 5 stars (company rows) |
ratings_breakdown | object | Indeed's company averages for work_life_balance, compensation_benefits, job_security_advancement, management and culture_values (company rows) |
happiness_score | integer | Indeed's Work Happiness score, 0-100 (company rows) |
happiness_grade | string | The band Indeed puts that score in, e.g. EXCELLENT, FAIR (company rows) |
top_pros | array | The positives Indeed extracts from the review corpus (company rows) |
top_cons | array | The negatives Indeed extracts from the review corpus (company rows) |
topics | array | Indeed's topic breakdown: name, rating and count for work-life balance, pay and benefits, job security, management and culture (company rows) |
industry | string | Industry Indeed files the company under, e.g. "Internet & Web Services" (company rows) |
company_size | string | Employee range, e.g. "10,000+" or "51 to 200" (company rows) |
headquarters | string | Headquarters location (company rows) |
website | string | The company's own website as Indeed lists it (company rows) |
founded | integer | Year founded (company rows) |
revenue | string | Revenue band, e.g. "more than $10B" (company rows) |
ceo | string | CEO name as Indeed lists it (company rows) |
company_url | string | The company's Indeed profile page (company rows) |
reviews_fetched | integer | Reviews read in this run for the company before filters (company rows) |
status | string | ok | no_reviews | not_found | duplicate | error (status rows) |
reviews | integer | Reviews delivered and charged for this company and country (status rows) |
filtered | integer | Reviews dropped by the filters, never charged (status rows) |
pages | integer | Review pages read (status rows) |
total | integer | Reviews Indeed holds for the company worldwide (status rows) |
duplicates | integer | 1 when this target resolved to a company another target already covers (status rows) |
error | string | Why a company or country failed (status rows) |
fetched_at | string | UTC timestamp of the request |
Indeed company reviews scraper in Python, JavaScript and curl
Python
from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("kestrel/indeed-company-reviews").call(run_input={
"companies": ["Google", "Starbucks"],
"countries": ["us"],
"maxReviewsPerCompany": 500,
"sort": "rating_asc",
"maxRating": 2,
"requireText": True,
})
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
if row["type"] == "review":
print(row["rating"], row["job_title"], "|", row["title"])
JavaScript (Node)
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('kestrel/indeed-company-reviews').call({
companies: ['Google'],
countries: ['all'],
maxReviewsPerCompany: 1000,
sinceDate: '90 days',
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.filter((i) => i.type === 'review').length, 'employee reviews');
curl
curl -X POST "https://api.apify.com/v2/acts/kestrel~indeed-company-reviews/run-sync-get-dataset-items?token=<YOUR_APIFY_TOKEN>&format=csv" \
-H 'Content-Type: application/json' \
-d '{"companies":["Google"],"countries":["us"],"maxReviewsPerCompany":100}' \
-o indeed-reviews.csv
The same three calls are runnable files in the examples repository: examples/indeed-company-reviews.py|.js|.sh.
Limits and known traps
- A page past the end silently repeats page one. Indeed answers
?start=beyond the last page with HTTP 200 and the first twenty reviews again, never an error. A naive scraper pages forever and bills you for the same rows over and over. This actor bounds the walk three ways — by the review count Indeed declares, by the page number Indeed reports, and by review ids it has already seen — so it stops on the first repeated page and charges you for nothing extra. - Roughly one fresh session in three is refused. Indeed puts a Cloudflare check in front of some connections. The actor rotates the proxy session and retries; a session that is served once keeps being served. If every retry is refused, the company gets
status: "error"with the reason — never a fake empty result. - A sub-rating of
0means "not rated", not zero stars. Indeed writes0when a reviewer skipped a category. Those becomenullhere, so an average is not silently dragged to the floor. - Counts differ by domain.
review_countis what Indeed holds worldwide;found_review_countis what the domain you read actually lists. They are supposed to differ. - A handful of Indeed hosts are not country domains.
my.indeed.comis the account subdomain and serves a sign-in page, so Malaysia is not in the list; a few other hosts have no company section at all. Only verified domains are offered. - Dates in a language we have not met. Every domain in the list was checked, and the raw string is always kept in
review_date_text; if a format ever changes,review_datebecomes null rather than wrong, andsinceDatekeeps such a review rather than silently dropping it. - Reviews are moderated by Indeed. Only approved reviews appear on the page, so this is Indeed's published corpus, not every review ever submitted.
FAQ
How do I find a company's Indeed slug?
Search the company on Indeed, open its page, and copy the part of the URL after /cmp/ — https://www.indeed.com/cmp/Home-Depot gives Home-Depot. You can also paste the whole URL; the actor extracts the slug for you. That slug is how you ask for Indeed reviews by company rather than by keyword.
Can I get all Indeed reviews for a company, not just the US ones?
Yes: set countries to ["all"] and maxReviewsPerCompany to 0. That reads www.indeed.com with Indeed's worldwide filter and returns essentially the complete corpus in one walk.
How do I export Indeed reviews to CSV?
Run the actor, open the run's Storage tab and choose CSV, or add &format=csv to the dataset API call as in the curl example above. Every review row carries the same fields, so the Indeed reviews CSV has stable columns and can be loaded straight into a spreadsheet or a warehouse.
Can I get only the negative reviews?
Set sort to rating_asc and maxRating to 2. The lowest-rated reviews arrive first and the walk stops at the first page above the ceiling, so you pay only for the complaints.
Does it return the employer's replies?
Yes — employer_reply and employer_reply_date, when the company has responded. Indeed employer replies are missing from most reviews at most companies, which is itself a useful metric: the share of one- and two-star reviews you have answered.
How do I monitor new reviews every day?
Schedule the actor with sinceDate: "1 day" and the default newest-first order. The walk stops at the first page older than the cut, so each company costs one page.
Is there an Indeed employee reviews API?
Not a public one. This actor is the practical substitute, and it reads Indeed reviews without an API key of any kind: a stable JSON contract over the public company pages, with paging, retries and the wrap-around trap handled for you.
What happens if a company has no reviews?
You get a status row with no_reviews and found_review_count: 0, plus the free company row, and you are charged nothing.
Do I need proxies or my own API key?
No key. Proxies come from Apify Proxy and the datacenter group is the default and is enough — this needs no residential traffic and no browser.
How fast is it?
About one page — twenty reviews — per second per session, so roughly 1,200 reviews per minute at the default two sessions. Raise sessions for bulk company reviews across many employers.
Can I scrape Indeed reviews for many companies at once?
Yes. Put every slug in companies; they are processed in parallel across your sessions, duplicates are recognised, and the per-company cap applies to each one independently.
How to use Indeed Company Reviews Scraper
Related scrapers
Run Indeed Company Reviews Scraper
The actor lives on the Apify Store, where the pricing tab carries the live per-row price and the input form has every field above: apify.com/kestrel/indeed-company-reviews