Почему исчерпаны попытки выполнения proxy-request

Проблема

Вызов POST /api/proxy/ возвращает ошибку без данных. Сообщение короткое и всегда имеет одну и ту же структуру:

{
  "error": "Download maxTry limit reached",
  "total": 34.812,
  "request": { "...": "..." }
}

Эта фраза выглядит одинаково независимо от того, были ли заблокированы все выходные узлы, были ли они нерабочими, или FourA успешно получал реальную страницу почти при каждой попытке, а ваши собственные правила validate ее отбрасывали. Для этих трех случаев требуются совершенно разные решения.

Ответ: attemptReport

Каждый неудачный ответ Proxy содержит объект attemptReport рядом с ошибкой. Он подсчитывает, с чем именно столкнулись попытки:

{
  "error": "Download maxTry limit reached",
  "attemptReport": {
    "total": 25,
    "noResponse": 0,
    "defense": 0,
    "contentRejected": 25,
    "statusRejected": 0,
    "other": 0,
    "vendors": [],
    "profilesTried": ["default"],
    "summary": "25 attempt(s): 25 returned HTTP 200 with no defense present and were rejected only by your validate.data - the page was fetched, your content rule did not match it"
  },
  "total": 34.812
}

Строка error намеренно оставлена без изменений, чтобы клиентская логика на ее основе продолжала работать. Читайте attemptReport.summary для получения краткого ответа в одну строку или используйте числовые показатели для ветвления логики.

Поля

Поле Тип Что подсчитывает
total integer Количество выполненных попыток
noResponse integer Выходной узел не ответил, целевой сайт не был достигнут
defense integer Сайт ответил, и в ответе обнаружен провайдер защиты от ботов
contentRejected integer HTTP 200, защиты от ботов нет, отклонено только вашим validate.data
statusRejected integer Сайт ответил, защиты от ботов нет, отклонено вашим validate.status
other integer Ответ получен, но не подошел ни под одну категорию выше
vendors string[] Все провайдеры защиты от ботов, обнаруженные в рамках задачи
profilesTried string[] Профили браузера, отправленные задачей, в порядке первого использования. default означает, что запрос ушел точно в исходном виде.
summary string Одно предложение на основе счетчиков. Подходит для логирования или показа пользователю.

Интерпретация результатов

Высокое значение contentRejected

Страницы получены. Ваше правило validate.data с ними не совпало.

Эту проблему вы можете исправить сами, и именно ее скрывают все остальные метрики: запросы выглядят как сбои по всем показателям, хотя FourA все это время доставлял реальный контент. Запросите страницу один раз через POST /api/single/ вообще без validate, посмотрите на реальный ответ и перепишите правило под него.

Частая причина: одно правило применяется к разным типам страниц. Селектор, который есть на страницах статей, но отсутствует на страницах с видео, будет падать каждый раз при попадании на страницу с видео, постоянно и с полным расходом ресурсов.

Высокое значение statusRejected

Сайт ответил, но ваше правило validate.status отклонило ответ. Если это статусы 401, 403, 429 или 503, сайт блокирует клиента, а не сообщает об отсутствии страницы. Попробуйте:

  • Другой профиль браузера (browser, os, version во внутреннем объекте request)
  • exitCountries, если контент ограничен по регионам
  • POST /api/browser/, если для прохождения проверки требуется JavaScript

Высокое значение defense

В ответах обнаружена проверка на ботов, а vendors указывает конкретную систему. Смотрите Anti-Bot Defenses, чтобы узнать, какие системы FourA обходит автоматически, а о каких только сообщает. Если провайдер защиты не обходится на этом endpoint, перенаправьте вызов в POST /api/browser/ или POST /api/auto/.

Высокое значение noResponse

Выходные узлы вообще не ответили, поэтому информацию о целевом ресурсе получить не удалось. Увеличьте maxTries, увеличьте timeout_ms и проверьте доступность URL из публичного интернета.

Высокое значение other

Ответ получен, но не классифицирован ни одной из категорий выше. Проверьте total_time относительно вашего timeout_ms: слишком медленный ответ целевого ресурса попадает сюда.

Ротация профилей браузера

Когда сайт отклоняет браузер, отправленный FourA, Proxy прекращает его использовать и пробует другое семейство из каталога публичных профилей. Это не расходует дополнительную попытку: ротация меняет параметры повторного запроса, но не влияет на сам факт его отправки.

profilesTried позволяет отслеживать этот процесс. Одна запись означает, что запрос каждый раз отправлялся в исходном виде. Несколько записей означают, что ротация сработала и сайт отклонил каждую попытку, что отличается от ситуации полного отсутствия ротации.

В успешном ответе Proxy поле profile появляется только тогда, когда в процессе ротации был выбран браузер, который вы не запрашивали:

{
  "status": 200,
  "data": "<!doctype html>...",
  "proxy": "A1B2C3",
  "profile": "...",
  "total": 4.108
}

Значение представляет собой идентификатор каталога из GET /api/profiles. Отсутствие означает, что request был отправлен в точности как указано. Наличие означает, что сработавший браузер отличался от указанного вами, поэтому передавайте этот id как profile в последующих вызовах вместо повторной отправки завершившегося ошибкой. Playground в дашборде делает это автоматически с помощью опции Carry.

Явно указанные в вашем request параметры profile, browser, os или version никогда не перезаписываются. То же касается и request с вашим собственным header User-Agent или Cookie, поскольку clearance привязан к сигнатуре, с помощью которой он был получен.

Reading It in Code

import requests

API = "https://eu.api.foura.ai"
H = {"X-API-Key": "YOUR_API_KEY", "Content-Type": "application/json"}

r = requests.post(f"{API}/api/proxy/", headers=H, json={
    "maxTries": 5,
    "request": {
        "method": "GET",
        "url": "https://example.com/product/42",
        "validate": {"data": {"accept": ["Add to cart"]}},
    },
}).json()

if "error" in r:
    rep = r.get("attemptReport", {})
    print(rep.get("summary", r["error"]))

    if rep.get("contentRejected", 0) > rep.get("total", 0) / 2:
        # The pages arrived. The validate rule is what threw them away.
        raise SystemExit("validate.data did not match the real page")
    if rep.get("defense", 0):
        print("bot check met:", ", ".join(rep.get("vendors", [])))

Связанные разделы

  • API Endpoints: Полный справочник по request и response в Proxy
  • Anti-Bot Defenses: Вендоры, defense и повторное использование clearance
  • Request Outcomes: Классификация и тарификация отклоненных response
  • Common Issues: Другие проблемы и способы их решения
  • Choosing the Right Endpoint: Когда Proxy не подходит
Обновлено: 31 августа 2026 г.