Парсер тянет 400 КБ HTML ради восьми полей, которые сайт сам себе отдаёт в JSON-ответе на 8 КБ. Разница в пятьдесят раз — это не про «красивый код», это про счёт за резидентные прокси, где вы платите за каждый гигабайт. Разбираем, как найти внутренний API сайта, что в 2026 году мешает его повторить, и когда от этой идеи стоит отказаться.
Зачем искать скрытый API, если HTML уже парсится
Почти любой современный интерфейс — React, Vue, Angular, Next.js — сначала грузит каркас страницы, а данные подтягивает отдельными запросами к собственным эндпоинтам. Эти эндпоинты не документированы, но они существуют, отвечают чистым JSON и доступны без headless-браузера.
Что вы получаете, перейдя на них:
- Трафик падает на порядок. На разборе типичной товарной выдачи HTML-страница весит около 400 КБ вместе с разметкой, стилями и трекерами, а соответствующий JSON-эндпоинт — примерно 8 КБ, причём полей в нём больше: внутренние ID, остатки, варианты товара.
- Не нужен браузер. Уходит рендеринг JavaScript, а с ним — память, процессор и десятки дополнительных запросов за шрифтами и аналитикой.
- Данные уже структурированы. Никаких селекторов, которые ломаются от смены CSS-класса.
- Меньше запросов — меньше поводов для бана. Отрисовка одной страницы каталога в браузере — это десятки обращений к сайту; тот же объём данных через API — одно.
Для проекта на резидентных прокси это прямая экономия: тариф считается по гигабайтам, и переход с рендеринга на JSON обычно сжимает счёт сильнее, чем любые ухищрения с блокировкой картинок. Смежная тема — как сократить трафик парсера в 5 раз остальными методами.
Пошагово: как найти эндпоинт
- Сначала проверьте, нет ли официального API. Загляните на
/developers,/api,/docsцелевого сайта. Публичный документированный API версионируется и предупреждает о депрекациях — приватный меняется молча. - Откройте DevTools (F12) и перейдите на вкладку Network, убедившись, что запись включена.
- Включите фильтр Fetch/XHR. Он отсекает картинки, шрифты и аналитику, оставляя только обращения за данными.
- Очистите список, чтобы убрать шум первичной загрузки.
- Спровоцируйте нужные данные: пролистайте выдачу, нажмите «следующая страница», примените фильтр, откройте карточку. Интересующий вас запрос появится в момент действия.
- Найдите ответ с вашими данными. Самый быстрый способ — Ctrl+F по панели Network: ищите уникальное значение, которое видите на экране (артикул, точную цену, кусок имени), и смотрите, какой запрос его породил.
- Скопируйте запрос целиком: правый клик по строке → Copy → Copy as cURL. Дальше конвертируйте в код через curlconverter — так вы не потеряете ни одного заголовка.
Характерные пути, на которые стоит смотреть в первую очередь: /api/, /v1/, /v2/, /search, /products, /listings, /graphql.
Особый случай: сайты на Next.js
Здесь данные часто вообще не требуют отдельного запроса — они лежат прямо в HTML. На старом Pages Router это блок __NEXT_DATA__. На App Router (Next.js 13 и новее) данные для гидратации разложены по вызовам self.__next_f.push() в нескольких script-узлах — это сериализованный payload React Server Components. Разбирать его руками неприятно: чанки ссылаются друг на друга через $-префиксы и могут быть разрезаны посреди строки. Для Python есть библиотека nextflight, которая парсит и Flight-payload из HTML, и сырой RSC-ответ (запрос с заголовком RSC: 1), а искать в нём предлагает по именам ключей, а не по индексам массива — так парсер переживает передеплой сайта.
Реверс параметров: пагинация и фильтры
Найденный эндпоинт почти всегда параметризован. Встречаются три схемы:
- По страницам:
?page=3&per_page=20 - Смещение и лимит:
?offset=40&limit=20 - Курсор:
?after=<token>&limit=20— токен следующей страницы приходит в теле предыдущего ответа
Три правила, которые экономят часы отладки:
- Останавливайтесь на пустой пачке, а не на заранее вычисленном числе страниц: счётчик
totalв приватных API врёт чаще, чем хотелось бы. - Проверяйте фактический размер пачки. Запросили 100, пришло 20 — значит, у эндпоинта свой потолок, и ваша арифметика по страницам уже неверна.
- Не лезьте на страницу 500. Глубокая пагинация почти везде обрезается сервером; вместо неё режьте выборку фильтрами — по категории, по диапазону цен, по дате.
Почему cURL из браузера работает, а ваш код — нет
Это самая частая точка провала, и причина почти всегда одна: потерянный заголовок. Скопированный cURL несёт весь контекст запроса, а самописный клиент — нет.
Что обычно оказывается обязательным:
- Кастомные заголовки с префиксом
X-—X-CSRF-Token,X-Requested-With: XMLHttpRequestи всевозможныеX-*-Token, которые фронтенд подставляет сам. Без них вы получите ответ из диапазона 400–500. Referer— контекстный заголовок, который генерируется действием пользователя. Многие эндпоинты проверяют, что запрос «пришёл со своей страницы».Authorization: Bearer <JWT>— короткоживущий токен, обычно на 15–60 минут. Хардкодить его бессмысленно: нужно уметь получать свежий.- Сессионные cookie — держите их в объекте сессии, а не копируйте руками.
- Корректный
Content-Typeдля POST:application/jsonиapplication/x-www-form-urlencodedкодируют тело по-разному, и несовпадение с объявленным типом ломает запрос молча.
Где искать сами токены, если они не в cookie: в HTML-исходнике внутри <script> (поиск по известному значению через Ctrl+F), в бандлах JavaScript, в localStorage или IndexedDB — вкладка Application в DevTools.
Подводные камни, о которых узнают поздно
Приватный API меняется без предупреждения. У него нет версионирования, обещаний совместимости и поддержки: команда фронтенда переименовывает поле в четверг вечером, и ваш парсер собирает пустоту. Защита — не «надёжный селектор», а контроль структуры: проверяйте, что обязательные поля на месте и нужного типа; следите за долей пустых значений и количеством записей в прогоне; пропускайте битые записи, но поднимайте тревогу, если брак превысил 10%; храните сырые ответы, чтобы потом было с чем сравнить.
API иногда защищён жёстче, чем страница. Встречается регулярно: HTML отдаётся спокойно, а на /api/ висит антибот, который проверяет и фингерпринт TLS, и связку заголовков. Тогда экономия трафика оборачивается ростом доли неуспешных запросов, и выигрыш съедается.
Подписанные запросы. Если в параметрах видно что-то вроде sign, hash или _s, фронтенд считает подпись в JavaScript. Воспроизводить её — отдельный проект, и часто дешевле остаться на HTML.
Ограничения по частоте. Приватные эндпоинты не рассчитаны на поток: держите 1–2 запроса в секунду, ставьте раздельные таймауты на соединение и чтение (например, 5 и 30 секунд), повторяйте только транзиентные ошибки — 429, 500, 502, 503, 504 — и не трогайте 401 и 404. Экспоненциальная задержка с джиттером обязательна, иначе все воркеры пойдут на второй круг одновременно. Подробнее — в разборе таймаутов и retry-логики для прокси.
Юридическая рамка. Публичные неаутентифицированные эндпоинты — одна ситуация, вход в аккаунт — принципиально другая: регистрация означает принятие пользовательского соглашения. Персональные данные подпадают под GDPR независимо от того, насколько легко они достаются. Факты — цены, характеристики, наличие — авторским правом не защищены, в отличие от текстов и изображений.
Когда остаться на HTML
Скрытый API — не всегда выигрыш. Оставайтесь на разборе страниц, если:
- сайт серверный и никакого внутреннего API попросту нет;
- эндпоинт требует подписи или ротации токенов — поддерживать его дороже, чем страницу;
- на API стоит более злая защита, чем на публичных страницах;
- нужен именно итоговый результат, который фронтенд собирает из нескольких источников;
- вы ведёте десятки сайтов: единый HTML-конвейер масштабируется лучше, чем зоопарк приватных API с индивидуальными причудами.
Какой тип прокси брать под API-парсинг
Переход на JSON меняет расчёт, потому что смещается узкое место: трафика становится мало, а требований к качеству IP и к стабильности сессии — больше.
- Открытый эндпоинт без авторизации и без антибота. Здесь хватает датацентр-прокси: объём данных маленький, платить за резидентные незачем.
- Эндпоинт за антиботом или с привязкой к сессии. Нужны резидентные прокси с липкой сессией: токен, cookie и IP должны совпадать на всём протяжении цепочки, иначе сервер сбросит сессию на втором запросе. При этом счёт останется скромным — гигабайты в JSON-режиме расходуются медленно.
- Данные из мобильного приложения. Если веб-версия закрыта, а приложение отдаёт то же самое проще, эндпоинты ищут через перехват трафика — это отдельная процедура, разобранная в статье про поиск скрытого API мобильного приложения через mitmproxy.
Коротко
Двадцать минут в DevTools часто заменяют дни борьбы с headless-браузером: фильтр Fetch/XHR, поиск по видимому значению, Copy as cURL — и у вас на руках рабочий запрос. Дальше решают детали: перенести все заголовки, разобрать схему пагинации, поставить валидацию ответа и трезво оценить, не защищён ли эндпоинт злее самой страницы. Там, где приватный API работает, он снижает и объём трафика, и число запросов — то есть сразу и стоимость прокси, и вероятность бана.
