How to Scrape Glassdoor Company Reviews and Salaries in 2026 (BFF GraphQL, DataDome, and Proxies)

A practical guide for HR-analytics and labor-market data engineers: what Glassdoor data is public vs login-walled, how DataDome and Cloudflare bot defenses work, and how to collect truncated review nodes through the BFF GraphQL endpoint with rotating residential proxies.

How to Scrape Glassdoor Company Reviews and Salaries in 2026 (BFF GraphQL, DataDome, and Proxies)

Legal caveat: This guide covers only publicly accessible, non-login-walled data on Glassdoor. Scraping behind an authentication wall, circumventing access controls, or collecting personally identifiable employee data may violate Glassdoor's Terms of Service, the Computer Fraud and Abuse Act (CFAA) in the United States, and the General Data Protection Regulation (GDPR) in the European Union. Always consult legal counsel before deploying a scraper at scale, and prefer official data partnerships or APIs where available.

If you build HR-analytics pipelines or labor-market intelligence products, you have probably looked at Glassdoor as a source of employer reviews and compensation benchmarks. The challenge is that Glassdoor has hardened its anti-bot stack significantly since 2023, combining DataDome's behavioral detection with Cloudflare Bot Management. This guide explains how to scrape Glassdoor company reviews and salary data in 2026 — specifically, what is publicly reachable, how the BFF GraphQL endpoint works, and why rotating residential proxies are the difference between a 5% success rate and a 90% success rate.

How to Scrape Glassdoor Company Reviews and Salaries in 2026: What's Actually Public

Before writing any code, you need to understand the boundary between public and gated content. Glassdoor's site structure splits data into two tiers:

  • Public (no login required): Company overview pages at /Overview/Working-at-<company>-EI_IE<id>.htm, truncated review listings at /Reviews/<company>-Reviews-E<id>.htm, and the aggregate rating summary. Each truncated review node typically includes ratingOverall, a shortened pros and cons snippet, jobTitle, and a date. Full review text requires clicking through, which may trigger a login prompt depending on the page.
  • Login-walled (gated): Full salary breakdowns by role, location, and years of experience; individual interview questions; and the complete text of reviews beyond the truncated preview. Glassdoor intentionally gates this content to drive account creation.

The practical implication: you can build a dataset of Glassdoor reviews at the summary level without authentication, but Glassdoor salary data in its detailed form is not publicly accessible. Any scraper that attempts to bypass the login wall is operating in legally risky territory and is outside the scope of this guide.

Glassdoor's Anti-Bot Stack: DataDome + Cloudflare Bot Management

Glassdoor layers two enterprise bot-mitigation systems. Understanding both is essential to building a reliable Glassdoor reviews scraper.

DataDome: Behavioral and IP Scoring

DataDome is a real-time bot detection service that evaluates each request using IP reputation, TLS fingerprint, header consistency, and behavioral signals (mouse movements, request cadence). DataDome is particularly aggressive about datacenter IP ranges — a request from an AWS us-east-1 IP has a dramatically higher chance of being challenged than one from a residential ISP in the same city. This is why residential proxies matter: they shift your IP reputation from "known datacenter" to "plausible human user."

Cloudflare Bot Management: JS Challenge and TLS Fingerprinting

Cloudflare's bot management adds a JavaScript challenge page that expects the client to execute JS and return a computed token. It also inspects the TLS ClientHello — the cipher suites, extensions, and ALPN protocols your client offers — and compares them against known browser profiles. A standard Python requests call produces a TLS fingerprint that does not match any real browser, so Cloudflare flags it immediately.

This is where curl_cffi comes in. The curl_cffi library wraps libcurl with BoringSSL and lets you impersonate Chrome's exact TLS fingerprint, including the ClientHello byte sequence. This is not a nice-to-have; it is a hard requirement for any request to succeed past Cloudflare on Glassdoor in 2026.

The BFF GraphQL Endpoint: Structured Review Data Without HTML Parsing

Glassdoor renders review pages server-side for SEO, but the interactive "load more reviews" button on the page fires a POST request to an undocumented BFF (Backend-for-Frontend) GraphQL endpoint. This endpoint returns structured JSON, which is far more reliable to parse than HTML.

The endpoint lives at a path like /graph or /bff/ (the exact prefix has shifted between deploys — check your browser's network tab on a live review page). The request is a standard GraphQL POST:

  • Method: POST
  • Content-Type: application/json
  • Required header: gd-csrf-token — a token extracted from the initial HTML page's embedded JavaScript or meta tags. Without it, the endpoint returns a 403.
  • Body: A GraphQL query string and variables object including the employer ID and a pagination cursor.

The response contains review nodes with fields like ratingOverall, pros, cons, jobTitle, reviewDateTime, and a cursor for the next page. This is the cleanest way to collect truncated review data at scale.

ProxyHat Setup: Residential Proxies for DataDome Evasion

Because DataDome scores IP reputation heavily, using datacenter proxies will get you blocked within 20-50 requests on most Glassdoor pages. Rotating residential proxies distribute your requests across real ISP-assigned IP addresses, keeping each IP's request count low enough to stay under DataDome's per-IP threshold.

ProxyHat's residential gateway is at gate.proxyhat.com on port 8080 (HTTP) or 1080 (SOCKS5). You control geo-targeting and session stickiness through the username string. See the proxy locations page for available countries, and check pricing for residential proxy plans. For broader scraping patterns, review our web scraping use case guide.

Worked Python Example: Scraping One Review Node via BFF GraphQL

Below is a complete Python example that fetches the initial review page HTML (to extract the gd-csrf-token), then POSTs to the BFF GraphQL endpoint through a ProxyHat residential proxy with a sticky session. It uses curl_cffi for Chrome TLS impersonation.

import re
import json
import time
import requests
from curl_cffi import requests as cffi_requests

# --- ProxyHat residential proxy with sticky session ---
PROXY = "http://user-session-glassdoor-001:pass@gate.proxyhat.com:8080"

HEADERS = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
                  "AppleWebKit/537.36 (KHTML, like Gecko) "
                  "Chrome/131.0.0.0 Safari/537.36",
    "Accept-Language": "en-US,en;q=0.9",
}

def fetch_csrf_token(employer_id: int) -> str:
    """Fetch the review page HTML and extract gd-csrf-token."""
    url = f"https://www.glassdoor.com/Reviews/example-Reviews-E{employer_id}.htm"
    resp = cffi_requests.get(
        url,
        headers=HEADERS,
        proxies={"http": PROXY, "https": PROXY},
        impersonate="chrome",
        timeout=30,
    )
    if resp.status_code == 403:
        raise RuntimeError("Blocked by DataDome/Cloudflare — rotate IP")
    # The token is embedded in a meta tag or inline script
    match = re.search(r'gd-csrf-token["\']?\s*[:=]\s*["\']([a-f0-9-]+)', resp.text)
    if not match:
        raise RuntimeError("Could not find gd-csrf-token in page HTML")
    return match.group(1)

def fetch_reviews_bff(employer_id: int, csrf_token: str, cursor: str = None) -> dict:
    """POST to the BFF GraphQL endpoint for structured review nodes."""
    bff_url = "https://www.glassdoor.com/bff/graphql"
    query = """
    query getReviews($employerId: Long!, $cursor: String) {
      employer(employerId: $employerId) {
        reviews(cursor: $cursor, limit: 10) {
          nodes {
            ratingOverall
            pros
            cons
            jobTitle { title }
            reviewDateTime
          }
          pageInfo { nextCursor hasNextPage }
        }
      }
    }
    """
    variables = {"employerId": employer_id, "cursor": cursor}
    headers = {
        **HEADERS,
        "Content-Type": "application/json",
        "gd-csrf-token": csrf_token,
    }
    resp = cffi_requests.post(
        bff_url,
        headers=headers,
        json={"query": query, "variables": variables},
        proxies={"http": PROXY, "https": PROXY},
        impersonate="chrome",
        timeout=30,
    )
    if resp.status_code == 403:
        raise RuntimeError("BFF blocked — rotate IP and retry")
    return resp.json()

# --- Main flow ---
employer_id = 6036  # example employer ID
try:
    token = fetch_csrf_token(employer_id)
    time.sleep(3)  # pace between page load and API call
    data = fetch_reviews_bff(employer_id, token)
    reviews = data["data"]["employer"]["reviews"]["nodes"]
    for r in reviews:
        print(f"{r['ratingOverall']}★ | {r['jobTitle']['title']}")
        print(f"  Pros: {r['pros'][:80]}...")
        print(f"  Cons: {r['cons'][:80]}...")
except RuntimeError as e:
    print(f"Error: {e}")

This example uses a sticky session (-session-glassdoor-001) so that both the initial HTML fetch and the BFF POST originate from the same residential IP. DataDome sets cookies on the first request, and if the second request comes from a different IP, those cookies may be invalidated.

Node.js Example: Same Flow with undici and ProxyAgent

For teams running Node.js, here is the equivalent using undici with a proxy agent. Note that TLS fingerprint impersonation in Node.js is less mature than curl_cffi in Python — you may need the node-curl-impersonate package for full Chrome TLS matching.

import { ProxyAgent, request } from 'undici';

const proxy = new ProxyAgent('http://user-session-glassdoor-001:pass@gate.proxyhat.com:8080');

const headers = {
  'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
  'Accept-Language': 'en-US,en;q=0.9',
};

async function fetchReviews(employerId, csrfToken, cursor = null) {
  const bffUrl = 'https://www.glassdoor.com/bff/graphql';
  const query = `
    query getReviews($employerId: Long!, $cursor: String) {
      employer(employerId: $employerId) {
        reviews(cursor: $cursor, limit: 10) {
          nodes { ratingOverall pros cons jobTitle { title } reviewDateTime }
          pageInfo { nextCursor hasNextPage }
        }
      }
    }`;
  const body = JSON.stringify({
    query,
    variables: { employerId, cursor },
  });
  const res = await request(bffUrl, {
    method: 'POST',
    dispatcher: proxy,
    headers: {
      ...headers,
      'Content-Type': 'application/json',
      'gd-csrf-token': csrfToken,
    },
    body,
  });
  if (res.statusCode === 403) throw new Error('Blocked — rotate IP');
  return await res.body.json();
}

Pagination, Sticky Sessions, Pacing, and Retry Logic

Scraping Glassdoor reviews at scale requires careful session management. Here are the patterns that work in 2026:

Pagination Cursors

The BFF GraphQL response includes a pageInfo.nextCursor field. Pass it as the cursor variable in the next request. Glassdoor typically returns 10 reviews per page. A company with 5,000 reviews requires 500 paginated requests — plan your pacing accordingly.

Sticky Sessions

Use ProxyHat's -session- flag to pin a residential IP for the duration of a pagination sequence. If DataDome issues a 403 mid-pagination, switch to a new session ID (e.g., -session-glassdoor-002) to get a fresh IP, then re-fetch the CSRF token from the HTML page before continuing. The old cookies are tied to the old IP and will not work with the new one.

# Sticky session for pagination
PROXY_PAGE_1 = "http://user-session-glassdoor-001:pass@gate.proxyhat.com:8080"
PROXY_PAGE_2 = "http://user-session-glassdoor-001:pass@gate.proxyhat.com:8080"  # same IP

# On 403, rotate to a new session
PROXY_FALLBACK = "http://user-session-glassdoor-002:pass@gate.proxyhat.com:8080"

Pacing

DataDome's behavioral model flags rapid sequential requests from the same IP. A safe pace is 1 request every 5-10 seconds per session. If you run 10 concurrent sessions with different sticky IPs, you can achieve roughly 60-120 requests per minute — enough to collect 600-1,200 reviews per minute. Do not push beyond this without monitoring your 403 rate; if it exceeds 5%, slow down.

Challenge-Rtry Logic

MAX_RETRIES = 3

def fetch_with_retry(employer_id, cursor=None, session_id="glassdoor-001"):
    for attempt in range(MAX_RETRIES):
        proxy = f"http://user-session-{session_id}:pass@gate.proxyhat.com:8080"
        try:
            token = fetch_csrf_token(employer_id, proxy)
            time.sleep(3)
            data = fetch_reviews_bff(employer_id, token, cursor, proxy)
            return data
        except RuntimeError:
            # Rotate session ID to get a new residential IP
            session_id = f"glassdoor-{attempt + 100}"
            time.sleep(10)  # cool-down before retry
    raise RuntimeError(f"Failed after {MAX_RETRIES} retries")

Common Mistakes and Edge Cases

  • Using Python requests without TLS impersonation: This fails immediately on Cloudflare. Always use curl_cffi with impersonate="chrome".
  • Rotating IP on every request: This breaks DataDome cookies. Use sticky sessions for pagination sequences and rotate only on 403.
  • Ignoring gd-csrf-token: The BFF endpoint returns 403 without it. Always fetch the HTML page first to extract the token.
  • Scraping behind login: Full salary data and review text are auth-walled. Do not attempt to bypass login — this violates Glassdoor's ToS and may violate the CFAA.
  • Collecting identifiable employee data: Review text may contain names or personal details. Under GDPR, processing EU employee data without a legal basis is a violation. Aggregate and anonymize.
  • Not checking robots.txt: Always review Glassdoor's robots.txt and respect disallow directives where they apply to your use case.

Proxy Type Comparison for Glassdoor Scraping

Proxy TypeSuccess Rate vs DataDomeCostBest For
DatacenterLow (10-20%)$0.5-2/GBNot recommended for Glassdoor
Residential (rotating)High (80-90%)$3-8/GBPrimary choice for review scraping
Mobile (4G/5G)Very high (90-95%)$10-20/GBHigh-value targets or persistent blocks

For most Glassdoor scraping workloads, residential rotating proxies offer the best balance of cost and reliability. Mobile proxies are overkill unless you are hitting persistent DataDome challenges that residential IPs cannot clear.

Ethical Scraping and When to Use Official APIs

Scraping Glassdoor — even public data — sits in a gray area. Here are the principles that responsible data teams follow:

  1. Collect only public, aggregate review data. Truncated review summaries, overall ratings, and job titles are public. Full review text, salary details, and interview questions are not.
  2. Do not scrape behind login. Bypassing authentication to access gated content is a clear ToS violation and carries legal risk under the CFAA in the US. Under GDPR, processing EU employee data without consent or another legal basis is a violation carrying fines up to 4% of global annual revenue.
  3. Anonymize and aggregate. Do not store reviewer names, specific dates that could identify individuals, or any free-text field that contains personal data. Aggregate to company-level statistics (average rating, pros/cons keyword frequency, rating distribution).
  4. Respect rate limits. Pacing at 1 request per 5-10 seconds per session is not just a technical recommendation — it is a courtesy that reduces load on Glassdoor's infrastructure.
  5. Consider an official data license. If you are building a commercial product that depends on Glassdoor data at scale, Glassdoor offers employer data products and API access through partnerships. This is the legally safest path for production datasets, especially for salary benchmarks where public data is unavailable.
Key principle: If your use case requires detailed salary data or full review text, you need an official data agreement — not a more aggressive scraper.

Key Takeaways

  • Only truncated review summaries and company overview data are public on Glassdoor. Full salary breakdowns and complete review text are behind a login wall — do not scrape them.
  • Glassdoor uses DataDome (IP/behavioral scoring) plus Cloudflare Bot Management (JS challenge, TLS fingerprinting). You need curl_cffi with Chrome impersonation and residential proxies to get past both.
  • The BFF GraphQL endpoint returns structured JSON review nodes (ratingOverall, pros, cons, jobTitle) and requires a gd-csrf-token header extracted from the initial HTML page.
  • Use ProxyHat sticky sessions (-session- flag) to maintain DataDome cookies across paginated requests. Rotate to a new session only on 403.
  • Pace at 1 request per 5-10 seconds per session. If your 403 rate exceeds 5%, slow down or switch to mobile proxies.
  • For production salary datasets, pursue an official Glassdoor data license rather than scraping behind login.

Ready to start collecting public Glassdoor review data? Explore ProxyHat residential proxy plans or read more about our SERP tracking capabilities. Full API documentation is available at docs.proxyhat.com.

Ready to get started?

Access 50M+ residential IPs across 148+ countries with AI-powered filtering.

View PricingResidential Proxies
← Back to Blog