API 오류
FourA API의 에러 처리 방법.
에러 응답 형식
API는 모든 에러에 대해 평면적인 JSON 객체를 반환합니다. 중첩된 error 객체나 에러 코드는 없습니다.
{
"error": "Invalid API key"
}
일부 오류에는 최상위 수준에 status, service, retryAfter, current 또는 limits와 같은 추가 필드가 포함됩니다:
{
"error": "Rate limit exceeded",
"status": 429,
"service": "single",
"retryAfter": 5,
"current": { "concurrency": 12, "rpm": 3000 },
"limits": { "maxConcurrency": 500, "maxRpm": 3000 }
}
request 추적
모든 API response(성공 또는 오류)에는 해당 호출에 대한 UUID가 포함된 X-FourA-Request-Id header가 포함되어 있습니다. 시스템에 이를 기록하십시오. 특정 request에 대해 고객 지원에 문의해야 하는 경우, 해당 ID를 사용하여 확인할 수 있습니다.
curl -i -X POST https://eu.api.foura.ai/api/single/ \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"method": "GET", "url": "https://example.com"}'
# HTTP/1.1 200 OK
# X-FourA-Request-Id: 9f1c4e6c-7b2a-4d3e-8a1f-2c9d8e4a3b15
# Content-Type: application/json
# ...
에러 유형
400: Bad Request
request 본문에 필수 필드가 누락되었거나, 유효하지 않은 값이 포함되었거나, API가 가져오기를 거부하는 대상을 지정했습니다.
{
"error": "Invalid request body format"
}
동일한 400은 SSRF 보호에도 적용됩니다. url이(가) 프라이빗, 루프백 또는 기타 예약된 IP 대역(RFC 5735, RFC 6598, IPv6 예약 블록)으로 확인되면, 해당 request는 FourA 네트워크를 벗어나기 전에 거부됩니다:
{
"error": "Target <ip> resolves to a private/reserved IP"
}
본문의 잘못된 형식의 JSON은 어떠한 필드도 읽기 전에 동일한 방식으로 거부됩니다:
{
"error": "Invalid JSON in request body"
}
proxy 및 ignoreProxies 필드에는 자체 400 에러가 있습니다. 두 필드 모두 이전 응답에서 반환된 불투명한 proxy ID를 사용하므로 다른 값은 디코딩에 실패합니다:
| 메시지 | 발생 원인 |
|---|---|
Invalid proxy format |
proxy 값이 FourA에서 발급한 proxy ID가 아닙니다. 원시 proxy 주소가 여기에 해당합니다. |
Invalid ignoreProxies format |
ignoreProxies의 항목 중 하나가 proxy ID가 아닙니다. |
Proxy not found |
ID가 정상적으로 디코딩되었지만 더 이상 활성 exit으로 확인되지 않습니다. 새 ID를 선택하세요. |
수정: request에 모든 필수 필드가 포함되어 있는지, URL이 http:// 또는 https://를 사용하는지, 호스트가 공용 주소로 확인되는지, proxy 값이 이전 응답에서 그대로 복사된 ID인지 확인하세요.
401: Unauthorized
API 키가 누락되었거나 유효하지 않습니다.
누락된 키:
{
"error": "Missing API key. Include X-API-Key header."
}
잘못된 키:
{
"error": "Invalid API key"
}
해결 방법: X-API-Key header에 유효한 키가 포함되어 있는지 확인합니다. 필요한 경우 대시보드에서 새 키를 생성합니다.
429: Rate Limited
짧은 시간에 너무 많은 request를 보냈습니다.
{
"error": "Rate limit exceeded",
"status": 429,
"service": "single",
"retryAfter": 5,
"current": { "concurrency": 12, "rpm": 3000 },
"limits": { "maxConcurrency": 500, "maxRpm": 3000 }
}
해결 방법: 추가 요청을 보내기 전에 retryAfter에 지정된 초 만큼 대기하십시오. 자세한 내용은 Rate Limits를 참조하십시오.
500: 서버 오류
서버 측에 문제가 발생했습니다.
해결 방법: 잠시 후 요청을 다시 시도하십시오. 오류가 지속되면 상태 페이지를 확인하거나 실패한 response의 X-FourA-Request-Id을 첨부하여 지원팀에 문의하십시오.
502: Upstream 사용 불가
FourA가 자체 엔진에 도달했지만 응답을 사용할 수 없습니다.
{
"error": "Upstream unavailable",
"details": "..."
}
해결 방법: 짧은 대기 시간 후 재시도하십시오. 이는 당사 측 문제이므로 비용이 발생하지 않습니다. 결과는 service_error이며 success만 청구됩니다.
504: 업스트림 시간 초과
엔진이 이 request에 할당된 시간 내에 작업을 완료하지 못했습니다.
{
"error": "Upstream timeout",
"details": "the backend did not finish inside the time budget for this request"
}
504는 작업에 걸린 시간에 관한 것이며, 키, 매개변수 또는 proxy와는 무관합니다. 느린 대상, 콜드 challenge 해결 및 큰 페이지가 일반적인 원인입니다.
해결 방법: request에서 timeout_ms 값을 늘리거나(Single은 최대 120000, Browser는 최대 120000, Auto는 최대 180000 허용) 재시도하십시오. FourA는 선언한 예산에 약간의 여유 시간을 더해 대기하므로, 더 많은 시간을 요청하면 실제로 더 많은 시간을 확보할 수 있습니다.
503: 서비스 비활성화 또는 용량 초과
503은 유지보수를 위해 서비스를 일시적으로 사용할 수 없거나 동시성 제한에 도달했음을 의미합니다. 두 response 모두 retryAfter 필드를 포함합니다. 동시성 형식에는 current 및 limits도 포함됩니다.
{
"error": "Service disabled",
"status": 503,
"retryAfter": 60
}
해결 방법: retryAfter초 대기 후 다시 시도하십시오. 상태 페이지에 활성 유지보수 시간이 나열되어 있습니다.
세 번째 503 형태에는 retryAfter이(가) 없습니다. 이는 호출이 도착했을 때 endpoint 뒤의 엔진이 다시 시작 중이었음을 의미합니다:
{
"error": "Backend service unavailable",
"backend_status": 503
}
1~2초 후 다시 시도하십시오.
/api/auto/에서 실패 읽기
POST /api/auto/는 모든 단계가 실패하더라도 래더가 실행될 때마다 HTTP 200으로 응답합니다. 실제 결과는 body에 있습니다:
{
"status": 0,
"error": "all attempts failed",
"attempts": 7,
"meta": { "rung": "fail", "solved": false, "attempts": 7, "credits": 47 }
}
따라서 Auto의 전송 상태로 분기하지 마십시오. 대신 body에서 status 및 error를 읽으십시오. /api/auto/에서 발생한 실제 non-200은 ladder가 시작되기 전에 FourA가 호출을 거부했음을 의미합니다(위에 문서화된 401, 400, 429 또는 503).
200 OK 내부의 타겟 측 실패
모든 실패가 non-2xx HTTP 상태로 나타나는 것은 아닙니다. 타겟 사이트가 오류 페이로드와 함께 HTTP 200을 반환할 때, FourA는 여전히 body를 전달하지만 request를 application_error으로 분류합니다. 타겟이 사용자의 validate 규칙에서 허용하지 않는 non-2xx를 반환할 경우, 결과는 application_fail이 되며 body는 변경 없이 전달됩니다.
두 경우 모두 request가 네트워크 수준에서 성공한 것처럼 청구됩니다. 전체 분류 체계는 Outcomes 레퍼런스를 참조하십시오.
Response 인코딩
FourA는 response body를 UTF-8로 자동 디코딩합니다. 타겟이 windows-1251, gbk, shift_jis, iso-8859-* 또는 Content-Type header나 HTML <meta charset> 태그에 선언된 기타 charset을 제공하는 경우, data (single, proxy) 또는 body (browser) 필드에서 깨끗한 UTF-8 문자열을 수신하게 됩니다.
바이너리 페이로드(이미지, protobuf, 원시 오디오)의 경우, request에 returnBuffer: true을 설정하십시오. body는 charset 트랜스코딩이 적용되지 않은 base64 버퍼로 반환됩니다.
재시도 전략
실용적인 재시도 정책:
import time
import requests
def make_request(url, payload, api_key, max_retries=3):
for attempt in range(max_retries):
resp = requests.post(
url,
headers={"X-API-Key": api_key, "Content-Type": "application/json"},
json=payload,
)
if resp.status_code == 200:
return resp.json()
body = resp.json() if resp.headers.get("content-type", "").startswith("application/json") else {}
retry_after = body.get("retryAfter", 2 ** attempt)
request_id = resp.headers.get("X-FourA-Request-Id", "?")
if resp.status_code in (429, 503):
time.sleep(retry_after)
continue
if resp.status_code >= 500: # 500, 502, 503, 504 are all ours to fix
time.sleep(2 ** attempt)
continue
# 400/401/404 won't fix themselves
raise RuntimeError(f"{resp.status_code} (request {request_id}): {body.get('error')}")
raise RuntimeError(f"Exhausted {max_retries} retries")
관련 문서
- Rate Limits: 동시성 및 RPM 세부 정보
- Request Outcomes: 7가지 결과 값 설명
- Common Issues: 증상, 원인, 해결 방법
- Anti-Bot Defenses: body가 오류가 아닌 챌린지 페이지인 경우