La mayoría de los scrapers fallan antes de que se lea un solo header.
El servidor observa la firma a nivel de conexión que tu cliente envía a la red y decide si eres un navegador o una librería cliente fingiendo serlo. Python requests, net/http de Go, curl puro: todos entregan un fingerprint distintivo en el momento en que saludan. Los sitios a los que les importa (Datadome, Akamai, Imperva, la parte gestionada de Cloudflare) rechazan la conexión o te sirven una página de desafío antes de que tu string de User-Agent importe.
Eso es lo que unblocker: true resuelve en FourA. En el último mes, fijamos las piezas que hacen que funcione de manera fiable.
Novedades
unblocker: true es un único flag en cualquier llamada /api/single. Al activarlo hacemos tres cosas: inyectar el conjunto de headers del navegador, enviar el request por un transporte que coincide con lo que un navegador real envía a la red, y descomprimir lo que sea que devuelva el servidor (gzip, brotli, deflate). Las dos primeras están disponibles desde la beta. La tercera (autodescompresión de brotli) se lanzó el 25 de marzo, y el trabajo de fijación de versión llegó al día siguiente para mantener los headers y el transporte sincronizados.
Cómo funciona
Así se ve un request:
curl -X POST "https://api.foura.ai/api/single" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"url": "https://example.com/products",
"method": "GET",
"unblocker": true
}'
Tres capas se ejecutan por debajo.
Inyección de headers. Configuramos el paquete completo de headers del navegador: User-Agent, Sec-Ch-Ua, Sec-Ch-Ua-Platform, Sec-Fetch-Site, Sec-Fetch-Mode, Sec-Fetch-Dest, Accept, Accept-Language y Accept-Encoding. El orden importa. Los navegadores reales los emiten en una secuencia específica, y las librerías de detección lo verifican.
Firma de conexión. Nuestro transporte coincide con la forma a nivel de bytes de una sesión de navegador actualizada: el mismo orden de extensiones, las mismas preferencias de cifrado, las mismas peculiaridades del handshake. El curl estándar, Python requests y net/http de Go producen firmas que la infraestructura protegida marca automáticamente en milisegundos.
Autodescompresión. Cuando unblocker está activado, configuramos Accept-Encoding a gzip, deflate, br y el transporte desenvuelve el cuerpo. Obtienes un string decodificado (o un Buffer si pasas returnBuffer: true). Sin manejo manual de brotli, sin desajustes entre headers y cuerpo cuando un sitio elige deflate sobre gzip.
Por qué importa fijar la versión
Las firmas de conexión están bloqueadas por versión. Los detalles a nivel de red de un navegador este mes no son los del mes pasado, y un sitio que hace fingerprinting con cuidado notará la desviación. Fijamos las piezas móviles juntas para que los headers, los objetos navigator y la firma de conexión reporten la misma versión del navegador.
Si eso suena complicado, lo es. Sufrimos un desajuste durante la migración al monorepo en marzo cuando una pieza se actualizó automáticamente y el resto se desincronizó. La solución fueron dos commits: fijar la pieza móvil y nunca confiar en que el gestor de paquetes mantendrá las cosas alineadas por ti.
Impacto
En pruebas internas contra objetivos con fuerte fingerprinting (finanzas, viajes, e-commerce protegido), la diferencia entre unblocker: false y unblocker: true es la diferencia entre una página de desafío y un 200. Un curl puro apuntando a un Cloudflare gestionado termina en un 403 al primer intento. La misma URL con unblocker: true pasa porque la conexión parece una sesión de navegador a nivel de red.
Pero para los sitios que no hacen fingerprinting (la mayoría de las APIs públicas, plantillas de CMS antiguas, cualquier cosa controlada solo por rate limits de IP), dejar unblocker desactivado está bien y ahorra unos milisegundos de negociación. Úsalo donde lo necesites.
Para usuarios avanzados
Algunos patrones que vale la pena conocer.
Empareja unblocker con un proxy residencial cuando el objetivo también verifique la reputación de la IP. Las IPs de centro de datos más una firma de conexión perfecta aún levantan alertas en los ASNs que el sitio tiene en la lista negra. Nuestro endpoint del proxy (/api/proxy) rota por dominio de destino, por lo que agregar "proxy": "residential" al request suele ser suficiente.
Omite unblocker al llamar APIs JSON a las que no les importan los navegadores. Los headers adicionales pueden parecer sospechosos para una API que espera un cliente programático, por ejemplo, un backend llamando a su propio microservicio.
Si el sitio ejecuta anti-bot con JavaScript (desafíos interactivos de Turnstile, PerimeterX en su nivel más estricto, Akamai Bot Manager con heurística al máximo), unblocker por sí solo no será suficiente. Necesitas el endpoint del navegador, el cual ejecuta el desafío en un navegador completo. Ese es un producto diferente con un modelo de precios por créditos diferente, y escribimos sobre ello en Browser Tasks: Cómo hacer scraping en sitios con mucho JavaScript.
Y puedes combinar unblocker con el bloque validate para rechazar los responses que técnicamente devuelven un 200 pero contienen una página de desafío:
{
"url": "https://example.com/products",
"method": "GET",
"unblocker": true,
"validate": {
"data": { "fail": ["captcha", "Access Denied"] }
}
}
Eso convierte los fallos silenciosos en fallos clasificados, lo que importa para el seguimiento de tu tasa de éxito en el dashboard.
Próximos pasos
Los navegadores lanzan una nueva versión estable cada cuatro semanas. Actualizamos nuestro stack para que coincida. No necesitas tocar nada de tu lado: unblocker: true sigue apuntando a cualquier versión de navegador que hayamos verificado de principio a fin.
El trabajo más difícil está por venir. El fingerprinting de HTTP/3 ya está apareciendo en los anti-bots gestionados, el transporte QUIC es más complicado de imitar que el transporte antiguo, y la migración desde paquetes de headers estáticos hacia una emulación verdaderamente dinámica está comenzando. Los sitios protegidos han pasado a comprobar el orden de los frames de HTTP/2, y la brecha entre "librería que parece un navegador" y "un navegador" se va a reducir desde ambos lados. Escribiremos sobre ello cuando lo lancemos.