Glassdoor Jobs Scraper — Hiring, Careers, Vacancies + Ratings avatar

Glassdoor Jobs Scraper — Hiring, Careers, Vacancies + Ratings

Pricing

from $1.20 / 1,000 job listings

Go to Apify Store
Glassdoor Jobs Scraper — Hiring, Careers, Vacancies + Ratings

Glassdoor Jobs Scraper — Hiring, Careers, Vacancies + Ratings

Glassdoor job listings with the employer's star rating on the same row — no second run; about 4 in 5 rows carry one. From $1.20 per 1,000 job listings. Your row cap and deadline are hard limits. Blocked pages, empty searches and repeats are never charged.

Pricing

from $1.20 / 1,000 job listings

Rating

0.0

(0)

Developer

Steadyfetch Team

Steadyfetch Team

Maintained by Community

Actor stats

0

Bookmarked

1

Total users

0

Monthly active users

16 hours ago

Last modified

Share

Glassdoor job listings with the employer's star rating on the same row — no second run. About 4 in 5 rows carry one. Set a row cap and a deadline and the run stops exactly there; blocked pages, empty searches, repeated listings and listings we could not read come back with an honest status and are never charged.

Every rival makes you run twice: once for the job cards, once for the company's rating. Here employerRating sits on the job row, at no extra request and no extra charge.

From $1.20 per 1,000 job listings — all-inclusive pay per event, nothing charged for starting a run, charged only on delivery.

Just want to see it work? Run it with no input at all ({} from the API or an AI agent) and you get a real 5-listing sample — "software engineer" on Glassdoor US, one results page — charged like any run, a few cents at most. Set only the filters — a country site, a minimum employer rating, a row cap — and no search terms, and that same sample runs under them, charged like any run; a limit larger than the sample's own is capped at it, and one extra uncharged row names which settings were yours. In the console, Start with the example search terms already in the form and you get those searches for real, up to your row cap.

Running the same search again? A listing already delivered to your account is never charged a second time — see Never pay for the same listing twice.

Glassdoor Jobs Scraper input form in the Apify console: search terms, country sites, and the filters, employer profiles, limits and continue sections

Unofficial. This actor is not affiliated with, endorsed by, or sponsored by Glassdoor. "Glassdoor" and all related marks are trademarks of Glassdoor, Inc. It reads only the public search pages an ordinary browser sees.

Every row carries charged and missReason, so you can reconcile the invoice from the dataset itself without opening the console. Only rows with charged: true were billed.


Output — a real row

Five US "software engineer" listings from a real run, in the Apify console's dataset table — the employer's star rating, the salary text, the posted date and the listing link, one row per job:

Dataset table of a real Glassdoor Jobs Scraper run: five US software engineer listings with employer star rating, salary text, posted date, summary snippet and listing URL

And one delivered row in full:

{
"source": "glassdoor",
"sourceJobId": "1010240614385",
"jobUrl": "https://www.glassdoor.co.uk/job-listing/data-analyst-itv-JV_IC2671300_KO0,12_KE13,16.htm?jl=1010240614385",
"title": "Data Analyst",
"company": "ITV",
"employerRating": 3.9,
"glassdoorEmployerId": "10157",
"location": { "raw": "London, England", "city": "London", "region": "England", "country": "GB" },
"isRemote": null,
"salaryText": "£40k - £50k (Employer provided)",
"salaryIsGlassdoorEstimate": false,
"salary": { "min": 40000, "max": 50000, "currency": "GBP", "period": "year", "isEstimate": false },
"postedRelative": "24h",
"postedAt": "2026-08-26T09:12:00.000Z",
"easyApply": false,
"snippet": "…",
"query": "data analyst",
"country": "GB",
"dedupeKey": "glassdoor:1010240614385",
"charged": true,
"missReason": null
}

Browse the full live example dataset → — one real run, unedited: 30 delivered listings, 26 of them carrying the employer's star rating, and 60 repeated listings that were skipped and charged once. The last row is the run's own summary.

Plus one search row per search term and country sitequery, country, delivered, outcome — and one summary row with delivered, stoppedBy, resumeCursor, datasetId (paste it into resumeFromDatasetId to continue this run later), carriedOver and chargedEvents.

jobUrl is rebuilt from the listing's own id and carries no ad or tracking token, so it is safe to use as a join key and it will still resolve next month.

Agent / API paste-block

Actor: steadyfetch/glassdoor-jobs-scraper
Required: queries (array of job titles/keywords, or Glassdoor search URLs)
Optional: countries (array of ISO-2, default ["US"] — each is its own search)
minEmployerRating (1-5 — keeps only employers rated at least this)
postedWithinDays (integer, 1-30)
remoteOnly (boolean, default false — filtered rows are never charged)
includeEmployerProfiles (boolean, default false — one profile row per unique
employer, charged per delivered profile; see pricing tab)
maxItems (integer, default 100 — hard cap for the whole run)
maxPagesPerSearch (integer, default 5 — the walk stops early on repeats)
maxRunSeconds (integer, default 900 — clean stop, never a timeout kill)
includeSeen (boolean, default false — OFF: a listing already delivered to
your account is skipped, uncharged. ON: it comes back marked
repeat:true with firstSeenRunId, still uncharged)
resumeFromDatasetId (string — an earlier run's dataset ID; every listing already
in it is skipped and never charged again)
Charges: job-listing once per delivered job listing.
employer-profile once per delivered employer profile — only when
includeEmployerProfiles is on. Nothing else.
Note: sending null (or an empty value) for an optional field is the same as leaving
it out — the default is used, so templated callers need no special-casing.
Build spec: https://apify.com/steadyfetch/glassdoor-jobs-scraper/api
Token: https://console.apify.com/settings/integrations
curl -X POST "https://api.apify.com/v2/acts/steadyfetch~glassdoor-jobs-scraper/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"queries":["data analyst"],"countries":["GB"],"maxItems":30}'

The employer rating, and the honest number on it

The rating is on the same payload as the job card, so it costs nothing extra. It is not on every row: measured across three cleared result pages, 74 of 90 rows carried one — about 4 in 5. The rest are employers Glassdoor has no rating for, and those rows report employerRating: null rather than a zero.

minEmployerRating filters on it. Rows it removes are never fetched twice and never charged.

Employer profiles (optional add-on)

Turn on Include employer profiles and each unique employer among your delivered listings gets one extra row read from its Glassdoor profile page:

{
"employerName": "ITV",
"overallRating": 3.9,
"ratingCount": 682,
"recommendToFriendPct": 73,
"ceoName": "Carolyn McCall",
"ceoApprovalPct": 69,
"glassdoorEmployerId": "10157",
"profileUrl": "https://www.glassdoor.com/Overview/Working-at-ITV-EI_IE10157.11,14.htm"
}

One profile per employer per run, however many of its listings you collected — 30 listings from one employer is one profile row, and it is never fetched or charged twice, including when a run is moved or restarted. Each delivered profile is charged as its own event — see the pricing tab; while that event is not yet active on this listing, profiles are still delivered and nothing extra is charged. Every profile row also carries charged, and that field is what actually happened to the row — read it rather than assuming it. The honest-miss contract is the same as everywhere else in this actor: a profile page that answers with an access check is an uncharged row marked re-runnable, and an employer Glassdoor has no rating for is an uncharged row that says exactly that. Off by default — a run that does not ask for profiles is billed exactly as before.

How many jobs one search returns

About 40 to 50 distinct listings. Glassdoor shows 30 cards per result page, but paging deeper mostly returns the same 30 again: on four independent walks (both the ?p= and the _IP{N} form, up to page 20) the union of unique listing ids landed between 41 and 50.

So this actor de-duplicates by listing id and stops a search once two pages in a row add nothing new — otherwise you would pay for the same 30 jobs five times. A bigger maxItems does not produce more rows on its own: more rows come from more search terms and more country sites. The summary row repeats this every run.

Searching one city

Glassdoor encodes a location as an internal id inside the URL, not as a plain city name. Rather than guess it, run the search you want on Glassdoor, then paste that URL into queries — the location, radius and any filters you set come with it. A pasted search keeps its own country and is not crossed with your countries list.

Country sites

countries picks Glassdoor's own country sites. glassdoor.com and glassdoor.co.uk are verified end to end; Canada, Ireland, Australia, New Zealand, India and Singapore use the identical search grammar. location.country is a real ISO-2 country code, never a state or a postcode: the card's own line when it names a country, and otherwise the country site you searched. Glassdoor's card usually prints a city line, so nothing is geocoded or guessed.

Salary, exactly as Glassdoor prints it

salaryText is the string on the page. salary is our parse of it. salaryIsGlassdoorEstimate tells you whether the number is Glassdoor's own model or the employer's own figure — Glassdoor labels these "(Glassdoor est.)" and "(Employer provided)", and no rival surfaces the difference. When a listing has no salary at all, both stay null rather than being estimated.

What you are never charged for

situationmissReasonrun status
Glassdoor answered with an access checkCHALLENGEDSUCCEEDED — re-runnable, and the row says so
Glassdoor rate-limited usTHROTTLEDSUCCEEDED — re-runnable
Glassdoor returned a page we could not readSOURCE_UNAVAILABLESUCCEEDED — the row says it is on us
The search ran and matched nothingCLEAN_EMPTYSUCCEEDED — a real answer
A listing whose card we could not readSOURCE_UNAVAILABLESUCCEEDED — reported as its own row, never dropped
A listing repeated on a later result pagenever charged twice
A listing your account already has from an earlier runskipped, never charged twice
A row your filters removednever charged
A search term or URL we could not readUSER_INPUTSUCCEEDED — the row names the fix
Your own maxItems / maxRunSeconds / cost capSTOPPED_AT_LIMITSUCCEEDED — summary row with resumeCursor and the datasetId to continue from

We never guess. An access check is reported as an access check, not as "no jobs found" — a temporary problem is never dressed up as a permanent verdict, and it is never charged.

This actor may fail when Glassdoor changes things. The two you are most likely to see are CHALLENGED (Glassdoor answered with a Cloudflare access check) and SOURCE_UNAVAILABLE (a page we could not read as results). Both ship as a row with charged: false, the run still SUCCEEDS, and the row says whether re-running helps.

Never pay for the same listing twice

Every run remembers the listings — and the employer profiles — it delivered for your account, in a key-value store called glassdoor-watch-account in your own Apify account. Run the same searches again and what you already have is skipped before it takes a row slot: no row, no charge, and no bite out of your row cap. The status line says how many (18 already in your account, skipped, not charged) and OUTPUT.skippedSeen holds the number.

An employer profile costs its own event and its own fetch, so the same rule covers it: a profile your account already has is never re-fetched and never charged twice.

Want one complete dataset per run instead of only what changed? Turn on Include listings you already have (includeSeen: true) and those rows come back anyway — marked repeat: true with firstSeenAt and firstSeenRunId naming the run that first delivered them, charged: false.

  • The store lives in your account, one record (SEEN). Open it in Storage → Key-value stores to see everything the actor has delivered you, or delete the record to start over. This actor runs with limited permissions and touches nothing else in your account.
  • Entries older than 90 days count as new again, so a listing you collected last quarter is collected (and charged) again if it is still live.
  • Only delivered rows are remembered. A blocked page, an unreadable card or a search that matched nothing leaves nothing behind, so the next run tries it again.
  • If the store cannot be read on some run, the run still runs: it delivers and charges as usual and the status line says the repeat check was unavailable, so you know a repeat may have been charged that once.

What one job listing costs

One event per delivered job listing — nothing charged for starting a run, no per-search fee, no per-page fee, and no upcharge for the employer rating on the row. The optional employer-profile row is its own separately priced event, charged only when you turn it on and only per profile actually delivered (see the pricing tab).

Your Apify planPrice per job listingPer 1,000
Apify Free plan$0.0048$4.80
Bronze$0.0036$3.60
Silver$0.0024$2.40
Gold, Platinum, Diamond$0.0012$1.20

The store header shows the Gold price. The whole ladder is printed here because an actor whose promise is billing honesty cannot have an undisclosed tier ladder.

Limits are hard limits

maxItems, maxPagesPerSearch and maxRunSeconds stop the run exactly where you set them. The run still finishes successfully, the summary row names which limit bound, and resumeCursor lists the searches that were not reached. A time limit ends the collecting, never the delivering: a result page already fetched when the clock runs out is still delivered in full, so a run never pays for listings it does not hand you. To collect the rest without paying again for what you already have, re-run the same input with a higher cap and resumeFromDatasetId set to that run's dataset ID (it is on the summary row as datasetId): every listing already in that dataset is skipped — never fetched, never charged — and the new summary's carriedOver says how many. A dataset ID that cannot be read stops the run before it fetches anything, uncharged.

FAQ

Why did I get 45 rows when I asked for 200? Because you gave one search term. See "How many jobs one search returns" — add terms or country sites, not a bigger cap.

Can I paste a Glassdoor search URL? Yes, and it is the way to search a specific city. A link to a single job posting or a company overview page is refused with a guidance row that says what to paste instead.

Why is employerRating empty on some rows? Because Glassdoor has no rating for that employer. About one row in five. It is null, never a zero, so an average over the column is not silently wrong.

Is the salary real? salaryIsGlassdoorEstimate answers that per row. false means the employer published it; true means Glassdoor modelled it.

Does {} charge anything? Yes — like any run, and a few cents at most. With no input at all the run is a real 5-listing sample ("software engineer" on Glassdoor US, one results page) and you pay for the listings it delivers. If Glassdoor does not answer at that moment you get one uncharged row saying so, never an empty result. Set only filters and no search terms and the same sample runs under them, charged the same way.

How do I get this into n8n, Make or a sheet? Run it and read the dataset — one row per job, stable column names, charged on every row.

Honesty contract

Glassdoor sits behind an access check that clears for some requests and not others. When a search cannot be completed, it comes back with an honest status, marked re-runnable, and never charged. Before that row ships, a run that still has time waits a couple of minutes and tries the search again on fresh connections — up to three rounds, within a third of the time it has left — so a passing access-check storm usually clears inside the same run. When it does not, the row says how many connections were tried over how many minutes and roughly how much of the run was left. A search Glassdoor answers is never waited on.

Two runs started back to back can refuse each other — the access check reacts to how recently that route was hit, not to who is asking. If a run comes back with CHALLENGED rows, leave a few minutes and run it again; you were not charged for the attempt.


Run it from an AI agent (MCP)

Works out of the box from any MCP client (Claude, Cursor, …) via Apify's MCP server — ask for "product manager jobs on Glassdoor in London with the employer rating" and point it at this actor.

Pin this actor in one click — add this server to your MCP config:

{
"mcpServers": {
"glassdoor-jobs": {
"url": "https://mcp.apify.com?tools=steadyfetch/glassdoor-jobs-scraper",
"headers": { "Authorization": "Bearer YOUR_APIFY_TOKEN" }
}
}
}

Steadyfetch jobs suite

One actor per source, one job each, the same row shape — so a pipeline built on one source takes the next without a rewrite. All-inclusive pay per event, nothing charged for starting a run, charged only on delivery. From 16 September 2026 Google Jobs adds one search fee of $0.004 per search that returns listings; a search that returns nothing pays nothing.

SourceActor
IndeedIndeed Jobs Scraper — Salary, Apply Links, Exact Country
A company's own career site (paste a domain)Company Jobs Scraper — Career Pages by Domain, 9 ATS Boards
Glassdoor, with the employer ratingthis actor
Several boards at once, de-duplicatedJob Board Scraper — Indeed + Career Sites, One Deduped Feed
Google Jobs, with the full panel and apply linksGoogle Jobs Scraper — Full Panel, Salary, Apply Links

The rest of the steadyfetch shelf — same contract everywhere: all-inclusive pay per event, nothing charged for starting a run, charged only on delivery (three actors add a small delivery-conditional fee from 16 September 2026: keyword volume's fresh lookup, profile posts' profile lookup, Google Jobs' search fee — each README states it).

FamilyActors
Ad creative intelligenceFacebook · Google Ads video · TikTok · LinkedIn · Google Ads text & OCR
Trends & keywordsGoogle Trends · Trends Now · Breakout keywords · Autocomplete keywords · Keyword volume & CPC · Social trends
YouTube transcriptsYouTube videos · YouTube channels
InstagramReel transcripts · Profile posts
JobsIndeed · Career sites by domain · Glassdoor · Multi-board · Google Jobs
AmazonProducts · Search · Bestsellers · Sellers
Any media fileSpeech to Text · any link or file

Feedback & support

Found an issue? Open it on the Issues tab — we usually reply within a couple of hours, always within a day. Feature requests welcome. If this actor earned its keep, a rating helps other buyers find it, and saving it keeps it one click away in your console.