API 오류
FourA API에서 발생하는 오류를 처리하는 방법입니다.
오류 응답 형식
API는 모든 오류에 대해 플랫(flat)한 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 body에 필수 필드가 누락되었거나, 잘못된 값이 포함되어 있거나, 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"
}
해결 방법: request에 모든 필수 필드가 포함되어 있는지, URL에 http:// 또는 https://를 사용하는지, 호스트가 공인 IP 주소로 확인되는지 확인하십시오.
401: Unauthorized
API key가 누락되었거나 유효하지 않습니다.
Key 누락:
{
"error": "Missing API key. Include X-API-Key header."
}
유효하지 않은 Key:
{
"error": "Invalid API key"
}
해결 방법: X-API-Key header에 유효한 key가 포함되어 있는지 확인하십시오. 필요한 경우 Dashboard에서 새 key를 생성하십시오.
429: Rate Limited
짧은 시간 동안 너무 많은 request를 보냈습니다.
{
"error": "Rate limit exceeded",
"status": 429,
"service": "single",
"retryAfter": 5,
"current": { "concurrency": 12, "rpm": 3000 },
"limits": { "maxConcurrency": 500, "maxRpm": 3000 }
}
해결 방법: 추가 request를 보내기 전에 retryAfter에 지정된 초만큼 대기하십시오. 자세한 내용은 Rate Limits를 참조하십시오.
500: Server Error
서버 측에 문제가 발생했습니다.
해결 방법: 잠시 후 request를 다시 시도하십시오. 오류가 지속되면 status page를 확인하거나 실패한 response의 X-Foura-Request-Id를 첨부하여 지원 팀에 문의하십시오.
503: Service Disabled or At Capacity
503 오류는 서비스가 점검을 위해 일시적으로 중단되었거나 동시성 제한(concurrency limit)에 도달했음을 의미합니다. 두 response 모두 retryAfter 필드를 포함합니다. 동시성 제한인 경우에는 current 및 limits 필도 포함됩니다.
{
"error": "Service disabled",
"status": 503,
"retryAfter": 60
}
해결 방법: retryAfter 초 동안 대기한 후 다시 시도하십시오. 진행 중인 점검 일정은 status page에서 확인할 수 있습니다.
Target-Side Failures Inside 200 OK
모든 실패가 2xx가 아닌 HTTP status로 나타나는 것은 아닙니다. 대상 사이트가 오류 페이로드와 함께 HTTP 200을 반환하는 경우, FourA는 여전히 body를 전달하지만 해당 request를 application_error로 분류합니다. 대상 사이트가 귀사의 validate 규칙에서 허용하지 않는 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> 태그에 선언된 기타 문자 집합을 제공하는 경우, data(single, proxy) 또는 body(browser) 필드에서 깨끗한 UTF-8 문자열을 받게 됩니다.
바이너리 페이로드(이미지, protobuf, 원시 오디오)의 경우 request에 returnBuffer: true를 설정하십시오. body는 문자 집합 변환이 적용되지 않은 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:
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가지 결과(outcome) 값에 대한 설명
- Common Issues: 증상, 원인, 해결 방법