Защо proxy заявката изчерпа опитите си

Проблемът

Дадено POST /api/proxy/ извикване се връща с грешка и без данни. Съобщението е кратко и винаги със същата структура:

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

Това изречение звучи по един и същи начин, независимо дали всеки изход е бил блокиран, всеки изход е бил неактивен, или FourA е извлякъл реалната страница при почти всеки опит, а вашите собствени validate правила са я отхвърлили. Тези три случая изискват напълно противоположни решения.

Отговорът: attemptReport

Всеки неуспешен Proxy response съдържа 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 response полето profile се появява само когато ротацията е избрала браузър, който не сте заявили:

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

Стойността е catalogue id от GET /api/profiles. Липсата му означава, че заявката е изпратена точно както е написана. Наличието му означава, че браузърът, който е сработил, не е бил този, който сте посочили, така че подайте това id обратно като profile при следващите повиквания, вместо да повтаряте неуспешния. Playground в таблото за управление прави това автоматично чрез Carry.

Изрично зададен profile, browser, os или version във вашата заявка никога не се презаписва. Същото важи и за заявка, съдържаща ваш собствен хедър User-Agent или Cookie, тъй като разрешението е обвързано със сигнатурата, с която е получено.

Четене в кода

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: Пълната документация за Proxy request и response
  • Anti-Bot Defenses: Доставчици, defense и повторно използване на clearance
  • Request Outcomes: Как отхвърленият response се класифицира и таксува
  • Common Issues: Други проблеми и техните решения
  • Choosing the Right Endpoint: Кога Proxy не е правилният инструмент
Обновено: 31 август 2026 г.