Rate Limits

모든 FourA API request는 엔진에 도달하기 전에 세 가지 검사를 거칩니다. 사용자 플랜의 한도, 호출한 endpoint에 대한 플랫폼 공유 허용량, 플랫폼 전체 트래픽에 대한 공유 허용량 순입니다. 각 검사는 개별적으로 request를 거부할 수 있으며, 각각 서로 다른 body로 응답합니다.

세 가지 검사 순서

  1. 플랜 한도. 사용자의 플랜에서 허용하는 항목: 포함된 endpoint 및 파라미터, endpoint당 동시 실행 가능한 request 수, 분당 request 수, 일일 브라우저 request 수, 청구 기간 내 사용 가능한 크레딧 및 대역폭.
  2. 글로벌 플랫폼 한도. 트래픽이 전달된 endpoint와 관계없이, 호출된 API 호스트가 해당 시점에 처리 중인 모든 작업. 여기서 거부되면 "service": "api"(으)로 보고됩니다.
  3. Endpoint별 플랫폼 한도. 호출한 단일, proxy 또는 브라우저 서비스의 트래픽.

사용자 플랜이 가장 먼저 판정되며, 이 순서는 단순한 구현 세부 사항이 아닌 엄격한 규칙입니다. 공유 허용량은 공용 리소스이므로, 플랫폼이 거부할 예정인 request가 거부되는 과정에서 이를 소모해서는 안 됩니다. 플랜 허용량을 초과하여 대량 전송하는 단일 계정은 다른 사용자가 사용하는 리소스에 영향을 미치기 전에 차단됩니다.

검사 2와 3은 사용자의 트래픽이 아닌 FourA의 전체 트래픽을 계산합니다. 둘 중 하나에서 발생한 거부는 "request를 너무 많이 보냄"이 아니라 "FourA가 사용 중임"으로 해석해야 합니다. 검사 1은 사용자 계정에만 해당하며, 플랫폼의 다른 요소에 영향을 받지 않습니다.

공유 검사 중 하나에서 거부되면 request가 백엔드에 도달하지 않았으므로, 승인 시 계산된 분당 버킷 및 브라우저 일일 슬롯 등 계정의 모든 사용량이 환원됩니다. 또한 Requests per minute에 설명된 재시도 대기 시간에도 반영되지 않습니다. 플랜이 아닌 FourA의 용량 문제로 거부되었기 때문입니다.

POST /api/auto/은(는) 자체 슬롯을 차지하지 않습니다. 대신 수행되는 Single, Proxy, Browser 하위 호출은 다른 request와 마찬가지로 세 가지 검사를 모두 거치므로, auto 호출의 병렬 배치는 하위 호출을 통해 플랜 한도에 반영됩니다. (request 수 및 성공률에는 auto 호출 자체가 1회로 계산되며, 하위 호출은 해당 호출의 시도로 표시됩니다.)

플랜 한도

플랜 한도는 호출을 거부한 한도를 명시하는 X-FourA-Limit header로 응답합니다. 동일한 코드가 body의 reason에도 포함되므로, header를 읽지 않고도 조건 분기할 수 있습니다. 모든 플랜 한도 body에는 error, reason, documentation이(가) 포함되며, 나머지 필드는 해당 한도에 따라 달라집니다.

X-FourA-Limit 상태 소진된 항목
plan_limit_feature 403 호출한 endpoint 또는 exitCountries 파라미터가 플랜에 포함되어 있지 않음
plan_limit_premium 403 exitClass: premium 플랜에 포함되어 있지 않음
plan_limit_concurrency 429 해당 endpoint의 동시 request 수
plan_limit_rate 429 해당 endpoint의 분당 request 수
plan_limit_browser_daily 429 일일 브라우저 request 수
plan_limit_credits 429 결제 주기의 과금 크레딧
plan_limit_bandwidth 429 결제 주기의 대역폭

각 제한 수치는 사용 중인 플랜에 따르며, Usage & Limits의 Limits & Features 탭에서 실시간 사용량과 함께 확인할 수 있습니다. 값을 하드코딩하지 마십시오. 거부된 모든 response에는 거부 사유가 된 한도값이 포함됩니다.

거부된 request에는 비용이 청구되지 않습니다. 결과는 rate_limit이며, success에 대해서만 요금이 부과됩니다.

플랜에 포함되지 않은 endpoint 또는 파라미터

plan_limit_feature 코드와 함께 반환되는 403 에러는 플랜에 포함되지 않은 항목을 요청했음을 의미합니다. 이 검사는 사용량이 집계되기 전에 실행되므로, 거부된 호출은 rate limit 또는 일일 카운터에 영향을 주지 않습니다.

{
  "error": "The browser endpoint is not included in your plan (Free). Upgrade to use it.",
  "reason": "plan_limit_feature",
  "documentation": "https://foura.ai/prices"
}

동일한 코드 및 상태가 위치 타겟팅이 없는 플랜에서 exitCountries 설정을 시도한 POST /api/proxy/ 호출에 반환됩니다. error 문자열에 해당 매개변수 이름이 표시됩니다:

{
  "error": "Geo targeting (the exitCountries parameter) is not included in your plan (Free). Remove the parameter or upgrade.",
  "reason": "plan_limit_feature",
  "documentation": "https://foura.ai/prices"
}

plan_limit_premium는 프리미엄 exit이 없는 요금제에서 exitClass: premium와 동일한 형태입니다. FourA는 대신 표준 풀에서 해당 요청을 처리하고 응답에 exitClass: standard를 보고할 수 있으므로 두 응답을 모두 처리해야 합니다. 둘 다 프리미엄 exit을 소비하지 않습니다. exitClass를 참고하십시오.

두 403 모두 Retry-After을 설정하지 않습니다. 기다려도 결과는 변경되지 않습니다.

Simultaneous requests

동시성은 endpoint별로 계산됩니다. 요금제에는 Single용 한도, Proxy용 한도, Browser용 한도가 각각 적용됩니다. 한도를 초과하는 요청은 Retry-After: 1와 함께 429로 반환됩니다.

{
  "error": "Concurrency limit reached: your plan allows 50 simultaneous proxy request(s). Retry when an in-flight request finishes.",
  "reason": "plan_limit_concurrency",
  "documentation": "https://foura.ai/prices",
  "limit": 50,
  "in_flight": 51,
  "retry_after_seconds": 1
}

in_flight는 거부된 request도 계산하므로 limit보다 최소 1개 더 많이 표시됩니다.

해결책은 더 공격적으로 재시도하는 대신 자체 병렬 처리를 제한하는 것입니다. 429 수신 시 동일한 배치를 즉시 재전송하면 포함된 모든 호출에서 다시 429가 발생합니다. 구현된 패턴은 병렬로 Request 실행를 참고하십시오.

분당 Request 수

Single 및 Proxy는 슬라이딩 1분을 기준으로 측정되는 분당 허용량을 가집니다. 승인된 request만 여기에 포함됩니다. 거부된 request는 제외되므로 허용량을 약간 초과하여 지속적으로 요청하는 계정의 경우 대부분 거부되는 대신 허용량만큼 처리됩니다.

{
  "error": "Rate limit reached: your plan allows 600 single requests per minute. Cool down and retry.",
  "reason": "plan_limit_rate",
  "documentation": "https://foura.ai/prices",
  "limit_per_minute": 600,
  "current_rate": 613,
  "retry_after_seconds": 17
}

retry_after_seconds는 그 사이에 다른 request를 보내지 않을 경우 추가 request가 허용될 때까지 대기해야 하는 시간입니다. 최소 1초에서 최대 120초입니다. Retry-After header에도 동일한 값이 포함됩니다.

거부된 request를 해당 시간보다 빠르게 재시도할 때 적용되는 별도의 규칙이 있습니다. sliding minute 동안 이 허용량에 의해 거부된 request 수가 허용량의 2배를 초과하면, 호출이 거부되고 대신 30초의 일시 중지가 적용됩니다.

{
  "error": "Rate limit cooldown: 1250 requests over your plan's 600 single requests per minute were refused in the last minute and kept arriving. Pause for 30 seconds.",
  "reason": "plan_limit_rate",
  "documentation": "https://foura.ai/prices",
  "limit_per_minute": 600,
  "current_rate": 540,
  "refused_last_minute": 1250,
  "cooldown": true,
  "retry_after_seconds": 30
}

일시 중지 중 거부된 요청은 계산되지 않으므로, 클라이언트가 재시도를 계속하더라도 해당 분(minute)이 지나면 일시 중지가 자동으로 종료됩니다. 일시 중지 상태를 일반 허용량과 구분하려면 error 텍스트 대신 cooldown을 확인하십시오.

일일 Browser 요청 수

Browser에는 분당 허용량이 없습니다. 플랜 제한은 UTC 자정부터 계산되는 일일 browser request 수로 적용되며, 성공한 요청뿐만 아니라 허용된 모든 browser request를 측정기에 집계합니다.

{
  "error": "Daily browser limit reached: your plan allows 300 browser requests per day (resets at midnight UTC).",
  "reason": "plan_limit_browser_daily",
  "documentation": "https://foura.ai/prices",
  "limit_per_day": 300,
  "used_today": 301
}

이 거부에는 대기 시간이 몇 초가 아니라 몇 시간 단위이므로 retry_after_seconds 및 Retry-After header가 포함되지 않습니다. 이를 중단으로 처리하고 다음 실행을 자정 UTC로 예약하세요.

청구 기간 크레딧

청구된 크레딧만 계산되며, 이는 성공한 request만 포함됨을 의미합니다. 청구된 총합이 이번 기간에 사용할 수 있는 크레딧에 도달하면 기간이 초기화되거나 추가 구매할 때까지 이후 request가 거부됩니다.

{
  "error": "Credit limit reached: 75,000 of 75,000 credits used this billing period. Upgrade your plan or wait for the reset.",
  "reason": "plan_limit_credits",
  "documentation": "https://foura.ai/prices",
  "used": 75000,
  "hard_stop": 75000,
  "retry_after_seconds": 86400,
  "resets_at": "2026-10-01T00:00:00.000Z"
}

hard_stop은(는) 이번 청구 기간에 request가 중단되는 청구 크레딧 기준값입니다. 직접 계산하지 말고 body에서 이 값을 확인하십시오. 플랜 외에 추가로 구매한 크레딧이 이미 포함되어 있습니다.

청구 기간 대역폭

대역폭 한도가 있는 플랜은 이번 기간의 표준 트래픽이 한도에 도달하면 request를 거부합니다. 프리미엄 트래픽은 별도의 허용량을 가지며 이 한도에 포함되지 않습니다. 추가 구매한 대역폭은 기본 제공 대역폭과 동일하게 취급되며, error 문자열은 플랜 기본 제공량만이 아닌 실제 사용 가능한 총량을 표시합니다.

{
  "error": "Bandwidth limit reached: 50 GB is available to you this billing period.",
  "reason": "plan_limit_bandwidth",
  "documentation": "https://foura.ai/prices",
  "used_bytes": 53687091200,
  "limit_bytes": 53687091200,
  "retry_after_seconds": 86400,
  "resets_at": "2026-10-01T00:00:00.000Z"
}

두 기간 제한 모두 retry_after_seconds의 상한은 24시간입니다. resets_at는 기간이 갱신되는 정확한 시점입니다.

플랜 제한 필드

필드 타입 표시 대상 설명
error string 전체 적용된 수치를 포함한 사람이 읽을 수 있는 메시지
reason string 전체 plan_limit_ 및 제한 이름. X-FourA-Limit 헤더와 동일한 값.
documentation string 전체 플랜 페이지 링크
retry_after_seconds number concurrency, rate, credits, bandwidth 대기 시간. Retry-After 헤더와 동일한 값.
limit number concurrency 해당 엔드포인트에서 플랜이 허용하는 동시 요청 수
in_flight number concurrency 거부된 요청을 포함하여 계정의 해당 엔드포인트에서 실행 중인 요청 수
limit_per_minute number rate 해당 엔드포인트에서 플랜이 허용하는 분당 요청 수
current_rate number rate 거부된 요청을 포함하여 최근 1분(sliding minute) 동안 집계된 요청 수
refused_last_minute number rate pause 최근 1분 동안 분당 허용 한도로 인해 거부된 요청 수. 30초 일시정지 상태에서만 표시.
cooldown boolean rate pause 너무 빠른 재시도로 인한 30초 일시정지 상태일 때 true. 일반적인 분당 거부 시에는 생략.
limit_per_day number browser daily 플랜이 허용하는 일일 브라우저 요청 수
used_today number browser daily 거부된 요청을 포함하여 오늘 집계된 브라우저 요청 수
used number credits 이번 기간에 현재까지 청구된 크레딧
hard_stop number credits 이번 기간에 요청이 중단되는 청구 크레딧 기준값
used_bytes number bandwidth 이번 기간에 현재까지 발생한 표준 트래픽(바이트 단위). 프리미엄 트래픽은 제외.
limit_bytes number bandwidth 이번 기간에 사용 가능한 바이트
resets_at string credits, bandwidth 기간 종료 시점의 ISO 8601 타임스탬프

플랜 제한은 retry_after_seconds을 사용합니다. 아래의 플랫폼 제한은 retryAfter을 사용합니다. 재시도 헬퍼는 두 항목을 모두 읽거나, 플랜 제한에서만 설정되는 Retry-After 헤더를 읽어야 합니다.

플랫폼 제한

플랫폼 검사는 서비스별 2개 항목과 전체 서비스 공통 1개 항목을 추적합니다.

  • Concurrency: FourA가 동시에 실행 중인 요청 수.
  • RPM: 지난 60초 동안 FourA가 수신한 요청 수.

두 카운터는 해당 서비스를 사용하는 모든 사용자가 공유합니다. 아래 응답의 current 및 limits은 귀하의 계정이 아닌 플랫폼 전체 상태를 나타냅니다. 본인 계정의 수치를 확인하려면 플랜 제한 응답에서 in_flight을 읽거나 대시보드의 Usage & Limits를 확인하십시오.

429: RPM 초과

{
  "error": "Rate limit exceeded",
  "status": 429,
  "service": "single",
  "retryAfter": 5,
  "current": {
    "concurrency": 12,
    "rpm": 3000
  },
  "limits": {
    "maxConcurrency": 500,
    "maxRpm": 3000
  }
}

서비스가 지난 1분 동안 허용된 request를 모두 사용했습니다. retryAfter초 동안 대기하십시오.

503: Concurrency Exceeded

{
  "error": "Service at capacity",
  "status": 503,
  "service": "proxy",
  "retryAfter": 2,
  "current": {
    "concurrency": 500,
    "rpm": 1200
  },
  "limits": {
    "maxConcurrency": 500,
    "maxRpm": 3000
  }
}

서비스가 동시에 실행할 수 있는 최대 허용 request 수를 처리하고 있습니다. 이 상태는 몇 초 내로 해제됩니다.

Service Disabled

유지보수를 위해 서비스가 일시적으로 오프라인 상태가 되면, API는 다른 오류 메시지와 함께 503을 반환합니다.

{
  "error": "Service disabled",
  "status": 503,
  "service": "single",
  "retryAfter": 60,
  "current": { "concurrency": 0, "rpm": 0 },
  "limits": { "maxConcurrency": 500, "maxRpm": 3000 }
}

이는 rate limit이 아닙니다. 서비스를 일시적으로 사용할 수 없습니다. retryAfter 값을 확인하고 해당 초만큼 기다린 후 다시 시도하십시오. 대개 몇 분 내로 해결됩니다.

두 가지 503 형태 모두 동일한 키를 전달하므로 필드 존재 여부가 아닌 error 문자열을 기준으로 분기하십시오. Service disabled는 유지 관리이고 Service at capacity는 동시성입니다.

유지 관리 형태에서는 current.concurrency 및 current.rpm가 항상 0입니다. 아무것도 측정되기 전에 request가 거부되었습니다.

플랫폼 제한 필드

필드 타입 설명
error string 사람이 읽을 수 있는 오류 메시지
status number HTTP 상태 코드 (429 또는 503)
service string 호출을 거부한 서비스: single, proxy, browser, api
retryAfter number 재시도 전 권장 대기 시간(초)
current.concurrency number 거부 당시 해당 서비스가 플랫폼 전체에서 실행 중이던 request 수
current.rpm number 최근 60초 동안 해당 서비스가 플랫폼 전체에서 수신한 request 수
limits.maxConcurrency number 서비스의 플랫폼 전체 동시성 허용치
limits.maxRpm number 서비스의 플랫폼 전체 분당 허용치

단일 헬퍼로 모든 거부 처리하기

대기할 가치가 있는 요금제 제한에는 Retry-After가 설정되어 있고, 해당 본문에는 retry_after_seconds가 있으며, 플랫폼 본문에는 retryAfter가 있습니다. 이 세 가지를 순서대로 읽고, 대기해도 해결되지 않는 요금제 제한은 중단하십시오.

import time
import requests

# Plan limits that a short wait never clears.
STOP_ON = {
    "plan_limit_feature",
    "plan_limit_premium",
    "plan_limit_browser_daily",
    "plan_limit_credits",
    "plan_limit_bandwidth",
}

def wait_seconds(resp, attempt):
    header = resp.headers.get("Retry-After")
    if header and header.isdigit():
        return int(header)
    try:
        body = resp.json()
    except ValueError:
        return 2 ** attempt
    return body.get("retry_after_seconds") or body.get("retryAfter") or 2 ** attempt

def fetch(url, api_key, max_retries=5):
    for attempt in range(max_retries):
        resp = requests.post(
            "https://eu.api.foura.ai/api/single/",
            headers={"X-API-Key": api_key, "Content-Type": "application/json"},
            json={"method": "GET", "url": url},
        )

        limit = resp.headers.get("X-FourA-Limit")
        if limit in STOP_ON:
            raise RuntimeError(f"stopped by plan limit {limit}: {resp.json().get('error')}")

        if resp.status_code in (429, 503):
            time.sleep(wait_seconds(resp, attempt))
            continue

        return resp

    raise RuntimeError("Max retries exceeded")

일일 허용량은 몇 시간 동안 복구되지 않으며, 기간 허용량은 며칠 동안 복구되지 않으므로 대기가 아닌 중단으로 처리하십시오. 다음 실행을 예약하려면 본문에서 resets_at를 확인하십시오.

팁

  • 거부된 배치를 재시도하는 대신 처리 중인 request 수를 제한하십시오. 재시도 폭풍은 하나의 429를 여러 개로 만듭니다.
  • X-FourA-Limit을 먼저 확인하십시오. 한 문자열로 제한이 사용자 것인지 플랫폼 것인지 알려주며, 플랫폼 거부는 이를 설정하지 않습니다.
  • 숫자를 하드코딩하지 마십시오. 모든 플랜 제한 response에는 거부 기준이 된 상한선이 포함되어 있으며, Usage & Limits에서 전체 목록을 확인할 수 있습니다.
  • 플랫폼 제한의 retryAfter 값은 유형별로 고정되어 있습니다. 동시성은 2초, RPM은 5초, 유지보수는 60초입니다.
  • 두 종류의 503을 구분하려면 error를 매칭하십시오. 두 형식 모두 current 및 limits를 포함하므로, 단순히 해당 필드의 유무만 확인하면 유지보수를 동시성 문제로 오인하게 됩니다.
  • X-FourA-Limit가 포함된 403은 대상 사이트가 아니라 사용자 플랜에 관한 것입니다. 대상 사이트는 응답조차 하지 않았습니다.

Proxy Port의 별도 수치

위의 모든 내용은 JSON API에 해당합니다. proxy.foura.ai을 통해 전송하는 트래픽은 다른 단위(동시 개방 터널 수, 분당 터널 개방 수, 청구 기간 표준 트래픽)의 별도 플랜 수치를 적용받습니다. CONNECT에는 본문이 없으므로 이러한 거부는 JSON 본문 대신 X-Foura-Error header가 포함된 HTTP 상태로 전달됩니다. 상태 코드는 Proxy Port를 참조하고, 포트의 기가바이트가 차감되는 풀은 How Your Plan Is Metered을 참조하십시오.

관련 항목

최근 업데이트: 2026년 9월 30일