# Glassdoor Reviews Scraper By Current or Former Employee (`scrapebase/glassdoor-reviews-scraper`) Actor

🔍 Glassdoor Reviews Scraper extracts employee reviews, ratings, pros/cons, job titles, locations & dates. 📊 Export JSON/CSV for sentiment, HR analytics & competitive research. ⚡ Fast, reliable, API & pagination-ready—ideal for employer brand & hiring. 🚀

- **URL**: https://apify.com/scrapebase/glassdoor-reviews-scraper.md
- **Developed by:** [ScrapeBase](https://apify.com/scrapebase) (community)
- **Categories:** Automation, Lead generation, Social media
- **Stats:** 12 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-usage

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Glassdoor Reviews Scraper By Current or Former Employee

Reads Glassdoor company employee reviews and lets you choose whether they come from people who
have already **left** the company or people who **still work there** — with every row tagged with
the kind of contract that person actually held (full-time / part-time). Built for a People-Ops or
retention lead — or an HR due-diligence analyst sizing up an acquisition target — who wants to
size a churn problem by tenure and workforce segment, not read a mixed feed dominated by current
staff.

**What it scrapes:** Glassdoor company employee reviews, for the companies you give it.
**What's new here:** you pick reviews from leavers or current staff, each row says whether that
person was full-time or part-time, and you can sort by newest-first instead of Glassdoor's default
relevance order.

### Why this is not "the base plus a filter"

The base actor already emits `is_current_job` on every row, so this variant is not "it tells you
if they're current" — the base already does that. The value here is the **filter itself** plus the
**employment-type tag**. On Google, 38,675 of 48,796 visible reviews (79.3%) were written by
current staff, and the base actor's hard-coded `RELEVANCE` sort surfaces that majority first — a
default 50-row base run is expected to contain about 10 leaver reviews, and you don't choose which
10\. Here, 50 rows means 50 leavers, by default, with no configuration.

### Input

All base fields are unchanged, in the same order, with the same names/types/defaults. Three new
fields are appended after `proxyConfiguration`.

| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| `startItems` | `array` | Yes | — | List of Glassdoor company URLs (e.g. `https://www.glassdoor.com/Overview/Working-at-IBM-EI_IE354.11,14.htm`), company names, or keywords. *(base field, unchanged)* |
| `maxResults` | `integer` | No | `50` | Maximum number of reviews to collect per company (1-1000). *(base field, unchanged)* |
| `proxyConfiguration` | `object` | No | `{"useApifyProxy": false}` | Choose proxies. Actor starts with no proxy, then datacenter, then residential if blocked. Sticks with residential after fallback. *(base field, unchanged)* |
| `reviewerStatus` | `string` (select) | No | `"former"` | `former` (default) / `current` / `any`. See "How `former` works" below. |
| `employmentTypes` | `array` of `string` | No | `[]` | Free list — type any Glassdoor employment-status value (confirmed: `REGULAR`, `PART_TIME`); empty = every kind. Not a fixed dropdown — see "Honest limits". |
| `sortBy` | `string` (select) | No | `"DATE"` | `DATE` (default, newest first) / `RELEVANCE` (the base's own default) / `RATING` / `RATING_ASC`. |

#### How `reviewerStatus: "former"` works — read before you rely on it

Glassdoor's API has a real server-side switch for **current** employees only
(`onlyCurrentEmployees: true`). There is **no equivalent server-side "former only" switch.** So in
`former` mode (the default), this actor downloads reviews with no tenure filter applied on
Glassdoor's side, and discards the current-employee rows itself, client-side, before pushing them
to the dataset. That means it reads more pages than it delivers rows: on Google (measured
2026-08-15), about 79.3% of reviews are from current employees, so to emit 50 former-employee
reviews the actor reads roughly 240 rows (about 3 pages at page size 100). **This share was
measured on Google only — it is not a universal constant and will vary by employer.** The actor
stops reading pages when it has emitted `maxResults` rows, when Glassdoor reports it has reached
the last page, or after a hard budget of `ceil(maxResults / 100) + 10` pages — whichever comes
first — so a company with very few leavers will not loop forever; it returns what it found and
logs how many pages it read.

`current` and `any` modes are real server-side filters (`onlyCurrentEmployees: true` / left
unfiltered exactly like the base) — no client-side discard, no extra page reads.

#### Honest limits — `employmentTypes`

Only two Glassdoor `EmploymentStatusEnum` values are verified live: `REGULAR` (full-time) and
`PART_TIME`. Other values may exist for some employers, which is why this field is a **free text
list**, not a locked dropdown — three additional guesses (`CONTRACT`, `INTERN`, `FREELANCE`) were
considered and rejected because they have zero confirmed occurrences anywhere. If you type a value
Glassdoor doesn't recognise, the run log will say so instead of silently returning nothing.
Sending more than one value in the list was never tested live — behaviour on a multi-value list is
unverified.

#### `matching_reviews_count` — what it actually counts

`matching_reviews_count` is Glassdoor's own count for the filters **it** applied server-side — not
the number of rows this run delivered. In `current` mode it's the current-employee count. With
`employmentTypes` set, it's the contract-type count. In the default `former` mode, there is no
server-side former filter, so this number equals the *unfiltered* count (or the
`employmentTypes`-filtered count if you set one) — it is **not** the count of former-employee
reviews. Read it as "how many reviews Glassdoor matched for the filters it applied", never as "how
many reviews this run found".

#### Access note — Cloudflare warm-up

Glassdoor's content paths return HTTP 403 to a plain HTTP client on every request (measured: 11 of
11 routes, both TLDs, multiple UAs and proxy tiers) — this is a content-level Cloudflare challenge,
not an IP block. This actor runs a one-time browser warm-up per proxy tier
(`https://www.glassdoor.com/member/home/index.htm`) before making any `POST /graph` calls, and
carries the resulting cookies into the same HTTP session used for the rest of the run. The warm-up
browser runs non-headless (rendered under the container's own virtual display) — a headless browser
was measured to consistently get stuck on Cloudflare's interstitial, identically across every proxy
tier, while a non-headless one clears it reliably. Whether a plain HTTP client can hold that cleared
session across many requests over a long run was only proven inside a live browser, not over N
HTTP-only requests — treat this as a transport risk shared with the base actor's own request shape,
not something this variant claims to have fully solved; the warm-up retries once per proxy tier if
the clearance cookie doesn't land the first time.

### Output

All 29 base fields are unchanged (same names, order, meanings), including `is_current_job`,
`has_employer_response`, `length_of_employment`, `job_title`, `location`, `count_helpful`, and the
base's own (mislabelled, unrepaired) running-counter `reviews_count`. Three fields are appended:

```jsonc
{
  // ... all 29 base fields, unchanged ...
  "featured": false,

  "employment_type": "REGULAR",        // contract type Glassdoor recorded for this reviewer.
                                        // Verified live values: "REGULAR", "PART_TIME".
                                        // Can be null on older rows — never a substitute string.
  "matching_reviews_count": 48796,     // Glassdoor's own count for the filters IT applied — see note above
  "total_reviews_count": 70817         // Glassdoor's own total for this employer, before any filter
}
```

Both new counts are company/page-level and are written onto every row, the same way the base
already stamps `company_id` / `company_url` / `reviews_count` onto every row. The two
key-value-store writes (`company-{employer_id}` and the grouped `OUTPUT`) and the PPE event name
(`row_result`) are unchanged — the new fields ride inside the review dicts automatically.

### What is intentionally unchanged from the base (not bugs — known, preserved behaviour)

- `reviews_count` stays a running counter of rows emitted so far, not Glassdoor's true review
  total for the company (that's what the two new `*_reviews_count` fields are for).
- The base's `startItems` URL matching only recognises `EI_IE`-style Glassdoor URLs; company
  names/keywords are accepted as input but not resolved to a company.
- `applyDefaultCriteria: true` + `language: 'eng'` (both untouched) silently discard roughly 31% of
  a company's raw review corpus (Google: 70,817 → 48,796) before any of this variant's own
  filters apply — both counts are exposed in the output so you can see the gap.
- No claim is made anywhere about beating a "200 review" cap — no such cap exists at the API level;
  that is a UI-only limit some competitors market against.

### Costs

Same billing model as the base: one dataset row (`row_result` charged event) per delivered review.
`former` mode reads more Glassdoor pages per delivered row than `current`/`any` mode (see above),
but you are only charged for rows actually written to the dataset, not for pages read.

# Actor input Schema

## `startItems` (type: `array`):

List of Glassdoor company URLs (e.g., https://www.glassdoor.com/Overview/Working-at-IBM-EI\_IE354.11,14.htm), company names, or keywords.

## `maxResults` (type: `integer`):

Maximum number of reviews to collect per company (1-1000).

## `proxyConfiguration` (type: `object`):

Choose proxies. Actor starts with no proxy, then datacenter, then residential if blocked. Sticks with residential after fallback.

## `reviewerStatus` (type: `string`):

Choose which employees you want to hear from. Former keeps only reviews written by people who no longer work at the company — the exit record, and the default here. Current asks Glassdoor's own server for reviews from people still employed there. Any returns everyone, exactly as the base actor does. Note on how "former" works: Glassdoor offers a server-side filter for current employees only — there is no server-side "former only" switch — so this mode downloads reviews with no tenure filter and drops the current-employee ones as they arrive. It costs more pages per delivered review than the other two modes. On Google, roughly 1 review in 5 is written by a former employee, so expect the actor to read about five reviews for every one it keeps.

## `employmentTypes` (type: `array`):

Keep only reviews written by people on a particular kind of contract, and tag every delivered row with the kind Glassdoor recorded. Leave this empty to get every kind — the tag still lands on every row either way. Glassdoor's own values are written in capitals; the two confirmed on the live site are REGULAR (full-time staff) and PART\_TIME. Other values may exist on some employers, so this is a free list rather than a fixed menu — anything you type is passed straight to Glassdoor, and the run log will say so if a value is not recognised. This filter is applied by Glassdoor's server, so it also narrows the "matching\_reviews\_count" figure in the output.

## `sortBy` (type: `string`):

The order Glassdoor returns reviews in. DATE is the default here because a retention question is about this quarter's exits, not 2015's most-upvoted post. RELEVANCE is what the base actor uses. RATING returns the highest-rated first and RATING\_ASC the lowest-rated first. This changes the order reviews arrive in, not which reviews exist.

## Actor input object example

```json
{
  "startItems": [
    "https://www.glassdoor.com/Overview/Working-at-IBM-EI_IE354.11,14.htm",
    "https://www.glassdoor.com/Overview/Working-at-Google-EI_IE9079.11,17.htm"
  ],
  "maxResults": 50,
  "proxyConfiguration": {
    "useApifyProxy": false
  },
  "reviewerStatus": "former",
  "employmentTypes": [],
  "sortBy": "DATE"
}
```

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "startItems": [
        "https://www.glassdoor.com/Overview/Working-at-IBM-EI_IE354.11,14.htm",
        "https://www.glassdoor.com/Overview/Working-at-Google-EI_IE9079.11,17.htm"
    ],
    "proxyConfiguration": {
        "useApifyProxy": false
    },
    "employmentTypes": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapebase/glassdoor-reviews-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "startItems": [
        "https://www.glassdoor.com/Overview/Working-at-IBM-EI_IE354.11,14.htm",
        "https://www.glassdoor.com/Overview/Working-at-Google-EI_IE9079.11,17.htm",
    ],
    "proxyConfiguration": { "useApifyProxy": False },
    "employmentTypes": [],
}

# Run the Actor and wait for it to finish
run = client.actor("scrapebase/glassdoor-reviews-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "startItems": [
    "https://www.glassdoor.com/Overview/Working-at-IBM-EI_IE354.11,14.htm",
    "https://www.glassdoor.com/Overview/Working-at-Google-EI_IE9079.11,17.htm"
  ],
  "proxyConfiguration": {
    "useApifyProxy": false
  },
  "employmentTypes": []
}' |
apify call scrapebase/glassdoor-reviews-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,scrapebase/glassdoor-reviews-scraper"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/UggRueC5SiViWGMHl/builds/JoD9o6mRBZeYLMPBG/openapi.json
