# Glassdoor Scraper (`mlg14/glassdoor-scraper`) Actor

Scrape public employee reviews with ratings, pros, cons, dates, job details, and employer aggregates. Filter by rating, language, keyword, or date and collect beyond the visible review slice.

- **URL**: https://apify.com/mlg14/glassdoor-scraper.md
- **Developed by:** [MLG Data](https://apify.com/mlg14) (community)
- **Categories:** Jobs, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$3.80 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Glassdoor Scraper

Scrape Glassdoor employee reviews from public employer pages and export ratings, pros, cons, dates, job context, and employer-level scores to JSON or CSV. This Glassdoor API alternative accepts employer URLs, paginates beyond the small review sample shown on a page, and lets you narrow results by rating, language, keyword, and date.

The actor returns one flat record per review. A verified run collected 50 recent reviews from one employer URL, with complete identifiers, timestamps, headlines, pros, cons, and overall ratings. The collection limit is yours to set; the actor requests up to 100 reviews at a time from the public reviews endpoint.

### What data can you extract from Glassdoor?

Each dataset row represents one public employee review. Fields remain present when their values are unavailable, so a CSV export keeps stable columns. A missing value is `null`; a zero rating or vote count remains numeric zero where the source supplies it. Ratings for specific workplace dimensions use the source's one-to-five scale when provided.

| Field | Description | Example |
|---|---|---|
| `reviewId` | Unique public review identifier | `105742819` |
| `reviewUrl` | Direct review URL when the input has a review-page slug | A review page URL |
| `sourceUrl` | Employer URL supplied in the input | A Reviews tab URL |
| `companyId` | Employer identifier from the page URL and record | `9079` |
| `companyName` | Employer display name | Employer name |
| `companyLogoUrl` | Employer logo address | Image URL |
| `companyReviewCount` | All reviews reported for that employer | `71231` |
| `companyOverallRating` | Employer's aggregate overall score | `4.4` |
| `companyRecommendToFriendRating` | Employer recommendation ratio, zero to one | `0.88` |
| `reviewDateTime` | Review publication timestamp | `2026-09-25T22:31:08.583` |
| `summary` | Review headline | `Good work-life balance, but salary could improve` |
| `pros` | Positive review text | `Work life balance is great` |
| `cons` | Negative review text | `Salary not competitive as peers` |
| `advice` | Advice to management, if supplied | `null` |
| `ratingOverall` | Overall score for this review | `5` |
| `ratingWorkLifeBalance` | Work-life balance score | `5` |
| `ratingCultureAndValues` | Culture and values score | `5` |
| `ratingDiversityAndInclusion` | Diversity and inclusion score | `5` |
| `ratingCareerOpportunities` | Career opportunities score | `5` |
| `ratingCompensationAndBenefits` | Compensation and benefits score | `4` |
| `ratingSeniorLeadership` | Senior leadership score | `5` |
| `ratingCeo` | CEO approval response | `APPROVE` |
| `ratingBusinessOutlook` | Business outlook response | `POSITIVE` |
| `ratingRecommendToFriend` | Recommendation response | `POSITIVE` |
| `jobTitle` | Reviewer's public job title, when present | `null` |
| `location` | Reviewer's public location, when present | `null` |
| `employmentStatus` | Employment type attached to the review | `REGULAR` |
| `isCurrentJob` | Whether the reviewer marked the role as current | `true` |
| `lengthOfEmployment` | Reported employment length in years | `6` |
| `countHelpful` | Helpful votes | `0` |
| `countNotHelpful` | Not-helpful votes | `0` |
| `employerResponse` | Public reply from the employer, if present | `null` |
| `employerResponseDate` | Timestamp of that reply, if present | `null` |

The employer aggregates are attached to each row to make spreadsheet filtering and grouping simple. They describe the employer at collection time, while `ratingOverall` and the other `rating...` fields describe the individual review. Do not average the repeated employer aggregate fields across rows as though they were independent reviews.

### How to scrape Glassdoor

1. Open a public employer Reviews page and copy its URL. The address must contain the employer's `E` number. Other employer tab URLs containing that identifier are accepted, though a Reviews URL also supplies the slug used for a direct `reviewUrl`.
2. Paste one or more URLs into `startUrls`. Set `maxReviewsPerCompany` for the number of reviews you want from each URL, and set `maxItems` for a firm total across the run.
3. Choose a three-letter review language. Optionally set an overall star rating, a search keyword, or an inclusive starting date.
4. Run the actor. Each matching review is saved as its own dataset row. Download the dataset as JSON or CSV, or read it through the dataset API.

The result order is newest first. A small run therefore captures the latest matching reviews, useful for regular monitoring. If an employer has fewer matching reviews than the requested limit, the dataset contains only the available matches.

### Input

| Parameter | Type | Default | Description |
|---|---|---|---|
| `startUrls` | URL list | Example employer URL | Public employer URLs containing an `E` number. The Reviews tab gives the most complete links in output. |
| `maxReviewsPerCompany` | Integer | `100` | Maximum reviews for each employer URL. Set `0` to continue through all accessible pages. |
| `maxItems` | Integer | `100` | Maximum saved reviews across the whole run. Set `0` for no total cap. |
| `language` | String | `eng` | Three-letter language code used by the source, such as `eng` or `fra`. |
| `rating` | Integer | `0` | Overall star rating from `1` to `5`. Zero includes all ratings. |
| `keyword` | String | Empty | Server-side text search across public review content. |
| `reviewsStartDate` | Date | Empty | Inclusive `YYYY-MM-DD` cutoff for reviews newer than or on that date. |
| `proxyConfiguration` | Object | Enabled | Optional network configuration. The actor begins with datacenter routing and can escalate after blocks. |

A Reviews URL has the form `https://www.glassdoor.com/Reviews/Employer-Reviews-E12345.htm`. Replace the path and employer ID with a real public employer URL. The actor uses the numeric ID to request reviews; the readable portion of a Reviews URL is used only to construct direct review links. The URL itself is retained in `sourceUrl` for traceability.

This example requests recent one-star reviews and stops after 50 items. The example URL shows the expected shape; use an actual employer URL for a run:

```json
{
  "startUrls": [
    { "url": "https://www.glassdoor.com/Reviews/Employer-Reviews-E12345.htm" }
  ],
  "maxReviewsPerCompany": 50,
  "maxItems": 50,
  "language": "eng",
  "rating": 1,
  "reviewsStartDate": "2026-01-01"
}
```

For a broad collection, raise both item limits. Raising only `maxItems` will still stop at `maxReviewsPerCompany` for a single employer. Conversely, a large per-employer limit cannot override a smaller total limit. With several input URLs, collection proceeds in input order until the total limit is reached.

### Output example

These values come from a real 50-review verification run. Employer name and identifying URLs are omitted from the excerpt; all shown review values are unchanged. The full row also includes the source and direct review URLs, logo URL, remaining rating dimensions, and reply fields.

```json
{
  "reviewId": 105742819,
  "companyId": 9079,
  "companyReviewCount": 71231,
  "companyOverallRating": 4.4,
  "companyRecommendToFriendRating": 0.88,
  "reviewDateTime": "2026-09-25T22:31:08.583",
  "summary": "Good work-life balance, but salary could improve",
  "pros": "Work life balance is great",
  "cons": "Salary not competitive as peers",
  "advice": null,
  "ratingOverall": 5,
  "ratingWorkLifeBalance": 5,
  "ratingCultureAndValues": 5,
  "ratingCompensationAndBenefits": 4,
  "ratingCeo": "APPROVE",
  "ratingBusinessOutlook": "POSITIVE",
  "ratingRecommendToFriend": "POSITIVE",
  "employmentStatus": "REGULAR",
  "isCurrentJob": true,
  "lengthOfEmployment": 6,
  "countHelpful": 0,
  "countNotHelpful": 0
}
```

In the verification run, all 50 rows had a review ID, employer ID and name, timestamp, headline, pros, cons, and overall rating. Job title was populated in 84% of rows and location in 82%. Those fill rates describe that one sample, not a promise for every employer or filter. Individual reviews may omit a role, location, advice, sub-rating, or employer response.

### Use cases

- **Employer reputation tracking:** Collect the newest reviews on a schedule and compare workplace ratings and written feedback over time. Retain `reviewId` to identify records already seen in earlier runs.
- **Recruiting research:** Read public pros, cons, work-life balance, and leadership scores alongside job titles and locations when those details are available.
- **Workplace comparisons:** Export several employer URLs, then group by `companyId` to compare recent review volume, recurring themes, and rating distributions.
- **Management feedback review:** Search for a specific topic with `keyword`, then inspect the original pros, cons, and advice rather than relying on a summary alone.
- **Regional review samples:** Run the same employer ID with different supported three-letter language codes to compare public feedback by language. Keep these datasets distinct because source coverage can vary.
- **Survey preparation:** Use public review topics to prepare questions for interviews or internal surveys. Treat anonymous reviews as individual opinions rather than verified statements about every workplace.

### How much does it cost to scrape Glassdoor?

The actor is configured at **$3.80 per 1,000 delivered review rows**, or **$0.0038 per row**. The charge is tied to saved results, so the final number of rows determines the result charge. The `maxItems` and `maxReviewsPerCompany` settings are the simplest way to bound it.

| Delivered reviews | Result charge |
|---:|---:|
| 50 | $0.19 |
| 1,000 | $3.80 |
| 5,000 | $19.00 |

For example, five employers with a per-employer limit of 100 can produce at most 500 rows. If all five have enough matching reviews and `maxItems` is at least 500, the result charge is $1.90. A date or keyword filter may produce fewer rows and a lower result charge. Account-level platform charges, if applicable to your plan, are separate from this row calculation.

### Tips for best results

Start with a moderate limit and inspect the dataset before scaling up. A 50-review verification run finished successfully and required one 100-record page request; the actor saved only the requested 50 rows. For large historical collections, increase both limits and expect more pages. Each request asks for up to 100 reviews. The endpoint was verified to return distinct batches at pages 1, 3, 4, and 100, well beyond a 200-review display slice.

Use a Reviews URL when possible. It gives `reviewUrl` the readable employer slug needed for a direct review page. If you supply another employer tab URL with a usable `E` number, the review data still resolves, but `reviewUrl` can be `null`; `sourceUrl` and `reviewId` remain available. Keep the original input URL if you intend to trace results back to a specific research list.

Set `language` deliberately. The tested default is `eng`, and a separate `fra` request returned reviews. An `all` value did not behave as a combined-language export in probing, so collect additional languages with separate runs rather than assuming a single request merges them. Language coverage differs by employer. Empty or sparse language slices can return fewer items than expected.

For recent changes, use `reviewsStartDate` with a small item limit. Results arrive newest first, and the actor stops once it reaches a review older than the cutoff. For a topic search, use `keyword` to apply the source's text filter before pagination. For a rating slice, set `rating` from 1 to 5; the actor sends the source's corresponding rating token. Combining a narrow keyword, a rating, and a recent date can reduce the result count sharply.

Use `reviewId` as the stable key when merging scheduled exports. The actor skips duplicate IDs it encounters within each employer collection. If your own workflow combines runs or languages, perform a second deduplication by `reviewId` after export. Keep `reviewDateTime` as a timestamp rather than converting it immediately to a local calendar day; time-zone interpretation can affect daily trend charts.

### Limits

The actor collects **public employee reviews**. It does not extract job vacancies, salary reports, interview stories, benefits, or a complete employer profile. Those sections have different records and are outside this actor's input and output contract. It does not log in, request private profiles, or collect information that requires an account. Some public fields, especially job title, location, advice, detailed ratings, and employer replies, can be absent.

Direct requests to the main review site were blocked during discovery, and a page route displayed a bot-detection login interstitial. The public reviews endpoint on an alternate site host returned structured records from the remote network. The actor starts with datacenter routing and the shared fetch layer can rotate sessions, use a browser challenge step, and escalate the proxy group if blocked. Access can change without notice; a run may fail if the site changes its endpoint or introduces a new gate.

A request for page 100 succeeded during probing. That establishes collection beyond the visible sample, but it does not establish that every historical review can always be retrieved. Very large runs have not been exhaustively validated across every employer or language. The source may alter page sizes, sort behavior, counts, or filtering. The actor stops when a page is short, the reported filtered count is reached, the date cutoff is passed, or a user limit is met.

The date cutoff uses the date portion of `reviewDateTime` and is inclusive. The `keyword` filter follows the site's matching behavior; it should not be treated as an exact phrase search. Employer aggregate counts and ratings are snapshots returned with each page. They can change between runs, and the number of reviews delivered may be much lower than the employer's reported total when limits or filters apply.

### Use with automated workflows

Two requests that work well in a scheduled workflow are: “Collect the 100 newest English reviews for this public employer URL and export the dataset as JSON,” and “Collect one-star reviews since the start of the quarter, then group the resulting pros and cons by employer ID.” Supply the real URL, limit, language, rating, and date as structured inputs. Use review IDs to compare runs rather than assuming row order alone identifies new material.

### FAQ

#### Is scraping public reviews legal?

Public availability does not settle every legal question. Check the site's terms and the rules that apply in your location and use case. Respect privacy and data protection obligations, especially if review text could identify an individual. Do not use anonymous opinions as verified personal facts.

#### Do I need to provide proxies?

No proxy settings are required in a standard run. The actor creates network routing automatically. If a request is blocked, the shared fetch layer can retry, rotate its session, and escalate its proxy group. You can provide `proxyConfiguration` if your workspace needs a specific setup.

#### How fast is a run?

Speed depends on the number of pages, filters, proxy behavior, and site response time. The 50-review verification run completed successfully after deployment. Because each page asks for up to 100 rows, a modest run can finish with one review request, while large historical collections require many requests and take longer.

#### Can I schedule monitoring runs?

Yes. Schedule the same input at a chosen interval, use newest-first output, and compare `reviewId` values with your previous dataset. A recent `reviewsStartDate` can shorten the collection. The actor does not keep a cross-run database of previously seen reviews; your monitoring workflow should store the IDs it has processed.

#### Can I export to a spreadsheet?

Yes. Download the dataset as CSV and open it in a spreadsheet, or connect through the dataset API. Each output field is flat, so ratings, dates, and text can be filtered without unpacking nested objects. Preserve review IDs as identifiers, not measurements.

#### Why are some fields empty?

The source does not require every reviewer to provide a job title, location, advice, or every dimension score. Employers may not have replied to a review. These appear as `null` instead of invented values. Check fill rates on your own employer and language sample before relying on a field in a report.

#### Does this collect every review for a large employer?

Set both item limits to zero to continue through accessible pages, or use explicit limits for a bounded run. Pagination was observed well past 200 reviews, including page 100. Complete historical retrieval for every employer was not verified, and site changes can affect access or filtering.

### Integrations

Use the dataset API to read results from a script or reporting pipeline. Webhooks can trigger downstream processing after a run, and workflow automation services can move CSV or JSON rows into a database or spreadsheet. For scheduled trend reports, store each run time with the dataset and merge on `reviewId` so the same review is not counted twice.

### Support

Open an issue on the Issues tab; we reply within 24h and add fields on request.

# Actor input Schema

## `startUrls` (type: `array`):

Public employer Reviews URLs containing an E-number. Other employer tab URLs with an employer ID are also accepted.

## `maxReviewsPerCompany` (type: `integer`):

Maximum reviews for each employer URL. Set to 0 for all accessible pages.

## `maxItems` (type: `integer`):

Stop after this many reviews across all employer URLs. Set to 0 for no total limit.

## `language` (type: `string`):

Three-letter review language code used by the public reviews endpoint, such as eng or fra.

## `rating` (type: `integer`):

Return reviews with this overall rating. Zero includes every rating.

## `keyword` (type: `string`):

Optional server-side text search across public review content. Leave empty for all reviews.

## `reviewsStartDate` (type: `string`):

Optional inclusive date cutoff in YYYY-MM-DD format. Reviews are sorted newest first.

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

Proxy configuration. The actor starts with datacenter addresses and escalates after blocks.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://www.glassdoor.com/Reviews/Microsoft-Reviews-E1651.htm"
    }
  ],
  "maxReviewsPerCompany": 20,
  "maxItems": 20,
  "language": "eng",
  "rating": 0,
  "keyword": "",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `reviews` (type: `string`):

Scraped public reviews in the default dataset.

# 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 = {
    "startUrls": [
        {
            "url": "https://www.glassdoor.com/Reviews/Microsoft-Reviews-E1651.htm"
        }
    ],
    "maxReviewsPerCompany": 20,
    "maxItems": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("mlg14/glassdoor-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 = {
    "startUrls": [{ "url": "https://www.glassdoor.com/Reviews/Microsoft-Reviews-E1651.htm" }],
    "maxReviewsPerCompany": 20,
    "maxItems": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("mlg14/glassdoor-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 '{
  "startUrls": [
    {
      "url": "https://www.glassdoor.com/Reviews/Microsoft-Reviews-E1651.htm"
    }
  ],
  "maxReviewsPerCompany": 20,
  "maxItems": 20
}' |
apify call mlg14/glassdoor-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,mlg14/glassdoor-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/zGfN7FAA0OxSc0FbF/builds/qXpg7arA2ffrFzPOz/openapi.json
