Parseo de APIs GraphQL protegidas: cómo eludir los límites, APQ y el TLS fingerprinting
14/7/26


Markus_automation
Expert in data parsing and automation
Para recolectar datos de recursos web modernos, sobre todo redes sociales y aplicaciones web grandes, ya no basta con simplemente obtener páginas HTML mediante solicitudes GET. La mayoría de las plataformas populares han adoptado interfaces dinámicas y APIs, entre las que GraphQL se usa cada vez más.
Por un lado, GraphQL permite obtener datos relacionados a través de un único punto de entrada, sin tener que llamar a un montón de endpoints REST separados. En lugar de encadenar peticiones, basta con crear una sola consulta indicando exactamente qué datos se necesitan.
Por otro lado, esta arquitectura convierte el endpoint GraphQL en el elemento central de la interacción entre cliente y servidor. Y es justamente ahí donde hoy se concentra la atención de los sistemas antibot. Si antes la protección analizaba muchísimas peticiones distintas, ahora buena parte de la lógica de control gira en torno al contenido de las consultas GraphQL, su estructura, su frecuencia y su forma de uso.
Para entender por qué GraphQL resulta a la vez cómodo para el scraping e interesante desde el punto de vista de la protección, primero veamos cómo está construida esta tecnología.
Contenidos
Manténgase anónimo, aproveche el multicuentas y alcance sus objetivos con el navegador antidetección de mayor calidad del mercado.
¿Desea probar Octo Browser con un descuento?
Utilice el código promocional OCTOSCRAPER para obtener un 30% de descuento en cualquier suscripción. Esta oferta es válida solo para nuevos usuarios.
El problema del punto de entrada único
En el REST clásico, el enrutamiento ayuda a distribuir la carga y a configurar límites. El endpoint /api/users puede tener ciertas restricciones, y /api/feed, otras.
En GraphQL hay un solo punto de entrada. Toda la carga útil viaja en el cuerpo de una petición POST como JSON, que contiene la propia consulta (query), las variables (variables) y el nombre de la operación (operationName). A nivel de tráfico HTTP, se ve más o menos así:
{ "operationName": null, "variables": {}, "query": "query { users { id name posts(last: 5) { id text timestamp } } }" }
{ "operationName": null, "variables": {}, "query": "query { users { id name posts(last: 5) { id text timestamp } } }" }
Pero, para que sea más fácil de leer, estamos acostumbrados a ver esa misma consulta en su forma expandida, en forma de grafo:
query { users { id name posts(last: 5) { id text timestamp } } }
query { users { id name posts(last: 5) { id text timestamp } } }
Esto significa que, para los balanceadores de carga y los sistemas de protección superficiales, todas las peticiones se ven como llamadas idénticas a una misma URL.
Para entender qué está haciendo realmente el cliente, el script antibot tiene que inspeccionar el JSON en tiempo real, mientras que el servidor de la aplicación analiza el árbol AST correspondiente bastante pesado.
Es justo sobre esta particularidad arquitectónica que se construyen las estrategias modernas tanto de scraping como de protección.
Qué hacer si la Introspection está deshabilitada
Antes de ponerte a desarrollar un parser, es imprescindible entender cómo funciona el sistema antibot del recurso objetivo. El análisis de GraphQL arranca intentando enviar una consulta de Introspection. Es una funcionalidad integrada que obliga al servidor a devolver la documentación completa del API: todos los tipos, campos, relaciones y argumentos.

Naturalmente, en los recursos más serios el acceso a esta información está restringido. Pero aun así quedan formas de conseguirla:
Análisis de los mensajes de error. La mayoría de los servidores GraphQL intentan ayudar al usuario por defecto. Por ejemplo, si consultas un campo que no existe, digamos
nam, el servidor responde con un error del tipo:"Cannot query field "nam". Did you mean "name"?". Ese proceso se puede automatizar para ir probando distintas variantes y reconstruir, poco a poco, mucho del esquema oculto.

Filtraciones en el middleware. Muchas veces la
Introspectionestá cerrada a nivel del servidor principal de la aplicación, pero se olvidan de deshabilitarla en los microservicios o en losAPI Gateways, a los que se puede acceder directamente.
El scraping siempre requiere cierta capacidad de improvisación: hay que buscar rutas alternativas, que muchas veces quedan sin protección.
Automatic Persisted Queries (APQ)
Supongamos que lograste reconstruir el esquema, entendiste la estructura de los datos y preparaste tu propia consulta GraphQL. Pero al enviarla al servidor, en lugar de datos recibes un error de validación. Eso es obra de APQ.
Para no tener que mandar por la red textos enormes con consultas GraphQL, los desarrolladores idearon las Automatic Persisted Queries (consultas persistentes automáticas). Es una tecnología de optimización de GraphQL en la que el cliente le envía al servidor el hash (un identificador único) de la consulta, en lugar de su texto completo. Esto reduce considerablemente el tamaño de la petición, acelera la transmisión de datos y permite cachear las consultas en una CDN.
Así funciona para un cliente normal: en lugar de enviar el texto de la consulta, el frontend calcula su hash SHA-256 y envía solo este hash junto con las variables. El servidor lo compara contra su propia caché y, si reconoce el hash, ejecuta la consulta asociada.

Un ejemplo de Reddit: el frontend no envía directamente el cuerpo de la consulta (query). En su lugar, al servidor solo se le manda el nombre de la operación previamente registrado (operation: "ExposeVariant") junto con las variables
Por qué esto le complica la vida a los scrapers: no puedes simplemente escribir tu propia consulta personalizada en tu script y enviarla al servidor. El servidor espera un hash de 64 caracteres. Si envías texto plano, vas a recibir un error de validación.
Cómo trabajar con esto:
Análisis de los archivos estáticos. Todos los hashes legítimos se encuentran en los archivos JavaScript compilados del frontend. Un script de automatización primero tiene que descargar esos archivos JS, extraer de ahí la correspondencia hash → consulta, y solo después construir su propia base para el scraping.
Consultas fallback. La mecánica de APQ implica que, si el servidor no reconoce el hash, el cliente debe enviarle tanto el hash como el texto completo de la consulta. Muchos servidores dejan esta función abierta, lo que le permite al scraper registrar dinámicamente, al vuelo, cualquier consulta (incluso una que el frontend nunca contempló).
Camuflaje a nivel de transporte (TLS fingerprinting)
Para no quemar tu pool de proxies, presta atención a la capa de transporte. Empieza el camuflaje por una correcta formación del paquete TLS.
TLS (Transport Layer Security) es el protocolo que establece una conexión segura. Al establecer la conexión, el cliente envía un mensaje ClientHello con un conjunto de parámetros de cifrado. Son justamente esos parámetros los que le cuentan al servidor quién eres.
Los sistemas de protección (Cloudflare, Akamai, DataDome) analizan los fingerprints de red. Bibliotecas básicas, como requests en Python o axios en Node.js, tienen firmas muy marcadas y fácilmente reconocibles. Se diferencian de un Google Chrome o un Safari reales porque envían, en el paquete ClientHello, un orden de cifrados distinto, otras versiones de protocolo y otras extensiones.
Para la recolección masiva de datos existen dos soluciones:
Bibliotecas especializadas. Deja de lado los clientes HTTP estándar en favor de herramientas capaces de imitar fingerprints de TLS (
curl-cffipara Python otls-clienten Go). Esto permite emular el comportamiento de un navegador a nivel de las peticiones y mantener una alta velocidad de trabajo.Navegadores antidetección. Si la protección del recurso objetivo es especialmente agresiva, puedes utilizar un sistema más avanzado: un navegador antidetección, como Octo Browser. En este caso, la suplantación de los fingerprints TLS ocurre directamente a nivel del motor del navegador propio. Es la forma de camuflaje más confiable, aunque no la más rápida. El servidor ve el comportamiento de un navegador real, lo que reduce al mínimo el riesgo de bloqueo.
Cómo evitar el Rate Limiting
En cuanto el parser se pone en marcha, es posible toparse con otra capa de protección: el Rate Limiting (limitación de la frecuencia de peticiones).
En una API REST, los sistemas de protección suelen contar la cantidad de peticiones HTTP por segundo desde una misma dirección IP. Pero la particularidad de GraphQL permite extraer enormes volúmenes de datos relacionados en una sola llamada HTTP. Los parsers usan activamente dos funcionalidades propias del lenguaje para sortear los contadores clásicos de peticiones:
Peticiones por lotes (batching). Originalmente, esta funcionalidad se pensó para que el frontend pudiera agrupar varias consultas independientes al API en un solo array JSON y enviarlas todas juntas. Para quien desarrolla un parser, esto significa que, en lugar de hacer 100 llamadas independientes para obtener 100 perfiles de usuario, se puede enviar una sola petición que contenga un array de 100 objetos de operación. Si el Rate Limiter solo analiza el nivel HTTP, va a contar esa petición como una sola.
Alias. Si el servidor bloquea los arrays JSON (el batching está deshabilitado), puede utilizarse una característica propia de GraphQL: los alias. Permiten pedir el mismo campo varias veces, con argumentos distintos, dentro de una sola operación. Esto le genera carga a la base de datos del servidor, pero para el firewall de red se ve como una sola petición legítima.
query GetMultipleUsers { user_1: user(id: "1001") { name, email } user_2: user(id: "1002") { name, email } # … and so on }
query GetMultipleUsers { user_1: user(id: "1001") { name, email } user_2: user(id: "1002") { name, email } # … and so on }
Sin embargo, hay que tener en cuenta que hoy en día ni el batching ni los alias son una solución garantizada.
En lugar de contadores simples de peticiones, se está implementando el análisis de costo de consulta (Query Cost Analysis). A cada campo y relación del esquema se le asigna un peso propio. Un campo escalar simple (name) puede costar un punto, y una consulta de una lista anidada (posts(last: 100)), 10 puntos. El servidor parsea tu consulta antes de ejecutarla, suma el puntaje total y, si 500 alias superan un límite de, digamos, 1.000 puntos, la petición se rechaza con un error del tipo Max query complexity exceeded.
Además, los sistemas de protección vigilan de cerca la profundidad de anidamiento. Si un scraper intenta traer, en una sola consulta, un árbol recursivo de relaciones (usuario → sus amigos → posts de los amigos → comentarios de esos posts), se va a topar definitivamente con el límite de seguridad.
Cómo trabajar con esto: en lugar de buscar el tamaño perfecto para una sola consulta profunda (dividiéndola no en 500 alias, sino en lotes de 20 a 30), conviene rediseñar la arquitectura del scraper hacia una recolección plana:
Consultas planas. Primero se ejecuta una consulta ligera de nivel superior, que extrae solo una lista plana de identificadores de los objetos necesarios, sin sus relaciones.
Chunking y workers. Los identificadores obtenidos se dividen en lotes pequeños y seguros (chunks). Después, un conjunto de workers ejecutados en paralelo solicita los datos detallados correspondientes a esos identificadores.
Reconstrucción local del grafo. La estructura relacional se reconstruye posteriormente del lado del parser. Los datos se guardan localmente (o en una base de datos intermedia) y se combinan de forma programática.
Este enfoque exige lógica de orquestación adicional y almacenamiento en caché local. A cambio, garantiza un trabajo estable y multihilo, sin disparar los triggers del WAF, sin shadow bans y sin timeouts de la base de datos del recurso objetivo.
Conclusión
El scraping de las APIs GraphQL modernas exige una interacción completa con sistemas distribuidos complejos: tener en cuenta la capa de transporte (emulación de TLS), entender cómo funciona el AST y comprender el cacheo del lado del servidor.
Los sistemas de protección siguen evolucionando, pero la flexibilidad arquitectónica que hace de GraphQL una tecnología tan cómoda para los desarrolladores también deja abiertas numerosas oportunidades para sistemas de recopilación de datos bien diseñados.
Manténgase anónimo, aproveche el multicuentas y alcance sus objetivos con el navegador antidetección de mayor calidad del mercado.
¿Desea probar Octo Browser con un descuento?
Utilice el código promocional OCTOSCRAPER para obtener un 30% de descuento en cualquier suscripción. Esta oferta es válida solo para nuevos usuarios.
El problema del punto de entrada único
En el REST clásico, el enrutamiento ayuda a distribuir la carga y a configurar límites. El endpoint /api/users puede tener ciertas restricciones, y /api/feed, otras.
En GraphQL hay un solo punto de entrada. Toda la carga útil viaja en el cuerpo de una petición POST como JSON, que contiene la propia consulta (query), las variables (variables) y el nombre de la operación (operationName). A nivel de tráfico HTTP, se ve más o menos así:
{ "operationName": null, "variables": {}, "query": "query { users { id name posts(last: 5) { id text timestamp } } }" }
Pero, para que sea más fácil de leer, estamos acostumbrados a ver esa misma consulta en su forma expandida, en forma de grafo:
query { users { id name posts(last: 5) { id text timestamp } } }
Esto significa que, para los balanceadores de carga y los sistemas de protección superficiales, todas las peticiones se ven como llamadas idénticas a una misma URL.
Para entender qué está haciendo realmente el cliente, el script antibot tiene que inspeccionar el JSON en tiempo real, mientras que el servidor de la aplicación analiza el árbol AST correspondiente bastante pesado.
Es justo sobre esta particularidad arquitectónica que se construyen las estrategias modernas tanto de scraping como de protección.
Qué hacer si la Introspection está deshabilitada
Antes de ponerte a desarrollar un parser, es imprescindible entender cómo funciona el sistema antibot del recurso objetivo. El análisis de GraphQL arranca intentando enviar una consulta de Introspection. Es una funcionalidad integrada que obliga al servidor a devolver la documentación completa del API: todos los tipos, campos, relaciones y argumentos.

Naturalmente, en los recursos más serios el acceso a esta información está restringido. Pero aun así quedan formas de conseguirla:
Análisis de los mensajes de error. La mayoría de los servidores GraphQL intentan ayudar al usuario por defecto. Por ejemplo, si consultas un campo que no existe, digamos
nam, el servidor responde con un error del tipo:"Cannot query field "nam". Did you mean "name"?". Ese proceso se puede automatizar para ir probando distintas variantes y reconstruir, poco a poco, mucho del esquema oculto.

Filtraciones en el middleware. Muchas veces la
Introspectionestá cerrada a nivel del servidor principal de la aplicación, pero se olvidan de deshabilitarla en los microservicios o en losAPI Gateways, a los que se puede acceder directamente.
El scraping siempre requiere cierta capacidad de improvisación: hay que buscar rutas alternativas, que muchas veces quedan sin protección.
Automatic Persisted Queries (APQ)
Supongamos que lograste reconstruir el esquema, entendiste la estructura de los datos y preparaste tu propia consulta GraphQL. Pero al enviarla al servidor, en lugar de datos recibes un error de validación. Eso es obra de APQ.
Para no tener que mandar por la red textos enormes con consultas GraphQL, los desarrolladores idearon las Automatic Persisted Queries (consultas persistentes automáticas). Es una tecnología de optimización de GraphQL en la que el cliente le envía al servidor el hash (un identificador único) de la consulta, en lugar de su texto completo. Esto reduce considerablemente el tamaño de la petición, acelera la transmisión de datos y permite cachear las consultas en una CDN.
Así funciona para un cliente normal: en lugar de enviar el texto de la consulta, el frontend calcula su hash SHA-256 y envía solo este hash junto con las variables. El servidor lo compara contra su propia caché y, si reconoce el hash, ejecuta la consulta asociada.

Un ejemplo de Reddit: el frontend no envía directamente el cuerpo de la consulta (query). En su lugar, al servidor solo se le manda el nombre de la operación previamente registrado (operation: "ExposeVariant") junto con las variables
Por qué esto le complica la vida a los scrapers: no puedes simplemente escribir tu propia consulta personalizada en tu script y enviarla al servidor. El servidor espera un hash de 64 caracteres. Si envías texto plano, vas a recibir un error de validación.
Cómo trabajar con esto:
Análisis de los archivos estáticos. Todos los hashes legítimos se encuentran en los archivos JavaScript compilados del frontend. Un script de automatización primero tiene que descargar esos archivos JS, extraer de ahí la correspondencia hash → consulta, y solo después construir su propia base para el scraping.
Consultas fallback. La mecánica de APQ implica que, si el servidor no reconoce el hash, el cliente debe enviarle tanto el hash como el texto completo de la consulta. Muchos servidores dejan esta función abierta, lo que le permite al scraper registrar dinámicamente, al vuelo, cualquier consulta (incluso una que el frontend nunca contempló).
Camuflaje a nivel de transporte (TLS fingerprinting)
Para no quemar tu pool de proxies, presta atención a la capa de transporte. Empieza el camuflaje por una correcta formación del paquete TLS.
TLS (Transport Layer Security) es el protocolo que establece una conexión segura. Al establecer la conexión, el cliente envía un mensaje ClientHello con un conjunto de parámetros de cifrado. Son justamente esos parámetros los que le cuentan al servidor quién eres.
Los sistemas de protección (Cloudflare, Akamai, DataDome) analizan los fingerprints de red. Bibliotecas básicas, como requests en Python o axios en Node.js, tienen firmas muy marcadas y fácilmente reconocibles. Se diferencian de un Google Chrome o un Safari reales porque envían, en el paquete ClientHello, un orden de cifrados distinto, otras versiones de protocolo y otras extensiones.
Para la recolección masiva de datos existen dos soluciones:
Bibliotecas especializadas. Deja de lado los clientes HTTP estándar en favor de herramientas capaces de imitar fingerprints de TLS (
curl-cffipara Python otls-clienten Go). Esto permite emular el comportamiento de un navegador a nivel de las peticiones y mantener una alta velocidad de trabajo.Navegadores antidetección. Si la protección del recurso objetivo es especialmente agresiva, puedes utilizar un sistema más avanzado: un navegador antidetección, como Octo Browser. En este caso, la suplantación de los fingerprints TLS ocurre directamente a nivel del motor del navegador propio. Es la forma de camuflaje más confiable, aunque no la más rápida. El servidor ve el comportamiento de un navegador real, lo que reduce al mínimo el riesgo de bloqueo.
Cómo evitar el Rate Limiting
En cuanto el parser se pone en marcha, es posible toparse con otra capa de protección: el Rate Limiting (limitación de la frecuencia de peticiones).
En una API REST, los sistemas de protección suelen contar la cantidad de peticiones HTTP por segundo desde una misma dirección IP. Pero la particularidad de GraphQL permite extraer enormes volúmenes de datos relacionados en una sola llamada HTTP. Los parsers usan activamente dos funcionalidades propias del lenguaje para sortear los contadores clásicos de peticiones:
Peticiones por lotes (batching). Originalmente, esta funcionalidad se pensó para que el frontend pudiera agrupar varias consultas independientes al API en un solo array JSON y enviarlas todas juntas. Para quien desarrolla un parser, esto significa que, en lugar de hacer 100 llamadas independientes para obtener 100 perfiles de usuario, se puede enviar una sola petición que contenga un array de 100 objetos de operación. Si el Rate Limiter solo analiza el nivel HTTP, va a contar esa petición como una sola.
Alias. Si el servidor bloquea los arrays JSON (el batching está deshabilitado), puede utilizarse una característica propia de GraphQL: los alias. Permiten pedir el mismo campo varias veces, con argumentos distintos, dentro de una sola operación. Esto le genera carga a la base de datos del servidor, pero para el firewall de red se ve como una sola petición legítima.
query GetMultipleUsers { user_1: user(id: "1001") { name, email } user_2: user(id: "1002") { name, email } # … and so on }
Sin embargo, hay que tener en cuenta que hoy en día ni el batching ni los alias son una solución garantizada.
En lugar de contadores simples de peticiones, se está implementando el análisis de costo de consulta (Query Cost Analysis). A cada campo y relación del esquema se le asigna un peso propio. Un campo escalar simple (name) puede costar un punto, y una consulta de una lista anidada (posts(last: 100)), 10 puntos. El servidor parsea tu consulta antes de ejecutarla, suma el puntaje total y, si 500 alias superan un límite de, digamos, 1.000 puntos, la petición se rechaza con un error del tipo Max query complexity exceeded.
Además, los sistemas de protección vigilan de cerca la profundidad de anidamiento. Si un scraper intenta traer, en una sola consulta, un árbol recursivo de relaciones (usuario → sus amigos → posts de los amigos → comentarios de esos posts), se va a topar definitivamente con el límite de seguridad.
Cómo trabajar con esto: en lugar de buscar el tamaño perfecto para una sola consulta profunda (dividiéndola no en 500 alias, sino en lotes de 20 a 30), conviene rediseñar la arquitectura del scraper hacia una recolección plana:
Consultas planas. Primero se ejecuta una consulta ligera de nivel superior, que extrae solo una lista plana de identificadores de los objetos necesarios, sin sus relaciones.
Chunking y workers. Los identificadores obtenidos se dividen en lotes pequeños y seguros (chunks). Después, un conjunto de workers ejecutados en paralelo solicita los datos detallados correspondientes a esos identificadores.
Reconstrucción local del grafo. La estructura relacional se reconstruye posteriormente del lado del parser. Los datos se guardan localmente (o en una base de datos intermedia) y se combinan de forma programática.
Este enfoque exige lógica de orquestación adicional y almacenamiento en caché local. A cambio, garantiza un trabajo estable y multihilo, sin disparar los triggers del WAF, sin shadow bans y sin timeouts de la base de datos del recurso objetivo.
Conclusión
El scraping de las APIs GraphQL modernas exige una interacción completa con sistemas distribuidos complejos: tener en cuenta la capa de transporte (emulación de TLS), entender cómo funciona el AST y comprender el cacheo del lado del servidor.
Los sistemas de protección siguen evolucionando, pero la flexibilidad arquitectónica que hace de GraphQL una tecnología tan cómoda para los desarrolladores también deja abiertas numerosas oportunidades para sistemas de recopilación de datos bien diseñados.
Mantente al día de las últimas noticias sobre Octo Browser
Al hacer clic en el botón, aceptas nuestra Política de privacidad.
Mantente al día de las últimas noticias sobre Octo Browser
Al hacer clic en el botón, aceptas nuestra Política de privacidad.
Mantente al día de las últimas noticias sobre Octo Browser
Al hacer clic en el botón, aceptas nuestra Política de privacidad.

Únete a Octo Browser ahora
O contacta al servicio al cliente en cualquier momento si tienes alguna pregunta.

Únete a Octo Browser ahora
O contacta al servicio al cliente en cualquier momento si tienes alguna pregunta.
Únete a Octo Browser ahora
O contacta al servicio al cliente en cualquier momento si tienes alguna pregunta.