# Glassdoor Jobs Scraper — Salaries, Skills & Companies (`logiover/glassdoor-jobs-scraper`) Actor

Scrape Glassdoor jobs by keyword and city, state or country: salary range (employer or Glassdoor estimate), company rating, skills, benefits, seniority, job type and posting date, plus optional company data. Splits big searches to reach thousands of jobs. Not affiliated with Glassdoor.

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

## Pricing

from $2.00 / 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.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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 Jobs Scraper — Salaries, Skills & Companies

> **Independent tool — not affiliated with, endorsed by or sponsored by Glassdoor, Inc.** "Glassdoor" is a trademark of its owner and is used here only to describe the public website this Actor reads.

Export **Glassdoor job listings in bulk** — with the **salary range** (employer-published or Glassdoor's estimate, with 10th/50th/90th percentiles), **company rating**, **skills**, **benefits**, **seniority**, **experience**, **education**, **job type**, **work setting** and **posting date** for every job. Search by job title or keyword in any **city, state or country** Glassdoor covers — the United States, the United Kingdom, Canada, Germany, India and more — or paste your own Glassdoor search URL.

Turn on **job details** and each row also gets the **full job description**, exact posting and expiry dates, **coordinates**, and the **employer card**: headquarters, employee count, revenue band, company type, industry, sector, founding year, website, CEO and seven Glassdoor **rating breakdowns** (culture, work/life balance, compensation, management, career opportunities, CEO approval, recommend-to-a-friend).

No login, no cookies, no API key.

### What does Glassdoor Jobs Scraper do?

Glassdoor's search only ever shows about **70 different jobs per search**, however many it claims to have: page 1 has 30 jobs, page 2 adds about 23 new ones, page 3 about 16 — and pages 5, 8, 12 or 40 return nothing you have not already seen. A plain page-by-page scraper therefore stops at ~70 jobs for "registered nurse in the United States" even though Glassdoor reports 435,177.

This Actor reads the **filter counts** Glassdoor prints next to every search and **splits a big search into slices** — each city, job function, industry, seniority level, job type, company size, posting window, salary band and sort order — and reads every slice as its own search. Each slice is another ~70-job window, and duplicates are removed by Glassdoor's job ID, so the slices add up to thousands of unique jobs instead of 70.

- 🔎 **Keyword × location grid** — every keyword is searched in every location you list.
- 🌍 **Any Glassdoor location** — cities ("Austin, TX", "London"), states/regions ("Texas", "California"), countries ("United States", "Germany", "Canada") or "Remote". Names are resolved by Glassdoor itself.
- 💰 **Salary data on ~75–95% of jobs** — min, median and max, currency, pay period, and whether the employer published it or Glassdoor estimated it.
- 🧠 **Structured requirements** — skills, seniority, years of experience, education, benefits, job type and work setting, as Glassdoor extracted them from the posting.
- 🏢 **Company firmographics** (optional job details) — HQ, size, revenue, industry, website, CEO, rating breakdowns.
- 🧹 **No duplicates** — one row per Glassdoor job ID per run.
- 🛡️ **Honest runs** — a run that cannot reach Glassdoor **fails with the reason** instead of finishing "successfully" with zero rows. Every run writes a `SUMMARY` record with the outcome and per-search counts.

### Who uses Glassdoor job data?

- **Recruiters and staffing agencies** — find every company hiring for a role in a metro, with salary and seniority, and prioritise accounts by hiring volume.
- **Salary benchmarking / compensation teams** — collect employer-published pay ranges and Glassdoor estimates per title, city and seniority level.
- **B2B sales and lead generation** — companies that are hiring are companies that are buying. Use company name, size, revenue, industry and website (job details) to build prospect lists by hiring signal.
- **Labor-market and economic research** — track demand for skills (Python, SQL, nursing, CDL…), remote-work share, benefits and pay over time.
- **Competitive intelligence** — monitor which roles and locations competitors are hiring for, week by week, with Glassdoor ratings alongside.
- **Job boards and aggregators** — feed fresh listings with structured salary, skills and benefits.

### How to use it

1. Enter one or more **job titles or keywords** (e.g. `data analyst`, `registered nurse`).
2. Enter **locations** — cities, states, countries or `Remote`. Leave empty for the whole United States.
3. Set **Max jobs** (0 = no limit) and keep **Split big searches** on *Standard* (or *Deep* for maximum coverage).
4. Optionally add filters (posted within, job type, seniority, remote only, Easy Apply only, minimum rating, minimum salary, company size) or switch on **Fetch job details**.
5. Click **Start**, then export the dataset as **JSON, CSV, Excel, XML or HTML**, or pull it through the API.

### Input examples

**Several roles across several cities:**

```json
{
  "keywords": [
    "data analyst",
    "data engineer"
  ],
  "locations": [
    "New York, NY",
    "Austin, TX",
    "Chicago, IL"
  ],
  "maxItems": 2000
}
```

**Country-wide, filtered, maximum coverage** (full-time nursing jobs posted in the last 7 days paying $80k+):

```json
{
  "keywords": [
    "registered nurse"
  ],
  "locations": [
    "United States"
  ],
  "remoteOnly": false,
  "postedWithinDays": "7",
  "jobType": "fulltime",
  "minSalary": 80000,
  "searchDepth": "2",
  "maxItems": 10000
}
```

**Your own Glassdoor search URL, with job details and company data:**

```json
{
  "startUrls": [
    {
      "url": "https://www.glassdoor.com/Job/london-accountant-jobs-SRCH_IL.0,6_IC2671300_KO7,17.htm"
    }
  ],
  "fetchDetails": true,
  "maxItems": 200
}
```

#### Main input options

| Option | What it does |
|---|---|
| `keywords` | Job titles / keywords. One search per keyword per location. |
| `locations` | Cities, states, countries or `Remote`. Empty = United States. |
| `startUrls` | Any Glassdoor job-search URLs, used as-is (your filters are kept). |
| `maxItems` | Maximum unique jobs per run. `0` = no limit. |
| `searchDepth` | `0` off (~70 jobs per search), `1` standard split (recommended), `2` deep split. |
| `maxPagesPerSearch` | Pages per search or slice (30 jobs each). 3 is the sweet spot — Glassdoor repeats jobs after page 3. |
| `postedWithinDays`, `jobType`, `seniority`, `remoteOnly`, `easyApplyOnly`, `minCompanyRating`, `minSalary`, `companySize`, `sortBy` | Glassdoor's own filters, applied to every search. |
| `fetchDetails` | Open every job page for the full description and company data (one extra page per job). |
| `concurrency` | Pages fetched in parallel (default 32, max 50). |
| `proxyConfiguration` | Keep the **UNBLOCKER** group — Glassdoor answers ordinary datacenter and residential proxies with a Cloudflare 403. |

### Output example

A real row from a test run (search results only):

```json
{
  "jobId": "1010196677363",
  "title": "Data Scientist II",
  "normalizedTitle": "data scientist",
  "company": "Scribd Inc.",
  "companyId": "354208",
  "companyRating": 2.9,
  "companyLogo": "https://media.glassdoor.com/sql/354208/scribd-inc-squareLogo-1772827903423.png",
  "location": "Los Angeles, CA",
  "city": "Los Angeles",
  "state": "CA",
  "country": "United States",
  "locationType": "city",
  "isRemote": false,
  "workSetting": "Hybrid",
  "jobTypes": "Full-time",
  "seniority": "Mid-level",
  "experience": "4 years",
  "education": "Bachelor's degree in statistics | Master's degree | Bachelor's degree in computer science | Bachelor's degree in data science",
  "skills": "Spark | SQL | Distributed systems | Natural language processing | APIs",
  "benefits": "Paid parental leave | Disability insurance | Health insurance | Dental insurance | Paid time off | Vision insurance",
  "otherHighlights": "AI models | Databricks | Big data projects | Statistics | PyTorch | NumPy | Generative models | Model deployment | Neural networks | …",
  "salaryText": "$118K - $150K (Employer provided)",
  "salaryMin": 118000,
  "salaryMedian": 134000,
  "salaryMax": 150000,
  "salaryCurrency": "USD",
  "salaryPeriod": "ANNUAL",
  "salarySource": "employer",
  "easyApply": false,
  "sponsored": false,
  "ageInDays": 92,
  "postedDate": "2026-07-10",
  "descriptionSnippet": "Bachelors or Masters in relevant quantitative discipline including but not limited to Statistics, Computer Science, Data Science, Artificial Intelligence or…",
  "jobUrl": "https://www.glassdoor.com/job-listing/data-scientist-ii-scribd-JV_IC1146821_KO0,17_KE18,24.htm?jl=1010196677363",
  "applyUrl": "https://www.glassdoor.com/partner/jobListing.htm?pos=116&ao=1136043&…&jobListingId=1010196677363",
  "searchKeyword": "data scientist",
  "searchLocation": "California, US",
  "searchSlice": null,
  "page": 1,
  "position": 16,
  "scrapedAt": "2026-10-10T18:34:00.114Z"
}
```

With **Fetch job details** on, each row additionally carries (real values from a test run):

```json
{
  "description": "Job Title: Senior Product Manager – Finance\n\nLocation: Seattle, Washington 98101 (Hybrid — 4 days onsite and 1 day remote per week)\n\nDuration: 04+month contract …",
  "datePosted": "2026-10-05",
  "validThrough": "2027-02-07",
  "employmentType": "CONTRACTOR",
  "latitude": 47.60639,
  "longitude": -122.33083,
  "companyHeadquarters": "Columbus, OH",
  "companySize": "51 to 200 Employees",
  "companyRevenue": "$25 to $100 million (USD)",
  "companyType": "Company - Private",
  "companyIndustry": "HR Consulting",
  "companySector": "Human Resources & Staffing",
  "companyFounded": 2009,
  "companyWebsite": "https://www.tailoredmanagement.com",
  "companyCeo": "Brad Beach",
  "ratingCareerOpportunities": 3.5,
  "ratingCompensationBenefits": 3.5,
  "ratingCultureValues": 3.7,
  "ratingSeniorManagement": 3.3,
  "ratingWorkLifeBalance": 3.9,
  "ceoApprovalPercent": 67,
  "recommendToFriendPercent": 61
}
```

### Data fields

#### Search-result fields (every row)

| Field | Type | Description |
|---|---|---|
| `jobId` | string | Glassdoor listing ID (the jl= value). Stable, use it to deduplicate across runs. |
| `title` | string | Title exactly as the employer posted it. |
| `normalizedTitle` | string | Glassdoor's standardised occupation for the posting, e.g. "data analyst" — handy for grouping. |
| `company` | string | Employer name. |
| `companyId` | string | Glassdoor employer ID. |
| `companyRating` | number | Overall Glassdoor rating of the employer (1–5). Empty when the employer has no rating yet. |
| `companyLogo` | string | URL of the employer's square logo. |
| `location` | string | Location as shown on the listing. |
| `city` | string | City part of the location. |
| `state` | string | State, province or region part of the location. |
| `country` | string | Country of the job, when it matches a country this run resolved. |
| `locationType` | string | city, state or country — how precisely the employer located the job. |
| `isRemote` | boolean | true when the job is listed as remote; empty when the posting does not say. |
| `workSetting` | string | Remote, Hybrid or On-site, when the posting states it. |
| `jobTypes` | string | Full-time, Part-time, Contract, Temporary, Internship, Permanent… (pipe-separated). |
| `seniority` | string | Entry level, Mid-level, Senior level… as extracted by Glassdoor. |
| `experience` | string | Years of experience the posting asks for, e.g. "5 years". |
| `education` | string | Degrees or qualifications mentioned, pipe-separated. |
| `skills` | string | Skills Glassdoor extracted from the posting, pipe-separated. |
| `benefits` | string | Benefits mentioned in the posting (health insurance, 401(k), PTO…), pipe-separated. |
| `otherHighlights` | string | Other requirements and responsibilities Glassdoor tagged in the posting, pipe-separated. |
| `salaryText` | string | Salary text as displayed, e.g. "$122K - $197K (Glassdoor est.)" or "(Employer provided)". |
| `salaryMin` | number | Lower end of the pay range (10th percentile for Glassdoor estimates), in salaryCurrency per salaryPeriod. |
| `salaryMedian` | number | Midpoint / median of the pay range. |
| `salaryMax` | number | Upper end of the pay range (90th percentile for Glassdoor estimates). |
| `salaryCurrency` | string | Currency of the salary figures, e.g. USD, GBP, EUR. |
| `salaryPeriod` | string | ANNUAL, MONTHLY, HOURLY… |
| `salarySource` | string | employer when the employer published the range, glassdoor_estimate when Glassdoor estimated it. |
| `easyApply` | boolean | Whether the job accepts Glassdoor Easy Apply. |
| `sponsored` | boolean | Whether the listing is a paid/sponsored placement. |
| `ageInDays` | number | Days since the job was posted, as Glassdoor reports it. |
| `postedDate` | string | Posting date (YYYY-MM-DD) derived from the age at scrape time. |
| `descriptionSnippet` | string | Short excerpt of the job description shown in search results. |
| `jobUrl` | string | Glassdoor job page. |
| `applyUrl` | string | Glassdoor's apply/redirect link for the job. |
| `searchKeyword` | string | Keyword of the search that found the job. |
| `searchLocation` | string | Location of the search that found the job. |
| `searchSlice` | string | Filter slice that surfaced the job when a big search was split (e.g. "city: Austin, TX"). Empty for the main search. |
| `page` | number | Results page the job was found on. |
| `position` | number | Position within the search results. |
| `scrapedAt` | string | When the row was collected (ISO 8601). |

#### Job-detail fields (with "Fetch job details")

| Field | Type | Description |
|---|---|---|
| `description` | string | Full job description text. |
| `datePosted` | string | Exact posting date from the job page. |
| `validThrough` | string | Date the listing is valid until. |
| `employmentType` | string | FULL_TIME, CONTRACTOR… from the job page. |
| `addressLocality` | string | City from the job page's structured address. |
| `addressRegion` | string | Region from the structured address. |
| `addressCountry` | string | Country from the structured address. |
| `latitude` | number | Job location latitude. |
| `longitude` | number | Job location longitude. |
| `experienceMonths` | number | Months of experience required, where stated. |
| `educationRequirement` | string | Education credential category, where stated. |
| `directApply` | boolean | Whether the job can be applied to directly. |
| `companyHeadquarters` | string | Employer headquarters. |
| `companySize` | string | Employee count band, e.g. "201 to 500 Employees". |
| `companyRevenue` | string | Annual revenue band. |
| `companyType` | string | Company - Private, Company - Public, Non-profit… |
| `companyIndustry` | string | Employer's primary industry. |
| `companySector` | string | Employer's sector. |
| `companyFounded` | number | Year the company was founded, where known. |
| `companyWebsite` | string | Employer website. |
| `companyCeo` | string | CEO name. |
| `companyReviewsUrl` | string | Employer's Glassdoor reviews page. |
| `companyOverviewUrl` | string | Employer's Glassdoor overview page. |
| `ratingCareerOpportunities` | number | Sub-rating 1–5. |
| `ratingCompensationBenefits` | number | Sub-rating 1–5. |
| `ratingCultureValues` | number | Sub-rating 1–5. |
| `ratingSeniorManagement` | number | Sub-rating 1–5. |
| `ratingWorkLifeBalance` | number | Sub-rating 1–5. |
| `ceoApprovalPercent` | number | Share of reviewers approving of the CEO. |
| `recommendToFriendPercent` | number | Share of reviewers who would recommend the employer. |

Sparse columns are sparse at the source, not lost: Glassdoor only tags seniority, experience, education, benefits and work setting when the posting states them (typically 30–80% of jobs), and only shows a salary when the employer published one or Glassdoor could estimate it (75–95% of jobs in our tests). Empty means "not stated", never "zero".

### How many results can I get?

Measured on the Apify platform on 2026-10-10:

| Run | Result |
|---|---|
| 1 keyword × 1 location, splitting **off** | ~70 unique jobs (page 1: 30 new, page 2: +23, page 3: +16, pages 5–40: +0) |
| "registered nurse" × United States, **standard** split | ~2,170 unique jobs before the deep level started |
| "registered nurse" × United States, **deep** split, max 3,000 | **3,000 unique jobs, 0 duplicates** (limit reached, still finding new jobs) |
| 3 keywords × 3 locations (US, Texas, London), max 1,500 | **1,500 unique jobs, 0 duplicates** |
| 4 keywords × 2 locations (US, California), max 1,200 | **1,200 unique jobs, 0 duplicates** |
| 2 keywords × 2 locations (Chicago, Texas), max 1,000 | **1,000 unique jobs, 0 duplicates in 44 seconds** |

**Tens of thousands of jobs:** combine many keywords with many locations (each keyword × location is its own search tree), use **Deep** splitting, set `maxItems` to `0` or a large number and give the run a longer timeout. Each slice adds up to ~70 new jobs; the run stops on its own when slices stop producing new jobs, when it reaches `maxItems`, or shortly before the run timeout (results are always saved).

### Speed and cost

- **Speed (measured on the platform, default settings):** **1,000 unique jobs in 44–51 seconds** (2 keywords × Chicago/Texas — the same search took 214 s before the speed update), with the **first rows in the dataset after ~3–9 seconds**. The prefilled example (50 jobs) finishes in about **7–15 seconds**.
- **Why it is fast:** 32 pages are fetched in parallel; a page that has not answered after 10 s is requested once more on a fresh connection and the first answer wins (the slower copy is cancelled), and the first page of every search is raced on two connections from the start; common locations need no lookup and the rest are looked up in parallel; the next page fetched is always the one expected to add the most new jobs; and the run stops the moment `maxItems` is reached. Rows are saved page by page, so you can watch them arrive.
- **With job details:** about 10–15 jobs per minute at default concurrency (one extra page per job) — raise `concurrency` for more.
- **Platform usage measured:** about **50–120 page requests and ≈ $0.07–0.17 of platform usage per 1,000 jobs** (fewer when you search several keywords/locations, more when you mine a single search very deeply); job details add about $1.70 per 1,000 jobs. Memory stays under ~250 MB.
- **Price:** see the Pricing tab — you pay per result (job) delivered.

### Tips

- **More jobs:** add locations (big cities of a state, states of a country) and related keywords ("nurse", "RN", "registered nurse"). Different search trees overlap less than you would expect.
- **Fresh jobs only:** `postedWithinDays: "1"` plus a daily schedule gives you a daily feed of new postings.
- **Salary research:** filter on `salarySource = employer` for published pay ranges only.
- **Company lead lists:** run with **job details**, then group by `company` — you get size, revenue, industry, website and HQ for every hiring company.
- **Remote work:** use `locations: ["Remote"]` or `remoteOnly: true` with a country.

### FAQ

**Do I need a Glassdoor account, cookies or an API key?**
No. The Actor reads public search pages only. Glassdoor has no public jobs API; this Actor is the practical alternative.

**Why does Glassdoor say 15,000 jobs but a normal search only gives ~70?**
Glassdoor's pagination repeats jobs after page 3. This Actor gets past that by splitting the search into filter slices (city, function, industry, seniority, date…) — see "How many results can I get?".

**Which countries are supported?**
Any location Glassdoor's own location search knows: US cities and states, the UK, Canada, Germany, France, India, Australia and more. Type the name as you would on glassdoor.com.

**Is the salary real or estimated?**
`salarySource` tells you: `employer` means the employer published the range; `glassdoor_estimate` means Glassdoor estimated it (shown as "Glassdoor est." on the site). `salaryMin`/`salaryMax` are the 10th/90th percentiles of the estimate.

**What happens if Glassdoor blocks the run?**
Glassdoor is behind Cloudflare; the Actor uses Apify's UNBLOCKER proxy group, which got through on every test run. Refused attempts are retried on a fresh exit, half-rendered pages are fetched again. If nothing can be fetched, the run **fails** with the reason — it never reports success with zero rows.

**Can I get the full job description?**
Yes — switch on **Fetch job details**. It also adds coordinates, exact dates and company data.

**Can I schedule it?**
Yes. Use Apify Schedules (e.g. daily with `postedWithinDays: "1"`) and deduplicate on `jobId` across runs.

**How do I export to Excel, Google Sheets or my database?**
Export the dataset as CSV/Excel/JSON from the Storage tab, use the Google Sheets / Make / Zapier / webhook integrations, or call the Apify API (examples below).

**How accurate is the data?**
Every value is taken from Glassdoor's own page data at scrape time; nothing is guessed. `postedDate` is derived from Glassdoor's "age in days" at scrape time.

**Does it collect personal data?**
No personal data about individuals is collected — only job postings and company information that employers publish. (The CEO name comes from the public employer card.)

**Is scraping Glassdoor legal?**
The Actor collects publicly available information without logging in or bypassing authentication. You are responsible for using the data in line with Glassdoor's terms, copyright and data-protection law (e.g. GDPR/CCPA) in your jurisdiction. If in doubt, consult a lawyer.

**Why did my run stop before `maxItems`?**
Either every slice was exhausted (check `SUMMARY.searches` for Glassdoor's reported totals per slice), or the run approached its timeout — raise the timeout or add keywords/locations.

### Use it via API

**JavaScript (apify-client):**

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

const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });
const run = await client.actor('logiover/glassdoor-jobs-scraper').call({
    keywords: ['data analyst'],
    locations: ['New York, NY', 'Austin, TX'],
    maxItems: 1000,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.length, items[0]);
```

**Python (apify-client):**

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("logiover/glassdoor-jobs-scraper").call(run_input={
    "keywords": ["registered nurse"],
    "locations": ["Texas"],
    "postedWithinDays": "7",
    "maxItems": 2000,
})
for job in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(job["title"], job["company"], job["salaryMin"], job["salaryMax"])
```

**REST (run synchronously and get items):**

```bash
curl -X POST "https://api.apify.com/v2/acts/logiover~glassdoor-jobs-scraper/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"keywords":["accountant"],"locations":["London"],"maxItems":200}'
```

### Related Actors

- [Indeed Jobs Scraper](https://apify.com/logiover/indeed-jobs-scraper) — the other big job board, for wider coverage.
- [LinkedIn Jobs Scraper](https://apify.com/logiover/linkedin-jobs-scraper) — LinkedIn job postings.
- [Company Firmographics Scraper](https://apify.com/logiover/company-firmographics-scraper) — revenue, headcount and competitors for the companies you find.
- [Greenhouse Job Board Scraper](https://apify.com/logiover/greenhouse-job-board-scraper) and [Ashby Job Board Scraper](https://apify.com/logiover/ashby-job-board-scraper) — every open role straight from company career pages.
- [Website Contact Scraper](https://apify.com/logiover/website-contact-scraper) — emails and phones from company websites (`companyWebsite`).

### Legal and trademark notice

This Actor is an independent tool and is **not affiliated with, endorsed by or sponsored by Glassdoor, Inc. or any of its subsidiaries**. All trademarks mentioned are the property of their respective owners. The Actor only accesses publicly available pages, does not log in and does not bypass authentication. Users are responsible for complying with applicable laws and the website's terms of use.

### Changelog

#### 2026-10-10 — faster

- 1,000 jobs in 44 s instead of 214 s (same search), first rows after ~6 s instead of ~18 s, and less than half the page requests.
- Slow pages are re-requested on a fresh connection after 10 s (first answer wins), the first page of each search is raced on two connections; default concurrency 32 (max 50).
- Common locations resolve instantly, the rest in parallel; the most productive pages are fetched first; the run stops as soon as the limit is reached; live progress in the status message.

#### 2026-10-10 — first release

- Keyword × location searches for any city, state or country Glassdoor knows, plus raw search URLs.
- Automatic splitting of big searches into filter slices (standard and deep), deduplicated by job ID.
- Salary (min/median/max, currency, period, source), skills, benefits, seniority, experience, education, job type, work setting.
- Optional job details: full description, dates, coordinates, company firmographics and rating breakdowns.
- Fails loudly when Glassdoor cannot be reached; `SUMMARY` record with outcome and per-search counts.

# Changelog

This Actor's version history is a separate document: https://apify.com/logiover/glassdoor-jobs-scraper/changelog.md

# Actor input Schema

## `keywords` (type: `array`):

One search per keyword, e.g. "data analyst", "registered nurse", "python developer". Each keyword is combined with every location.

## `locations` (type: `array`):

Cities ("Austin, TX", "London"), states ("Texas"), countries ("Germany", "Canada") or "Remote". Glassdoor resolves each name itself. Leave empty to search the whole United States.

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

Paste any Glassdoor job search URL (with your own filters). Used as-is, alongside the keyword searches.

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

Stop after this many unique jobs across all searches. 0 = no limit (the run ends when every slice is exhausted or the run times out).

## `searchDepth` (type: `string`):

Glassdoor only shows about 70 different jobs per search, however many it says it has. Splitting re-runs a big search per filter value (each city, industry, seniority level, posting date…) to reach far more of it.

## `maxPagesPerSearch` (type: `integer`):

Result pages to read per search or slice (30 jobs each). Glassdoor repeats earlier jobs after page 3, so 3 is the sweet spot.

## `postedWithinDays` (type: `string`):

Only jobs posted in this window.

## `jobType` (type: `string`):

Only this job type.

## `seniority` (type: `string`):

Only this seniority level.

## `remoteOnly` (type: `boolean`):

Only jobs Glassdoor lists as remote or work-from-home.

## `easyApplyOnly` (type: `boolean`):

Only jobs that accept Glassdoor Easy Apply.

## `minCompanyRating` (type: `string`):

Only employers rated at least this on Glassdoor.

## `minSalary` (type: `integer`):

Only jobs whose pay range reaches at least this annual amount, in the local currency. 0 = no filter.

## `companySize` (type: `string`):

Glassdoor's employer size bands.

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

Order of the main search results.

## `fetchDetails` (type: `boolean`):

Open each job page for the full description, exact dates, coordinates and the employer card: HQ, size, revenue, industry, website, CEO and 7 rating breakdowns. One extra page per job — much slower and costlier, so use it on targeted searches.

## `concurrency` (type: `integer`):

Pages fetched in parallel. 32 was the fastest stable setting in our tests; a page that is slow to answer is also re-requested on a fresh connection after 10 s and the first answer wins.

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

Glassdoor blocks ordinary datacenter and residential proxies (Cloudflare 403). Keep the UNBLOCKER group selected.

## Actor input object example

```json
{
  "keywords": [
    "data analyst",
    "software engineer"
  ],
  "locations": [
    "New York, NY"
  ],
  "startUrls": [],
  "maxItems": 50,
  "searchDepth": "1",
  "maxPagesPerSearch": 3,
  "postedWithinDays": "any",
  "jobType": "any",
  "seniority": "any",
  "remoteOnly": false,
  "easyApplyOnly": false,
  "minCompanyRating": "any",
  "minSalary": 0,
  "companySize": "any",
  "sortBy": "relevance",
  "fetchDetails": false,
  "concurrency": 32,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "UNBLOCKER"
    ]
  }
}
```

# Actor output Schema

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

All jobs collected in this run.

# 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 = {
    "keywords": [
        "data analyst",
        "software engineer"
    ],
    "locations": [
        "New York, NY"
    ],
    "maxItems": 50,
    "searchDepth": "1",
    "fetchDetails": false,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "UNBLOCKER"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("logiover/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 = {
    "keywords": [
        "data analyst",
        "software engineer",
    ],
    "locations": ["New York, NY"],
    "maxItems": 50,
    "searchDepth": "1",
    "fetchDetails": False,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["UNBLOCKER"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("logiover/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 '{
  "keywords": [
    "data analyst",
    "software engineer"
  ],
  "locations": [
    "New York, NY"
  ],
  "maxItems": 50,
  "searchDepth": "1",
  "fetchDetails": false,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "UNBLOCKER"
    ]
  }
}' |
apify call logiover/glassdoor-jobs-scraper --silent --output-dataset

```

## MCP server setup

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