Legal caveat: This guide covers access to publicly available Glassdoor company review data only. Scraping behind a login wall, extracting personally identifiable employee data, or violating Glassdoor's Terms of Service may breach the Computer Fraud and Abuse Act (CFAA) in the United States and the General Data Protection Regulation (GDPR) in the European Union. Always review the platform's ToS, respect robots.txt directives, and consult legal counsel before deploying a scraper at scale. ProxyHat does not encourage unauthorized data extraction.
If you build labor-market analytics pipelines, compensation benchmarking tools, or employer-brand monitoring dashboards, you have likely asked how to scrape Glassdoor company reviews and salaries in 2026. Glassdoor remains one of the richest sources of employee sentiment and compensation data, but its anti-bot infrastructure has matured significantly. This guide walks through what is publicly accessible, how Glassdoor's bot detection works, and how to use residential proxies with TLS impersonation to collect aggregate review data responsibly.
What You Can Scrape on Glassdoor Without Logging In
Glassdoor's public surface area is smaller than it appears. Company overview pages and truncated review snippets are reachable without authentication, but full salary breakdowns, interview questions, and complete review text sit behind a login wall.
Public vs. Login-Gated Data
| Data Type | URL Pattern | Public? | Notes |
|---|---|---|---|
| Company overview | /Overview/… | Yes | Industry, size, founded year, HQ |
| Truncated reviews | /Reviews/<company>-Reviews-E<id>.htm | Partially | First ~150 chars of pros/cons visible |
| Review metadata | BFF GraphQL endpoint | Yes (with headers) | ratingOverall, jobTitle, date — no full text |
| Full review text | Review detail page | No | Requires login session |
| Salary breakdowns | /Salary/… | No | Auth-walled; do not scrape |
| Interview questions | /Interview/… | No | Auth-walled; do not scrape |
The key insight: do not attempt to scrape behind Glassdoor's login wall. Full salary data and complete review text are gated for legal and business reasons. The BFF GraphQL endpoint, however, returns structured review metadata — ratings, job titles, dates, and truncated pros/cons — without requiring authentication. This is the sweet spot for aggregate analytics.
Glassdoor's Anti-Bot Stack: DataDome + Cloudflare Bot Management
Glassdoor layers two enterprise bot-detection systems: DataDome and Cloudflare Bot Management. Together they inspect three signals:
- TLS fingerprinting: The JA3/JA4 hash of your TLS ClientHello is compared against known browser profiles. Python's default
requestslibrary produces a fingerprint that neither Chrome nor Firefox matches, so it gets challenged immediately. - JavaScript challenges: Cloudflare issues a JS challenge that requires a real browser JS engine to solve. Headless browsers can pass this, but at a significant performance cost.
- IP reputation scoring: DataDome maintains a reputation database. Datacenter IPs from AWS, GCP, and DigitalOcean are flagged almost instantly. Residential IPs from real ISPs score far better.
This is why a naive requests.get() call to a Glassdoor review page returns a 403 within the first 2–3 requests. The TLS fingerprint mismatch alone is enough to trigger a block before IP reputation is even consulted.
Why curl_cffi Is Essential
The curl_cffi library wraps libcurl with Chrome's exact TLS fingerprint, including the ClientHello cipher ordering and extension list. When you make an HTTP request with curl_cffi in impersonate="chrome" mode, the server sees a JA3 hash identical to a real Chrome browser. Combined with residential proxies, this defeats both the TLS and IP-reputation layers of Glassdoor's bot detection.
The Undocumented BFF GraphQL Endpoint
Glassdoor's frontend communicates with an internal GraphQL service at /bff/graphql (sometimes /graph). This endpoint returns structured JSON with review metadata — no HTML parsing required. The request is a standard GraphQL POST with a few required headers:
Content-Type: application/jsongd-csrf-token: A CSRF token embedded in the initial page load's HTML or JavaScript bundle. You extract it from the company reviews page before making the GraphQL call.User-Agent: Must match the TLS profile you are impersonating (e.g., a current Chrome UA string).
The GraphQL query body requests fields like ratingOverall, pros (truncated), cons (truncated), jobTitle, reviewDateTime, and employerName. The response is clean JSON — far more reliable than parsing HTML that changes with every A/B test.
ProxyHat Setup: Residential Proxies for Glassdoor Scraping
Rotating residential proxies are the correct choice for Glassdoor because DataDome's IP scoring penalizes datacenter ranges aggressively. ProxyHat's residential pool routes your traffic through real ISP-assigned IPs, which score as legitimate user traffic.
Configure your connection with geo-targeting to match the Glassdoor locale you are scraping (e.g., US companies from US IPs). Use the -session- flag to maintain sticky sessions so DataDome's cookies remain valid across paginated requests.
See our pricing page for residential proxy plans, and check available locations for geo-targeting options. Full connection details are in the ProxyHat documentation.
Worked Python Example: Scraping Reviews via BFF GraphQL
Below is a complete Python example that fetches the CSRF token from a public company reviews page, then posts a GraphQL query to the BFF endpoint through ProxyHat residential proxies using curl_cffi for Chrome TLS impersonation.
import re
import json
import time
import curl_cffi.requests as cffi_requests
PROXY_URL = "http://user-country-US-session-glassdoor1:YOUR_PASSWORD@gate.proxyhat.com:8080"
COMPANY_REVIEWS_URL = "https://www.glassdoor.com/Reviews/{company-slug}-Reviews-E{id}.htm"
BFF_GRAPHQL_URL = "https://www.glassdoor.com/bff/graphql"
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": "application/json",
"Accept-Language": "en-US,en;q=0.9",
}
def fetch_csrf_token(reviews_url: str) -> str:
"""Fetch the public reviews page and extract the gd-csrf-token."""
resp = cffi_requests.get(
reviews_url,
headers=HEADERS,
proxies={"http": PROXY_URL, "https": PROXY_URL},
impersonate="chrome",
timeout=30,
)
if resp.status_code != 200:
raise Exception(f"Failed to load page: {resp.status_code}")
# CSRF token is typically in a meta tag or inline JS
match = re.search(r'gd-csrf-token["\']?\s*[:=]\s*["\']([a-f0-9-]+)', resp.text)
if not match:
match = re.search(r'csrfToken["\']?\s*[:=]\s*["\']([a-f0-9-]+)', resp.text)
if not match:
raise Exception("Could not find CSRF token on page")
return match.group(1)
def fetch_reviews_graphql(csrf_token: str, employer_id: int, cursor: str = None) -> dict:
"""Post a GraphQL query to the BFF endpoint for review metadata."""
graphql_headers = {
**HEADERS,
"Content-Type": "application/json",
"gd-csrf-token": csrf_token,
"Origin": "https://www.glassdoor.com",
"Referer": f"https://www.glassdoor.com/Reviews/E{employer_id}.htm",
}
query = {
"query": """
query EmployerReviews($employerId: ID!, $cursor: String) {
employerReviews(employerId: $employerId, after: $cursor) {
edges {
node {
ratingOverall
pros
cons
jobTitle
reviewDateTime
}
}
pageInfo { endCursor hasNextPage }
}
}
""",
"variables": {"employerId": str(employer_id), "cursor": cursor},
}
resp = cffi_requests.post(
BFF_GRAPHQL_URL,
headers=graphql_headers,
json=query,
proxies={"http": PROXY_URL, "https": PROXY_URL},
impersonate="chrome",
timeout=30,
)
if resp.status_code == 403:
raise Exception("Blocked by DataDome — rotate proxy or slow down")
return resp.json()
# Usage
csrf = fetch_csrf_token(COMPANY_REVIEWS_URL)
result = fetch_reviews_graphql(csrf, employer_id=12345)
for edge in result["data"]["employerReviews"]["edges"]:
node = edge["node"]
print(f"{node['ratingOverall']}★ | {node['jobTitle']} | {node['reviewDateTime']}")
print(f" Pros: {node['pros'][:80]}...")
print(f" Cons: {node['cons'][:80]}...")
This example extracts one page of review nodes with ratingOverall, pros, cons, and jobTitle. The session-glassdoor1 flag in the proxy username keeps the same residential IP for the duration of your scraping run, which is critical because DataDome validates session cookies against the originating IP.
Node.js Equivalent
For JavaScript-based pipelines, use the cycletls package for TLS impersonation with ProxyHat:
const initCycleTLS = require('cycletls');
const PROXY_URL = 'http://user-country-US-session-glassdoor1:YOUR_PASSWORD@gate.proxyhat.com:8080';
(async () => {
const cycleTLS = await initCycleTLS();
const resp = await cycleTLS('https://www.glassdoor.com/Reviews/example-Reviews-E12345.htm', {
method: 'GET',
ja3: '771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,0-23-65281-10-11-35-16-5-13-18-51-45-43-27-17513,29-23-24,0',
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
proxy: PROXY_URL,
timeout: 30,
});
console.log(resp.status, resp.body.substring(0, 200));
cycleTLS.exit();
})();
Pagination, Sticky Sessions, Pacing, and Challenge Retry
Glassdoor's GraphQL endpoint uses cursor-based pagination. The pageInfo.endCursor and pageInfo.hasNextPage fields tell you whether more reviews exist and what cursor to pass next.
Sticky Sessions
Use the -session- flag in your ProxyHat username to pin a residential IP for the entire pagination sequence. If DataDome issues a cookie on the first request and you switch IPs mid-sequence, the cookie becomes invalid and you get a 403. A typical session ID like session-glassdoor-acme-001 keeps the same IP for up to 30 minutes.
Pacing
Do not exceed 1 request per 3–5 seconds per session. DataDome flags bursts of rapid requests even from residential IPs. For higher throughput, run multiple sessions in parallel with different session IDs and different residential IPs, each maintaining its own pace.
import time
def scrape_all_reviews(csrf, employer_id, max_pages=50):
cursor = None
all_reviews = []
for page in range(max_pages):
try:
result = fetch_reviews_graphql(csrf, employer_id, cursor)
except Exception as e:
print(f"Page {page} failed: {e} — waiting 60s and retrying")
time.sleep(60)
continue
edges = result["data"]["employerReviews"]["edges"]
all_reviews.extend([e["node"] for e in edges])
page_info = result["data"]["employerReviews"]["pageInfo"]
if not page_info["hasNextPage"]:
break
cursor = page_info["endCursor"]
time.sleep(4) # 4-second pace between pages
return all_reviews
Challenge-Retry Logic
If you receive a 403 or a DataDome challenge page, implement exponential backoff:
- Wait 60 seconds on first failure.
- Rotate to a new session ID (new residential IP) on second failure.
- Re-fetch the CSRF token from the public page — the token may have expired.
- Retry the GraphQL call with the fresh token and new IP.
- After 3 consecutive failures, pause for 24 hours. DataDome may have flagged your proxy subnet.
For broader scraping strategies beyond Glassdoor, see our web scraping use case guide and SERP tracking documentation.
Comparison: Proxy Types for Glassdoor Scraping
| Proxy Type | DataDome Evasion | Sticky Sessions | Cost | Recommendation |
|---|---|---|---|---|
| Datacenter | Poor — flagged in 2–3 requests | Yes | Low | Not recommended |
| Residential (rotating) | Excellent — real ISP IPs | Yes via session flag | Medium | Best choice |
| Mobile (4G/5G) | Excellent — highest trust score | Yes | High | Overkill for Glassdoor |
| ISP (static residential) | Good — stable IP, ISP ASN | Yes (inherent) | Medium-high | Good for long sessions |
Ethical Scraping: When to Stop and Use Official APIs
Scraping Glassdoor sits in a legally gray area. The platform's ToS prohibits automated data extraction, and the CFAA has been used to pursue scrapers that exceed authorized access. In the EU, GDPR Article 6 requires a lawful basis for processing personal data — and employee reviews, even when posted pseudonymously, can constitute personal data if they identify an individual through context.
Guidelines for Responsible Scraping
- Collect aggregate data only: Ratings, distributions, and summary statistics. Avoid storing individual review text that could be linked to a specific person.
- Do not scrape behind login: Full salary data, interview questions, and complete review text require authentication. Scraping authenticated content is the clearest CFAA violation.
- Respect rate limits: Pace requests to avoid degrading Glassdoor's service. 1 request per 4 seconds is a reasonable upper bound.
- Honor robots.txt: Check Glassdoor's robots.txt before scraping any URL path. If a path is disallowed, do not scrape it.
- Anonymize and aggregate: If you publish analysis, aggregate at the company or industry level. Do not republish individual reviews.
When an Official Data License Is Better
If your use case requires salary data, full review text, or large-scale commercial analytics, Glassdoor offers data licensing and API products for employers and partners. A licensed data feed eliminates legal risk, provides structured access to the data you need, and includes SLAs for uptime and data freshness. For production-grade HR analytics platforms serving enterprise clients, a data license is almost always the right call.
Key Takeaways
- Public Glassdoor review pages expose truncated review snippets and metadata via the BFF GraphQL endpoint — full text and salary data are login-gated and should not be scraped.
- DataDome + Cloudflare require both TLS impersonation (curl_cffi) and residential proxies to bypass reliably.
- The
gd-csrf-tokenheader is mandatory for BFF GraphQL calls — extract it from the public reviews page on each session.- Use ProxyHat sticky sessions (
-session-flag) to keep DataDome cookies valid across paginated requests.- Pace at 1 request per 3–5 seconds per session; rotate sessions for parallel throughput.
- For salary data and full review text, use Glassdoor's official data licensing API instead of scraping.
FAQ
What is the best way to scrape Glassdoor company reviews in 2026?
The most reliable approach is to use the undocumented BFF GraphQL endpoint at /bff/graphql, which returns structured review metadata (ratings, job titles, truncated pros/cons) without requiring login. You need curl_cffi for Chrome TLS impersonation, a residential proxy to avoid DataDome IP scoring, and the gd-csrf-token header extracted from the public reviews page. Do not attempt to scrape full review text or salary data behind the login wall.
Why do residential proxies matter for scraping Glassdoor?
Glassdoor uses DataDome for bot detection, which scores IP reputation in real time. Datacenter IPs from cloud providers (AWS, GCP, DigitalOcean) are flagged within 2–3 requests. Residential proxies route traffic through real ISP-assigned IP addresses, which DataDome scores as legitimate user traffic. Combined with TLS impersonation via curl_cffi, residential proxies achieve significantly higher success rates than datacenter alternatives.
Which proxy type works best for a Glassdoor reviews scraper?
Rotating residential proxies with sticky session support are the best choice. The sticky session flag (e.g., session-glassdoor-001) keeps the same IP for the duration of a pagination sequence, which is critical because DataDome validates session cookies against the originating IP. Mobile proxies also work but are more expensive and unnecessary for Glassdoor's detection level. Datacenter proxies fail almost immediately.
How do you avoid DataDome blocks when scraping Glassdoor?
Use three layers: (1) curl_cffi with impersonate="chrome" to match Chrome's exact TLS fingerprint; (2) residential proxies via ProxyHat with sticky sessions to maintain IP consistency; (3) pacing of 1 request per 3–5 seconds per session with exponential backoff on 403 responses. On repeated failures, rotate to a new session ID and re-fetch the CSRF token. After 3 consecutive failures, pause for 24 hours to avoid subnet-level flagging.
Can I scrape Glassdoor salary data without logging in?
No. Full salary breakdowns are behind Glassdoor's authentication wall and should not be scraped. Attempting to bypass login to access salary data is a clear violation of Glassdoor's Terms of Service and may constitute a CFAA violation in the US. If you need salary data for commercial analytics, use Glassdoor's official data licensing and API products, which provide structured, legally compliant access.






