Aviso legal: Esta guía cubre únicamente el acceso a datos públicos sin autenticación. Debes respetar los Términos de Servicio de Glassdoor, la Computer Fraud and Abuse Act (CFAA) en EE.UU. y el GDPR en la UE. No accedas a contenido tras un muro de login ni extraigas datos personales identificables sin consentimiento.
Si trabajas en analítica de RR.HH. o en inteligencia de mercado laboral, sabes que Glassdoor es una de las fuentes más ricas de reseñas de empleados, valoraciones culturales y datos salariales. Pero scrapear Glassdoor en 2026 no es trivial: la plataforma combina DataDome, Cloudflare Bot Management, fingerprinting TLS y challenges de JavaScript. En esta guía verás cómo scrapear reseñas y salarios de Glassdoor de forma robusta, usando el endpoint BFF GraphQL no documentado, curl_cffi para impersonación TLS y proxies residenciales rotativos de ProxyHat.
Por qué scrapear Glassdoor es difícil en 2026
Glassdoor protege su contenido con múltiples capas anti-bot. DataDome analiza la reputación IP, los headers HTTP, el fingerprint del navegador y los patrones de comportamiento. Cloudflare Bot Management añade challenges de JavaScript y fingerprinting TLS (JA3/JA4) que detectan clientes HTTP que no coinciden con un navegador real. Un requests.get() básico con una IP de datacenter recibe un HTTP 403 en menos de 5 peticiones en la mayoría de los casos.
El problema se agrava porque Glassdoor sirve parte de su contenido dinámico a través de endpoints GraphQL internos (/graph y /bff/) que requieren un gd-csrf-token válido y cookies de sesión de DataDome. Si tu cliente TLS no coincide con Chrome o Firefox, el challenge nunca se resuelve y el token no se emite.
Qué es accesible sin login vs. gated
Antes de escribir una sola línea de código, es crítico entender el límite entre lo público y lo gated:
| Tipo de dato | Accesible sin login | URL / Endpoint |
|---|---|---|
| Reseña general de empresa | Sí (truncada) | /Reviews/<company>-Reviews-E<id>.htm |
| Valoración global y sub-valoraciones | Sí | BFF GraphQL |
| Pros y contras (truncados) | Sí | BFF GraphQL |
| Cargo del revisor | Sí (anonimizado) | BFF GraphQL |
| Desglose salarial completo | No | Tras login |
| Reseñas extendidas (texto completo) | No | Tras login |
| Datos personales del revisor | No | Nunca scrapear |
Esta guía se centra exclusivamente en los datos públicos: reseñas truncadas, valoraciones y metadatos de cargo. Los datos salariales completos están detrás de un muro de autenticación y no deben extraerse mediante scraping. Si necesitas datos salariales completos, considera una licencia de datos oficial o APIs de terceros como Glassdoor for Employers.
El stack anti-bot de Glassdoor: DataDome + Cloudflare
Glassdoor delega su protección a DataDome, un servicio anti-bot que combina heurísticas en tiempo real con scoring de IP. Según la documentación oficial de DataDome, el sistema evalúa más de 40 señales por petición, incluyendo headers, fingerprint JavaScript, reputación IP y patrones de navegación.
Cloudflare Bot Management se superpone a DataDome con fingerprinting TLS (JA3/JA4). Esto significa que incluso si envías los headers perfectos, el handshake TLS de tu cliente HTTP delata si eres requests, httpx o un navegador real. La solución es curl_cffi, una librería Python que usa libcurl-impersonate para reproducir el handshake TLS exacto de Chrome.
Por qué curl_cffi es imprescindible
La impersonación TLS de curl_cffi replica el ClientHello de Chrome 120+ incluyendo el orden de cipher suites, las extensiones y los parámetros ALPN. Sin esto, DataDome detecta tu cliente en el primer handshake y emite un challenge que un cliente no-JS no puede resolver. Con curl_cffi, la primera petición suele pasar el filtro si la IP residencial tiene buena reputación.
pip install curl_cffiEl endpoint BFF GraphQL de Glassdoor
Glassdoor sirve datos estructurados a través de un endpoint BFF (Backend-for-Frontend) en /bff/v1/graphql (la ruta exacta puede variar entre despliegues). Este endpoint devuelve JSON limpio con campos como ratingOverall, pros, cons, jobTitle, reviewDateTime y cursores de paginación, evitando el parseo de HTML.
Requisitos para usarlo:
- Header
gd-csrf-token: token CSRF que se genera en la carga inicial de la página. Debes extraerlo del HTML o de las cookies. - Cookies de DataDome:
datadomey cookies de sesión que se establecen tras pasar el challenge inicial. - Body GraphQL: una query POST con el
operationName,queryyvariables(incluyendo el ID de empresa y el cursor). - Headers de navegador:
User-Agent,Accept,Content-Type: application/json,OriginyReferercoherentes.
El endpoint /graph es una variante legacy que devuelve datos similares pero con un esquema diferente. En 2026, la mayoría del tráfico frontend usa /bff/.
Configuración de proxies ProxyHat
Para superar el scoring de IP de DataDome necesitas proxies residenciales. Las IPs de datacenter tienen una tasa de bloqueo superior al 90% en Glassdoor según pruebas internas de la comunidad. ProxyHat ofrece proxies residenciales rotativos con geo-targeting y sesiones sticky.
Parámetros de conexión:
| Parámetro | Valor |
|---|---|
| Gateway | gate.proxyhat.com |
| Puerto HTTP | 8080 |
| Puerto SOCKS5 | 1080 |
| Formato usuario | user-country-US-session-abc123 |
Consulta todas las ubicaciones disponibles y el detalle de planes y precios.
Ejemplo en Python: scrapear una reseña vía BFF GraphQL
A continuación, un ejemplo completo que combina curl_cffi, proxies residenciales de ProxyHat y el endpoint BFF GraphQL para extraer una reseña truncada.
import json
import time
import re
from curl_cffi import requests as cffi_requests
# --- Configuración del proxy ProxyHat (residencial, sesión sticky) ---
PROXY_USER = "user-country-US-session-glassdoor01"
PROXY_PASS = "tu_password"
PROXY_URL = f"http://{PROXY_USER}:{PROXY_PASS}@gate.proxyhat.com:8080"
GLASSDOOR_EMPLOYER_ID = "E12345" # ID de empresa de Glassdoor
BASE_URL = "https://www.glassdoor.com"
def obtener_csrf_y_cookies():
"""Carga la página de reseñas para obtener gd-csrf-token y cookies DataDome."""
url = f"{BASE_URL}/Reviews/empresa-Reviews-{GLASSDOOR_EMPLOYER_ID}.htm"
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/124.0.0.0 Safari/537.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.9",
}
resp = cffi_requests.get(
url,
headers=headers,
proxies={"http": PROXY_URL, "https": PROXY_URL},
impersonate="chrome124",
timeout=30,
)
if resp.status_code == 403:
raise Exception("DataDome challenge detectado — rota la sesión del proxy")
# Extraer gd-csrf-token del HTML o de los headers
csrf_match = re.search(r'gd-csrf-token["\']?\s*[:=]\s*["\']([^"\']+)', resp.text)
csrf_token = csrf_match.group(1) if csrf_match else ""
return csrf_token, resp.cookies
def scrapear_resenas_bff(csrf_token, cookies, cursor=None):
"""POST al endpoint BFF GraphQL para obtener reseñas estructuradas."""
bff_url = f"{BASE_URL}/bff/v1/graphql"
variables = {
"employerId": GLASSDOOR_EMPLOYER_ID,
"num": 10,
"cursor": cursor,
}
payload = {
"operationName": "EmployerReviews",
"query": """
query EmployerReviews($employerId: String!, $num: Int!, $cursor: String) {
employer(employerId: $employerId) {
reviews(num: $num, cursor: $cursor) {
edges {
node {
ratingOverall
pros
cons
jobTitle { title }
reviewDateTime
}
}
pageInfo { nextCursor hasNextPage }
}
}
}
""",
"variables": variables,
}
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/124.0.0.0 Safari/537.36",
"Content-Type": "application/json",
"Accept": "application/json",
"Origin": BASE_URL,
"Referer": f"{BASE_URL}/Reviews/empresa-Reviews-{GLASSDOOR_EMPLOYER_ID}.htm",
"gd-csrf-token": csrf_token,
}
resp = cffi_requests.post(
bff_url,
headers=headers,
json=payload,
cookies=cookies,
proxies={"http": PROXY_URL, "https": PROXY_URL},
impersonate="chrome124",
timeout=30,
)
if resp.status_code == 403:
raise Exception("Challenge DataDome en BFF — rota sesión y reintenta")
return resp.json()
# --- Ejecución principal ---
csrf, cookies = obtener_csrf_y_cookies()
time.sleep(2) # pacing entre carga inicial y API
resultado = scrapear_resenas_bff(csrf, cookies)
for edge in resultado["data"]["employer"]["reviews"]["edges"]:
node = edge["node"]
print(f"Cargo: {node['jobTitle']['title']}")
print(f"Valoración: {node['ratingOverall']}/5")
print(f"Pros: {node['pros'][:120]}...")
print(f"Contras: {node['cons'][:120]}...")
print(f"Fecha: {node['reviewDateTime']}")
print("---")
Este ejemplo extrae reseñas truncadas con sus valoraciones, pros, contras y cargo del revisor (anonimizado por Glassdoor). La sesión sticky session-glassdoor01 mantiene la misma IP para conservar las cookies de DataDome durante toda la sesión de paginación.
Ejemplo en Node.js con fetch y proxy HTTP
Para equipos que prefieren Node.js, aquí hay un equivalente usando undici con proxy HTTP. Nota: Node.js no tiene impersonación TLS nativa equivalente a curl_cffi, por lo que recomendamos usar el binding node-libcurl o ejecutar curl-impersonate como subproceso.
const { ProxyAgent, fetch } = require('undici');
const proxyUrl = 'http://user-country-US-session-glassdoor02:tu_password@gate.proxyhat.com:8080';
const agent = new ProxyAgent(proxyUrl);
async function scrapeReviews() {
const bffUrl = 'https://www.glassdoor.com/bff/v1/graphql';
const payload = {
operationName: 'EmployerReviews',
query: `query EmployerReviews($employerId: String!, $num: Int!, $cursor: String) {
employer(employerId: $employerId) {
reviews(num: $num, cursor: $cursor) {
edges { node { ratingOverall pros cons jobTitle { title } reviewDateTime } }
pageInfo { nextCursor hasNextPage }
}
}
}`,
variables: { employerId: 'E12345', num: 10, cursor: null }
};
const resp = await fetch(bffUrl, {
method: 'POST',
dispatcher: agent,
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'Origin': 'https://www.glassdoor.com',
'Referer': 'https://www.glassdoor.com/Reviews/empresa-Reviews-E12345.htm',
'gd-csrf-token': process.env.GD_CSRF_TOKEN || '',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36',
},
body: JSON.stringify(payload),
});
if (resp.status === 403) {
throw new Error('DataDome challenge — rota sesión del proxy');
}
const data = await resp.json();
for (const edge of data.data.employer.reviews.edges) {
const n = edge.node;
console.log(`${n.jobTitle.title} — ${n.ratingOverall}/5 — ${n.reviewDateTime}`);
}
}
scrapeReviews().catch(console.error);
Paginación, sesiones sticky y lógica de reintento
La paginación del BFF GraphQL usa cursores opacos. Cada respuesta incluye pageInfo.nextCursor y pageInfo.hasNextPage. Debes pasar el cursor en la siguiente petición dentro de variables.cursor.
Estrategia de sesión sticky
Las cookies de DataDome están vinculadas a la IP que las generó. Si rotas la IP en cada petición, DataDome invalida la sesión y emite un challenge. La solución es usar sesiones sticky en ProxyHat:
# Misma sesión = misma IP durante toda la paginación
PROXY_USER = "user-country-US-session-glassdoor01"
Mantén la misma sesión durante toda la extracción de una empresa. Solo rota la sesión (cambia el identificador) cuando recibas un HTTP 403 o un challenge de DataDome.
Pacing y rate limiting
- Espera entre peticiones: 1-3 segundos con jitter aleatorio. Glassdoor no publica límites públicos, pero DataDome marca como sospechosos los bursts de más de ~10 peticiones por minuto desde la misma IP.
- Concurrencia: máx. 2-3 peticiones concurrentes por sesión sticky. Si necesitas más throughput, usa múltiples sesiones con IPs diferentes.
- Retry con backoff exponencial: ante un 403, espera 5s, rota la sesión, re-obtén CSRF y cookies, y reintenta. Máximo 3 reintentos por cursor.
import random
def scrapear_todas_las_resenas(csrf, cookies, proxy_user_base):
cursor = None
todas = []
session_id = "glassdoor01"
reintentos = 0
while True:
proxy_user = f"user-country-US-session-{session_id}"
proxy_url = f"http://{proxy_user}:tu_password@gate.proxyhat.com:8080"
try:
data = scrapear_resenas_bff(csrf, cookies, cursor, proxy_url)
edges = data["data"]["employer"]["reviews"]["edges"]
todas.extend(edges)
reintentos = 0
page_info = data["data"]["employer"]["reviews"]["pageInfo"]
if not page_info["hasNextPage"]:
break
cursor = page_info["nextCursor"]
time.sleep(random.uniform(1.5, 3.0))
except Exception as e:
reintentos += 1
if reintentos > 3:
print(f"Máximo de reintentos alcanzado en cursor {cursor}")
break
# Rotar sesión y re-obtener CSRF
session_id = f"glassdoor{random.randint(100,999)}"
time.sleep(5 * reintentos)
csrf, cookies = obtener_csrf_y_cookies(proxy_url)
return todas
Errores comunes y casos límite
- Usar requests/httpx sin impersonación TLS: DataDome detecta el fingerprint JA3 en el primer handshake. Usa siempre
curl_cfficonimpersonate="chrome124". - Rotar IP en cada petición: invalida las cookies de DataDome. Usa sesiones sticky durante toda la paginación.
- No extraer el
gd-csrf-token: el BFF GraphQL devuelve 403 sin este header. Debes cargar la página HTML primero y extraerlo. - Scrapear tras login: viola los ToS de Glassdoor y potencialmente el CFAA. Esta guía cubre solo datos públicos.
- Ignorar robots.txt: aunque Glassdoor no bloquea explícitamente /Reviews/ en robots.txt, debes revisarlo periódicamente. Consulta https://www.glassdoor.com/robots.txt.
- No agregar datos: almacenar reseñas individuales con texto completo puede plantear problemas de GDPR si el texto contiene información personal. Agrega a nivel de empresa y elimina PII.
Scraping ético y cuándo usar APIs oficiales
El scraping de datos públicos es una herramienta legítima para la inteligencia de mercado laboral, pero tiene límites éticos y legales claros:
- Solo datos públicos sin login. No extraigas contenido tras el muro de autenticación. El precedente hiQ v. LinkedIn (2022) respalda el scraping de datos públicos, pero no anula los ToS de la plataforma.
- Agrega y anonimiza. Almacena métricas agregadas (valoración media, distribución de pros/contras por departamento) en lugar de reseñas individuales con texto completo. Esto reduce el riesgo bajo GDPR, que protege los datos personales de empleados en la UE incluso si fueron publicados voluntariamente.
- Respeta el GDPR para datos de empleados europeos. Una reseña que menciona un nombre, una ubicación específica o un equipo pequeño puede constituir dato personal bajo el Artículo 4 del GDPR. Aplica seudonimización y minimización de datos.
- Considera una licencia de datos oficial. Si necesitas datos salariales completos, coberturas históricas o acceso a gran escala, una licencia de Glassdoor for Employers o un proveedor de datos de mercado laboral es la opción legalmente segura. El coste de una licencia suele ser menor que el riesgo legal de un scraping agresivo.
- Usa APIs oficiales cuando existan. Algunas plataformas ofrecen APIs con rate limits razonables. Si Glassdoor ofrece una API para tu caso de uso, úsala antes de recurrir al scraping.
Para más recursos sobre scraping ético y casos de uso, consulta nuestra guía de web scraping y de SERP tracking. La documentación de ProxyHat tiene ejemplos adicionales de configuración.
Puntos clave
- Los datos públicos de Glassdoor (reseñas truncadas, valoraciones) son accesibles sin login vía el endpoint BFF GraphQL en
/bff/v1/graphql.- DataDome + Cloudflare Bot Management requieren impersonación TLS con
curl_cffi(impersonate="chrome124") y proxies residenciales.- El header
gd-csrf-tokenes obligatorio para el BFF GraphQL; se extrae de la carga HTML inicial.- Usa sesiones sticky de ProxyHat (
session-xxx) para conservar las cookies de DataDome durante la paginación.- Pacing de 1-3 segundos entre peticiones, máx. 2-3 concurrentes por sesión, y lógica de reintento con rotación de sesión ante HTTP 403.
- No scrapear tras login. Agrega y anonimiza los datos. Considera una licencia oficial para datos salariales completos.
Conclusión
Scrapear Glassdoor en 2026 es factible si combinas las herramientas adecuadas: curl_cffi para fingerprinting TLS, el endpoint BFF GraphQL para datos estructurados, y proxies residenciales rotativos con sesiones sticky de ProxyHat para evadir el scoring de IP de DataDome. El resultado es un pipeline de datos de reseñas públicas que alimenta dashboards de analítica de RR.HH. sin violar ToS ni GDPR.
El siguiente paso es configurar tu cuenta de ProxyHat, elegir proxies residenciales con geo-targeting US, y adaptar el código de ejemplo al ID de empresa que necesitas analizar. Empieza con una sola empresa, valida tu tasa de éxito (>90% sin 403), y escala gradualmente con múltiples sesiones sticky.






