# Glassdoor Jobs Scraper (`s-r/glassdoor-jobs-scraper`) Actor

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

## Pricing

Pay per event

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?

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 Jobs Scraper

Search Glassdoor and get the postings back as rows: title, employer, that employer's Glassdoor rating, location, and **the salary range with a note saying who produced it**.

No login, no cookie, no API key, no browser.

### What you get

- **Salary on most rows**, parsed into `salary_min`, `salary_max` and a currency rather than left as a string
- **Where the salary came from.** `salary_source` says "Employer provided" or "Glassdoor est.", and `salary_is_estimate` is the boolean. In testing, 68 of 80 salaried rows were the employer's own published figure and 12 were Glassdoor's model output. Those are different kinds of number, and averaging them together quietly turns a salary dataset into half opinion
- **The employer's Glassdoor rating** on the same row as the job, which is the pairing Glassdoor exists for
- **How many jobs actually match**, per search. A search returns 30 rows out of 58.544, and `totalAvailablePerSearch` tells you that instead of letting you assume 30 is the answer
- **Location, posting age, Easy Apply flag and the description snippet**
- **Many searches per run.** Pass a list of terms and each contributes its own 30, deduplicated on job id

### One thing worth knowing about the data

The obvious source on the page is a decoy. Glassdoor ships a JSON-LD `ItemList`
of 30 jobs that looks like the clean structured feed, and it carries only a name
and a URL. Salary, employer, rating and location exist only in the rendered
cards, which is what this Actor reads.

No login, no API key and no browser are needed on your side.

### Input

| Field | Type | Required | Default | What it does |
|---|---|---|---|---|
| `queries` | array | one of the two | `["software engineer"]` | One search per term, 30 jobs each |
| `urls` | array | one of the two | – | Glassdoor search URLs, for location-filtered searches |
| `limit` | integer | no | `200` | Total jobs to return, 1 to 600 |
| `retries` | integer | no | `3` | Retry attempts per search |

### Output

```json
{
  "position": 2,
  "job_id": "1010249450590",
  "url": "https://www.glassdoor.com/job-listing/ai-systems-engineer-...",
  "title": "AI Systems Engineer - DevOps & Observability Manager",
  "employer": "EY",
  "employer_rating": 3.6,
  "location": "Atlanta, GA",
  "salary_text": "$126K - $262K (Employer provided)",
  "salary_min": 126000.0,
  "salary_max": 262000.0,
  "salary_currency": "$",
  "salary_source": "Employer provided",
  "salary_is_estimate": false,
  "posted_age": "20d",
  "easy_apply": false,
  "snippet": "Design and operate observability platforms across...",
  "query": "software engineer"
}
```

### Use cases

**Salary benchmarking you can defend.** Filter to `salary_is_estimate: false` and you have only figures employers published themselves. That is the subset worth quoting in a compensation review; mixing in Glassdoor's estimates makes the number softer than it looks.

**Tracking who is hiring and what they pay.** Group by `employer` and watch `salary_min` over repeated runs. A company raising its posted range is competing for the same people you are.

**Reputation-weighted job sourcing.** `employer_rating` sits on every row, so a candidate-facing product can rank openings by what employees say rather than by posting date.

**Market sizing before you commit.** `totalAvailablePerSearch` returns Glassdoor's own count per term. Comparing 58.544 software engineer roles against 11.125 data analyst roles is a market read that costs one run.

**Wide sweeps.** Because each search is capped at 30, a list of twenty specific terms beats one broad term. "Senior Rust engineer" returns a more useful 30 than "engineer" does.

### How it compares

| | this actor | `valig/glassdoor-jobs-scraper` | `orgupdate/glassdoor-jobs-scraper` |
|---|---|---|---|
| Per 1.000 jobs | **$4,00** | **free** | $4,00 |
| Actor-start fee | $0,002 | none | $0,002 |
| Salary parsed into numbers | **yes** | not stated | not stated |
| Employer-provided vs estimated salary | **yes** | no | no |
| Employer rating on the job row | **yes** | not stated | not stated |
| Result count per search | **yes** | no | no |
| Monthly users | new | **1.408** | 159 |

Honest about the other side, and it is a big other side: **the category leader is free.** `valig` has 1.408 monthly users, charges neither a start fee nor a per-row rate, and no price competes with that. The reason to pay for this one is the salary provenance and the employer rating, not the cost. If those do not matter to you, use the free one.

### Pricing

Two events. `run_start` costs $0,0020 per run. `job` costs $0,0040 per job written to the dataset, which is $4,00 per 1.000. Searches that are refused never reach the dataset and are never billed.

### Limits and gotchas

- **30 jobs per search, and there is no page two.** Five URL forms were tested (`_IP2`, `?p=2`, `?pageNo=2`, the `jobs.htm` query form and the plain page) and every one returns the same first job. Glassdoor loads more results through a button that fires its own request. Add search terms, not pages.
- **`locKeyword` in a URL is ignored.** A search for "Austin" that way silently returns United States results. For a location-filtered search, pick the location on the site and paste the resulting URL into `urls`; those carry a location id and do work.
- **Salary is missing on roughly one row in nine.** Those postings simply do not show one.
- **Employer rating is missing on about one row in five**, usually for companies with too few reviews.
- **A 403 means the identity was rejected**, not that the search was bad. It is reported as `forbidden` with that explanation rather than as an empty result.
- **US results by default.** Results are US-based. Other Glassdoor domains are not covered.

### FAQ

**Can I scrape Glassdoor without a browser?**
Yes, that is what this actor does. It needs no browser, no cookies and no solver.

**Why only 30 jobs per search?**
Because Glassdoor does not paginate its HTML. The fix is more searches, and `queries` takes a list for exactly that reason.

**How do I search a specific city?**
Pick the city on Glassdoor, then paste the URL from your browser into `urls`. Location keywords passed as plain query parameters are ignored by the site.

**Is the salary the real salary?**
It is what Glassdoor printed. `salary_source` tells you whether the employer published it or Glassdoor estimated it, which is the distinction that matters.

**Does it get company reviews?**
No, this reads job search results. Reviews are a different surface.

### Related Actors

- [LinkedIn Jobs Scraper](https://apify.com/s-r/linkedin-jobs-scraper) — job postings with full descriptions and applicant counts
- [Indeed Jobs Scraper](https://apify.com/s-r/indeed-jobs-scraper) — the same job for Indeed
- [StepStone Scraper](https://apify.com/s-r/stepstone-scraper) — German-speaking job market

# Actor input Schema

## `queries` (type: `array`):

One search per term. Glassdoor returns 30 jobs per search and does not paginate, so more terms is how you get more jobs.

## `urls` (type: `array`):

Glassdoor search URLs to read as well. Use these for location-filtered searches: pick the location on the site and paste the URL from your browser.

## `limit` (type: `integer`):

How many jobs to return in total, 1 to 600. Each search contributes about 30.

## `retries` (type: `integer`):

Retry attempts per search, each with a rotated TLS fingerprint.

## Actor input object example

```json
{
  "queries": [
    "software engineer",
    "data analyst"
  ],
  "urls": [
    "https://www.glassdoor.com/Job/austin-software-engineer-jobs-SRCH_IL.0,6_IC1139761_KO7,24.htm"
  ],
  "limit": 200,
  "retries": 3
}
```

# Actor output Schema

## `results` (type: `string`):

One row per job posting, with its salary range and where that figure came from.

## `summary` (type: `string`):

Jobs returned, searches run, how many jobs Glassdoor says exist per search, and the split between employer-published and Glassdoor-estimated salaries.

## `errors` (type: `string`):

Per-search failures with a code and a redacted message.

# 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 = {
    "queries": [
        "software engineer"
    ],
    "limit": 200,
    "retries": 3
};

// Run the Actor and wait for it to finish
const run = await client.actor("s-r/glassdoor-jobs-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 = {
    "queries": ["software engineer"],
    "limit": 200,
    "retries": 3,
}

# Run the Actor and wait for it to finish
run = client.actor("s-r/glassdoor-jobs-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 '{
  "queries": [
    "software engineer"
  ],
  "limit": 200,
  "retries": 3
}' |
apify call s-r/glassdoor-jobs-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,s-r/glassdoor-jobs-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/tEu7vxfLVtYuXlHvK/builds/0sWIlo700hJSzMGfj/openapi.json
