Парсинг защищенных GraphQL API: как обходить лимиты, APQ и TLS-фингерпринтинг
14.07.2026


Markus_automation
Expert in data parsing and automation
Для сбора данных с современных веб-ресурсов, особенно социальных сетей и крупных веб-приложений, недостаточно просто получать HTML-страницы по GET-запросу. Большая часть популярных платформ перешла на динамические интерфейсы и API, среди которых все чаще используется GraphQL.
С одной стороны, GraphQL позволяет получать связанные данные через одну точку входа, не обращаясь ко множеству отдельных REST-эндпойнтов. Вместо цепочки запросов достаточно сформировать один запрос и указать, какие именно данные нужны.
С другой стороны, такая архитектура делает GraphQL-эндпойнт центральным элементом взаимодействия между клиентом и сервером. В результате именно на него сегодня направлено внимание антибот-систем. Если раньше защита анализировала множество разных запросов, то теперь значительная часть логики контроля сосредоточена вокруг содержимого GraphQL-запросов, их структуры, частоты и характера использования.
Чтобы понять, почему GraphQL одновременно удобен для парсинга и интересен с точки зрения защиты, давайте разберемся, как устроена эта технология.
Содержание
Сохраняйте анонимность, используйте преимущества мультиаккаунтинга и добивайтесь своих целей с самым качественным решением на рынке антидетект-браузеров.
Хотите попробовать Octo Browser со скидкой?
По промокоду OCTOSCRAPER получите 30% скидку на любую подписку. Предложение действительно только для новых пользователей.
Проблема единой точки входа
В классическом REST маршрутизация помогает распределять нагрузку и настраивать лимиты. Эндпойнт /api/users может иметь одни ограничения, а /api/feed другие.
В GraphQL точка входа одна. Вся полезная нагрузка передается в теле POST-запроса в виде JSON, содержащего сам запрос (query), переменные (variables) и имя операции (operationName). На уровне HTTP-трафика это выглядит примерно так:
{ "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 } } }" }
Но для удобства чтения мы привыкли видеть этот же запрос в его развернутом, графовом виде:
query { users { id name posts(last: 5) { id text timestamp } } }
query { users { id name posts(last: 5) { id text timestamp } } }
Это означает, что для -балансировщиков нагрузки и поверхностных систем защиты все запросы выглядят как одинаковые обращения к одному URL.
Чтобы понять, что именно делает клиент, антибот-скрипт пытается инспектировать JSON на лету, а сервер приложения уже парсит тяжелое AST-дерево.
Именно на этой архитектурной особенности и строятся современные стратегии парсинга и защиты.
Что делать, если Introspection отключен
Перед тем как начать разрабатывать парсер, обязательно выясните, как устроена антибот-система на целевом ресурсе. Анализ GraphQL начинается с попытки отправить запрос Introspection. Это встроенная фича, заставляющая сервер отдать полную документацию по API: все типы, поля, связи и аргументы.

Естественно, на серьезных ресурсах доступ к этой информации ограничен. Но все равно остаются способы ее получить:
Анализ сообщений об ошибках. Большинство серверов GraphQL из коробки пытаются помочь пользователю. Например, если вы запрашиваете несуществующее поле
nam, сервер ответит ошибкой:"Cannot query field "nam". Did you mean "name"?". Можно автоматизировать этот процесс подбора и по частям восстановить значимую часть скрытой схемы.

Утечки в промежуточном ПО. Часто
Introspectionзакрывают на уровне основного сервера приложения, но забывают отключить на микросервисах или шлюзах (API Gateways), к которым можно обратиться напрямую.
Парсинг данных всегда требует импровизации — ищите обходные пути, они часто остаются без защиты.
Automatic Persisted Queries (APQ)
Допустим, вы успешно восстановили схему, поняли структуру данных и подготовили свой GraphQL-запрос. Но при отправке его на сервер вместо данных вы получите ошибку валидации. Это работа APQ.
Чтобы не гонять по сети огромные тексты с графовыми запросами, разработчики придумали Automatic Persisted Queries (автоматические сохраненные запросы). Это технология оптимизации GraphQL, при которой клиент отправляет на сервер хеш (уникальный идентификатор) запроса вместо его полного текста. Это значительно уменьшает размер запроса, ускоряет передачу данных и позволяет кэшировать запросы на CDN.
Как это работает для обычного клиента: вместо того чтобы отправлять сам текст запроса, фронтенд берет от него хеш SHA-256 и отправляет только его вместе с переменными. Сервер сверяется с собственным кэшем и, если этот хеш известен, выполняет связанный с ним запрос.

Пример с сайта Reddit. Фронтенд вообще не передает тело запроса (query). Вместо этого на сервер отправляется только заранее зарегистрированное имя операции (operation: "ExposeVariant") и переменные
Как это усложняет жизнь парсерам: вы не можете просто написать свой кастомный запрос в скрипте и отправить его на сервер. Сервер ожидает 64-символьный хеш. Если вы отправите чистый текст, то получите ошибку валидации.
Методы обхода:
Парсинг статики. Все легитимные хеши лежат в скомпилированных JavaScript-файлах фронтенда. Скрипт-автоматизатор должен сначала скачать эти JS-файлы, вытащить оттуда маппинг хеш -> запрос и только потом формировать базу для парсинга.
Fallback-запросы. Механика APQ подразумевает, что, если сервер не знает хеш, клиент должен отправить ему и хеш, и полный текст запроса. Многие серверы оставляют эту функцию открытой, позволяя парсеру динамически регистрировать любые (даже не предусмотренные фронтендом) запросы на лету.
Маскировка на транспортном уровне (TLS Fingerprinting)
Чтобы не сжечь ваш пул прокси-адресов, обратите внимание на транспортный уровень. Начните маскировку с правильного формирования TLS-пакета.
TLS (Transport Layer Security) — это протокол, обеспечивающий защищенное соединение. При установке связи клиент отправляет сообщение ClientHello с набором параметров шифрования. Именно эти параметры рассказывают о вас серверу.
Защитные системы (Cloudflare, Akamai, DataDome) анализируют сетевые отпечатки. Базовые библиотеки, такие как requests в Python или axios в Node.js, имеют яркие, легко узнаваемые сигнатуры. Они отличаются от реального Google Chrome или Safari, потому что передают в пакете ClientHello другой порядок шифров, другие версии протоколов и расширений.
Для массового сбора данных есть два решения:
Специализированные библиотеки. Отказ от стандартных HTTP-клиентов в пользу инструментов, умеющих имитировать TLS-отпечатки (
curl-cffiдля Python илиtls-clientна Go). Это позволяет эмулировать поведение браузера на уровне запросов и сохранять высокую скорость работы.Антидетект-браузеры. Если защита на целевом ресурсе слишком агрессивная, можно подключить продвинутую систему в виде антидетект-браузера, такого как Octo Browser. Подмена TLS-отпечатков в этом случае происходит на уровне самого движка. Это самый надежный способ маскировки, пусть и не самый быстрый. Сервер видит поведение реального браузера, что сводит риск блокировки к минимуму.
Обход Rate Limiting
Как только парсер начнет работать, вы можете столкнуться с еще одним уровнем защиты — Rate Limiting (ограничение частоты запросов).
В REST API системы защиты обычно считают количество HTTP-запросов в секунду с одного IP-адреса. Однако специфика GraphQL позволяет извлекать огромные объемы связанных данных за один HTTP-вызов. Парсеры активно используют две встроенные фичи языка, чтобы обходить классические счетчики запросов:
Пакетные запросы (батчинг). Изначально эта фича создавалась для того, чтобы фронтенд мог объединить несколько независимых запросов к API в один массив JSON и отправить их разом. Для разработчика парсера это означает, что вместо 100 отдельных обращений для получения 100 профилей пользователей можно отправить запрос, содержащий массив из 100 объектов операций. Если Rate Limiter анализирует только HTTP-уровень, он засчитает этот запрос за один.
Алиасы. Если сервер блокирует массивы JSON (батчинг отключен), используется фича самого языка GraphQL — алиасы. Она позволяет запросить одно и то же поле множество раз с разными аргументами внутри одной операции. Это создает нагрузку на базу данных сервера, но для сетевого экрана выглядит как один легитимный запрос.
query GetMultipleUsers { user_1: user(id: "1001") { name, email } user_2: user(id: "1002") { name, email } # ... и так далее }
query GetMultipleUsers { user_1: user(id: "1001") { name, email } user_2: user(id: "1002") { name, email } # ... и так далее }
Однако нужно понимать, что сегодня ни батчинг, ни алиасы не являются гарантированным решением проблем.
Вместо простых счетчиков запросов внедряется анализ стоимости запроса (Query Cost Analysis). Каждому полю и связи в схеме назначается свой вес. Простое скалярное поле (name) может стоить один балл, а запрос вложенного списка (posts(last: 100)) — 10 баллов. Сервер парсит ваш запрос до выполнения, считает общую сумму баллов, и, если 500 алиасов превышают лимит в условные 1 000 баллов — запрос отклоняется с ошибкой Max query complexity exceeded.
Также защитные системы жестко отслеживают глубину вложенности. Если парсер попытается одним запросом вытащить рекурсивное дерево связей (пользователь -> его друзья -> посты друзей -> комментарии к постам), он гарантированно упрется в лимит безопасности.
Как с этим работать: вместо того чтобы подбирать идеальный размер одного глубокого запроса (дробление запроса не на 500 алиасов, а на батчи по 20–30 штук), можно перестроить архитектуру парсера под плоскую выборку:
Плоские запросы. Сначала выполняется легкий запрос верхнего уровня, извлекающий только плоский список ID нужных объектов без их связей.
Чанкинг и воркеры. Полученные ID разбиваются на небольшие безопасные пакеты (чанки). Затем пул воркеров в параллельных потоках запрашивает детализацию по этим ID.
Локальная сборка графа. Реляционная структура восстанавливается уже на стороне парсера. Данные сохраняются локально (или в промежуточную базу данных) и объединяются программно.
Такой подход требует дополнительной логики оркестрации и локального кэширования. Зато он гарантирует стабильную, многопоточную работу без срабатывания триггеров WAF, теневых банов и тайм-аутов от базы данных целевого ресурса.
Итог
Парсинг современных GraphQL API требует полноценного взаимодействия со сложными распределенными системами. Это учет транспортного уровня (эмуляция TLS), понимание специфики работы AST и кэширования на стороне сервера.
Защита эволюционирует, но архитектурная гибкость, которая делает GraphQL удобным для разработчиков, оставляет множество лазеек для грамотно спроектированных систем сбора данных.
Сохраняйте анонимность, используйте преимущества мультиаккаунтинга и добивайтесь своих целей с самым качественным решением на рынке антидетект-браузеров.
Хотите попробовать Octo Browser со скидкой?
По промокоду OCTOSCRAPER получите 30% скидку на любую подписку. Предложение действительно только для новых пользователей.
Проблема единой точки входа
В классическом REST маршрутизация помогает распределять нагрузку и настраивать лимиты. Эндпойнт /api/users может иметь одни ограничения, а /api/feed другие.
В GraphQL точка входа одна. Вся полезная нагрузка передается в теле POST-запроса в виде JSON, содержащего сам запрос (query), переменные (variables) и имя операции (operationName). На уровне HTTP-трафика это выглядит примерно так:
{ "operationName": null, "variables": {}, "query": "query { users { id name posts(last: 5) { id text timestamp } } }" }
Но для удобства чтения мы привыкли видеть этот же запрос в его развернутом, графовом виде:
query { users { id name posts(last: 5) { id text timestamp } } }
Это означает, что для -балансировщиков нагрузки и поверхностных систем защиты все запросы выглядят как одинаковые обращения к одному URL.
Чтобы понять, что именно делает клиент, антибот-скрипт пытается инспектировать JSON на лету, а сервер приложения уже парсит тяжелое AST-дерево.
Именно на этой архитектурной особенности и строятся современные стратегии парсинга и защиты.
Что делать, если Introspection отключен
Перед тем как начать разрабатывать парсер, обязательно выясните, как устроена антибот-система на целевом ресурсе. Анализ GraphQL начинается с попытки отправить запрос Introspection. Это встроенная фича, заставляющая сервер отдать полную документацию по API: все типы, поля, связи и аргументы.

Естественно, на серьезных ресурсах доступ к этой информации ограничен. Но все равно остаются способы ее получить:
Анализ сообщений об ошибках. Большинство серверов GraphQL из коробки пытаются помочь пользователю. Например, если вы запрашиваете несуществующее поле
nam, сервер ответит ошибкой:"Cannot query field "nam". Did you mean "name"?". Можно автоматизировать этот процесс подбора и по частям восстановить значимую часть скрытой схемы.

Утечки в промежуточном ПО. Часто
Introspectionзакрывают на уровне основного сервера приложения, но забывают отключить на микросервисах или шлюзах (API Gateways), к которым можно обратиться напрямую.
Парсинг данных всегда требует импровизации — ищите обходные пути, они часто остаются без защиты.
Automatic Persisted Queries (APQ)
Допустим, вы успешно восстановили схему, поняли структуру данных и подготовили свой GraphQL-запрос. Но при отправке его на сервер вместо данных вы получите ошибку валидации. Это работа APQ.
Чтобы не гонять по сети огромные тексты с графовыми запросами, разработчики придумали Automatic Persisted Queries (автоматические сохраненные запросы). Это технология оптимизации GraphQL, при которой клиент отправляет на сервер хеш (уникальный идентификатор) запроса вместо его полного текста. Это значительно уменьшает размер запроса, ускоряет передачу данных и позволяет кэшировать запросы на CDN.
Как это работает для обычного клиента: вместо того чтобы отправлять сам текст запроса, фронтенд берет от него хеш SHA-256 и отправляет только его вместе с переменными. Сервер сверяется с собственным кэшем и, если этот хеш известен, выполняет связанный с ним запрос.

Пример с сайта Reddit. Фронтенд вообще не передает тело запроса (query). Вместо этого на сервер отправляется только заранее зарегистрированное имя операции (operation: "ExposeVariant") и переменные
Как это усложняет жизнь парсерам: вы не можете просто написать свой кастомный запрос в скрипте и отправить его на сервер. Сервер ожидает 64-символьный хеш. Если вы отправите чистый текст, то получите ошибку валидации.
Методы обхода:
Парсинг статики. Все легитимные хеши лежат в скомпилированных JavaScript-файлах фронтенда. Скрипт-автоматизатор должен сначала скачать эти JS-файлы, вытащить оттуда маппинг хеш -> запрос и только потом формировать базу для парсинга.
Fallback-запросы. Механика APQ подразумевает, что, если сервер не знает хеш, клиент должен отправить ему и хеш, и полный текст запроса. Многие серверы оставляют эту функцию открытой, позволяя парсеру динамически регистрировать любые (даже не предусмотренные фронтендом) запросы на лету.
Маскировка на транспортном уровне (TLS Fingerprinting)
Чтобы не сжечь ваш пул прокси-адресов, обратите внимание на транспортный уровень. Начните маскировку с правильного формирования TLS-пакета.
TLS (Transport Layer Security) — это протокол, обеспечивающий защищенное соединение. При установке связи клиент отправляет сообщение ClientHello с набором параметров шифрования. Именно эти параметры рассказывают о вас серверу.
Защитные системы (Cloudflare, Akamai, DataDome) анализируют сетевые отпечатки. Базовые библиотеки, такие как requests в Python или axios в Node.js, имеют яркие, легко узнаваемые сигнатуры. Они отличаются от реального Google Chrome или Safari, потому что передают в пакете ClientHello другой порядок шифров, другие версии протоколов и расширений.
Для массового сбора данных есть два решения:
Специализированные библиотеки. Отказ от стандартных HTTP-клиентов в пользу инструментов, умеющих имитировать TLS-отпечатки (
curl-cffiдля Python илиtls-clientна Go). Это позволяет эмулировать поведение браузера на уровне запросов и сохранять высокую скорость работы.Антидетект-браузеры. Если защита на целевом ресурсе слишком агрессивная, можно подключить продвинутую систему в виде антидетект-браузера, такого как Octo Browser. Подмена TLS-отпечатков в этом случае происходит на уровне самого движка. Это самый надежный способ маскировки, пусть и не самый быстрый. Сервер видит поведение реального браузера, что сводит риск блокировки к минимуму.
Обход Rate Limiting
Как только парсер начнет работать, вы можете столкнуться с еще одним уровнем защиты — Rate Limiting (ограничение частоты запросов).
В REST API системы защиты обычно считают количество HTTP-запросов в секунду с одного IP-адреса. Однако специфика GraphQL позволяет извлекать огромные объемы связанных данных за один HTTP-вызов. Парсеры активно используют две встроенные фичи языка, чтобы обходить классические счетчики запросов:
Пакетные запросы (батчинг). Изначально эта фича создавалась для того, чтобы фронтенд мог объединить несколько независимых запросов к API в один массив JSON и отправить их разом. Для разработчика парсера это означает, что вместо 100 отдельных обращений для получения 100 профилей пользователей можно отправить запрос, содержащий массив из 100 объектов операций. Если Rate Limiter анализирует только HTTP-уровень, он засчитает этот запрос за один.
Алиасы. Если сервер блокирует массивы JSON (батчинг отключен), используется фича самого языка GraphQL — алиасы. Она позволяет запросить одно и то же поле множество раз с разными аргументами внутри одной операции. Это создает нагрузку на базу данных сервера, но для сетевого экрана выглядит как один легитимный запрос.
query GetMultipleUsers { user_1: user(id: "1001") { name, email } user_2: user(id: "1002") { name, email } # ... и так далее }
Однако нужно понимать, что сегодня ни батчинг, ни алиасы не являются гарантированным решением проблем.
Вместо простых счетчиков запросов внедряется анализ стоимости запроса (Query Cost Analysis). Каждому полю и связи в схеме назначается свой вес. Простое скалярное поле (name) может стоить один балл, а запрос вложенного списка (posts(last: 100)) — 10 баллов. Сервер парсит ваш запрос до выполнения, считает общую сумму баллов, и, если 500 алиасов превышают лимит в условные 1 000 баллов — запрос отклоняется с ошибкой Max query complexity exceeded.
Также защитные системы жестко отслеживают глубину вложенности. Если парсер попытается одним запросом вытащить рекурсивное дерево связей (пользователь -> его друзья -> посты друзей -> комментарии к постам), он гарантированно упрется в лимит безопасности.
Как с этим работать: вместо того чтобы подбирать идеальный размер одного глубокого запроса (дробление запроса не на 500 алиасов, а на батчи по 20–30 штук), можно перестроить архитектуру парсера под плоскую выборку:
Плоские запросы. Сначала выполняется легкий запрос верхнего уровня, извлекающий только плоский список ID нужных объектов без их связей.
Чанкинг и воркеры. Полученные ID разбиваются на небольшие безопасные пакеты (чанки). Затем пул воркеров в параллельных потоках запрашивает детализацию по этим ID.
Локальная сборка графа. Реляционная структура восстанавливается уже на стороне парсера. Данные сохраняются локально (или в промежуточную базу данных) и объединяются программно.
Такой подход требует дополнительной логики оркестрации и локального кэширования. Зато он гарантирует стабильную, многопоточную работу без срабатывания триггеров WAF, теневых банов и тайм-аутов от базы данных целевого ресурса.
Итог
Парсинг современных GraphQL API требует полноценного взаимодействия со сложными распределенными системами. Это учет транспортного уровня (эмуляция TLS), понимание специфики работы AST и кэширования на стороне сервера.
Защита эволюционирует, но архитектурная гибкость, которая делает GraphQL удобным для разработчиков, оставляет множество лазеек для грамотно спроектированных систем сбора данных.
Следите за последними новостями Octo Browser
Нажимая кнопку, вы соглашаетесь с нашей политикой конфиденциальности.
Следите за последними новостями Octo Browser
Нажимая кнопку, вы соглашаетесь с нашей политикой конфиденциальности.
Следите за последними новостями Octo Browser
Нажимая кнопку, вы соглашаетесь с нашей политикой конфиденциальности.

Присоединяйтесь к Octo Browser сейчас
Вы можете обращаться за помощью к нашим специалистам службы поддержки в чате в любое время.

Присоединяйтесь к Octo Browser сейчас
Вы можете обращаться за помощью к нашим специалистам службы поддержки в чате в любое время.
Присоединяйтесь к Octo Browser сейчас
Вы можете обращаться за помощью к нашим специалистам службы поддержки в чате в любое время.