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"
}

proxyignoreProxies 필드에는 자체 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 필드를 포함합니다. 동시성 형식에는 currentlimits도 포함됩니다.

{
  "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에서 statuserror를 읽으십시오. /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")

관련 문서

최근 업데이트: 2026년 8월 12일