APIエラー

FourA APIからのエラーを処理する方法。

エラーレスポンス形式

APIはすべてのエラーに対してフラットなJSONオブジェクトを返します。ネストされたerrorオブジェクトはありません。障害に機械可読コードがある場合、それはトップレベルのフィールドになります。プラン制限ではreason、利用可能なイグジットがないproxy呼び出しではcodeとなります。

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

リクエストの追跡

すべてのAPI response(成功またはエラー)には、その呼び出しのUUIDを含むX-FourA-Request-Id headerが付与されます。ただし、FourAが全く読み取れないbody(不正なJSON、または100 KBを超えるbody)はIDが割り当てられる前に拒否されるため除外されます。クライアント側でこのIDを記録してください。特定の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

リクエスト body に必須フィールドが不足しているか、無効な値が含まれているか、または API が取得を拒否するターゲットが指定されています。

{
  "error": "Invalid request body format"
}

同じ 400 は SSRF 保護も対象とします。url がプライベート、ループバック、またはその他の予約済み IP 範囲(RFC 5735、RFC 6598、IPv6 予約済みブロック)に解決される場合、request は FourA のネットワークから送信される前に拒否されます。

{
  "error": "Refusing to fetch <target>: target resolves to a private or reserved IP range. The FourA scraping API only forwards requests to public internet hosts."
}

<target> はアドレス、またはホスト名とそれが解決されたアドレスです。パースできない URL や、http:// または https:// ではない URL は、同様に 400 を返します。

名前解決できないホスト名は拒否されません。FourA が到達できない他のターゲットと同様に、status: 0 とその理由 (could not resolve <host>: <reason>) を含む HTTP 200 が返され、課金は発生しません。

ボディ内の不正な 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 は正常にデコードされましたが、有効な出口に解決されなくなりました。新しいものを選択してください。
Managed exit: this proxy id cannot be pinned to a request 出口は実在しますが、FourA が名前付きリクエスト用に維持する出口ではありません。プランのプレミアムトラフィック残量がない場合、プレミアム出口の ID でこのエラーになります。返された元のセッションを再利用するか、POST /api/proxy/ 経由で呼び出しを実行して自動選択される出口を使用してください。

解決策: リクエストに必要なすべてのフィールドが含まれていること、URL に http:// または https:// が使用されていること、ホストがパブリックアドレスに解決されること、および proxy の値が以前のレスポンスからそのままコピーされた ID であることを確認してください。

これらは client_error の結果です。リクエストは FourA から送信されていないため、消費は発生していません。

401: Unauthorized

API キーが存在しないか無効です。

キーが見つかりません:

{
  "error": "Missing API key. Include X-API-Key header."
}

無効なキー:

{
  "error": "Invalid API key"
}

解決策: X-API-Key ヘッダーに有効なキーが含まれていることを確認してください。必要に応じて ダッシュボード から新しいキーを生成してください。

403: プラン対象外

現在のプランに含まれていない endpoint またはパラメータがリクエストされました。レスポンスには X-FourA-Limit が設定され、body 内の reason に同じコードが含まれます:

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

reason は、プランに含まれない endpoint やジオターゲティング非対応プランでの exitCountries に対しては plan_limit_feature となり、プレミアム exit 非対応プランでの exitClass: premium に対しては plan_limit_premium となります。error 文字列には対象の endpoint または parameter が示されます。

FourA からの 403 はターゲットサイトに起因するものではありません。ターゲットにはアクセスされていません。ターゲットが返した 403 は、body 内に status: 403 を含む HTTP 200 として返されます。

修正方法: parameter を削除するか、プランに含まれる endpoint を呼び出すか、アップグレードしてください。待機しても結果は変わらないため、Retry-After は設定されません。消費は発生せず、結果は rate_limit となり、success のみが課金されます。

413: Payload Too Large

JSON の request body が FourA の許容サイズ (100 KB) を超えています。body は読み込まれる前に拒否されるため、response は JSON ではなく、X-FourA-Request-Id も含まれません。

修正方法: より小さい data ペイロードを送信してください。消費は発生しません。

429: Rate Limited

2 つの異なるチェックが 429 を返し、それぞれ含まれるフィールドが異なります。

ご契約プラン固有の制限: response は呼び出しを拒否した制限を示す X-FourA-Limit header を設定し、body 内の reason に同じコードを含めます:

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

reasonはplan_limit_concurrency、plan_limit_rate、plan_limit_browser_daily、plan_limit_credits、またはplan_limit_bandwidthのいずれかです。待機が有効な場合、待機時間はretry_after_secondsおよびRetry-Afterヘッダーに含まれ、retryAfterには含まれません。plan_limit_browser_dailyにはどちらも含まれません。利用枠は秒単位ではなくUTCの午前0時にリセットされるためです。消費は発生していません。結果はrate_limitであり、successのみが課金対象となります。

プラットフォームの共有利用枠。 X-FourA-Limitヘッダーはなく、待機時間はretryAfterに含まれます:

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

currentおよびlimitsは、アカウント単位ではなくトラフィック全体におけるサービス状態を示します。ここでの拒否はFourAが高負荷状態であることを意味します。

修正方法: レスポンスに含まれるRetry-After、retry_after_seconds、またはretryAfterのいずれかの時間だけ待機してください。同時実行数やrate limitに達した場合は、拒否されたバッチを再送信するのではなく、並行して開くrequest数を制限してください。1日または請求期間の上限に達した場合は、実行を停止してください。各フィールドの詳細はRate Limitsを、実装パターンはRun Requests in Parallelを参照してください。

500: Server Error

サーバー側で問題が発生しました。

修正方法: 少し待ってからrequestを再試行してください。エラーが解消しない場合は、ステータスページを確認するか、失敗したresponseのX-FourA-Request-Idを添えてサポートにお問い合わせください。

502: Upstream Unavailable

FourA内部エンジンに接続されましたが、その応答を利用できませんでした。

{
  "error": "Upstream unavailable",
  "details": "..."
}

解決方法: 短いバックオフを設定して再試行してください。これはシステム側の問題であるため、費用は発生しません。結果は service_error となり、success のみが請求対象となります。

504: Upstream Timeout

この request の制限時間内にエンジンが処理を完了できませんでした。

{
  "error": "Upstream timeout",
  "details": "the backend did not finish inside the time budget for this request"
}

504 は処理に要した時間に関するエラーであり、キー、パラメータ、proxy の問題ではありません。ターゲットの応答遅延、初回の CAPTCHA/challenge 解決、サイズの大きいページが主な原因です。

修正方法: request の timeout_ms を増やすか(Single は最大 120000、Browser は最大 120000、Auto は最大 180000 まで対応)、リトライしてください。FourA は指定された許容時間に少量のマージンを加えた時間待機するため、時間を多く設定することで確実に処理時間を確保できます。

503: Service Disabled or At Capacity

503 は、メンテナンスによりサービスが一時的に利用できないか、プラットフォームの同時実行数の上限に達していることを意味します。どちらの場合も同じキー(error、status、service、retryAfter、current、limits)が含まれます。フィールドの有無ではなく、error 文字列で判別してください。

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

Service disabledはメンテナンス中を示し、計測前にリクエストが拒否されたためcurrentは両方のカウンターで0を示します。Service at capacityは同時実行形式であり、currentにはプラットフォームの実際の使用状況が保持されます。その形式についてはRate Limitsを参照してください。

解決策: retryAfter秒待ってから再試行してください。ステータスページにアクティブなメンテナンスウィンドウが一覧表示されます。

3つ目の503形式にはretryAfterがありません。これは、呼び出しが到達した際に対象エンドポイントのエンジンが再起動中であったことを意味します:

{
  "error": "Backend service unavailable",
  "backend_status": 503
}

1〜2秒後に再試行してください。

/api/auto/ からのエラー情報の読み取り

POST /api/auto/ は、ラダーが実行された場合はすべてのステップが失敗しても HTTP 200 を返します。実際の結果は body 内に含まれます:

{
  "status": 403,
  "error": "exit blocked by the target defense",
  "attempts": 7,
  "meta": { "rung": "fail", "solved": false, "attempts": 7, "credits": 47 }
}

status はターゲットが最後に返したステータス、または試行が到達しなかった場合は 502 です (先にタイムバジェットを使い切った場合は 504)。Auto が受け入れられない request フィールド (5000 未満または 180000 超の timeout_ms など) も同様に返されます。試行が行われる前にコストなしで、"status": 400 と error に理由が含まれた HTTP 200 が返ります。

そのため、Auto ではトランスポートステータスで分岐しないでください。代わりに body から status と error を読み取ってください。/api/auto/ からの真正な非 200 は、ラダーの開始前に FourA が呼び出しを拒否したか、完了できなかったことを意味します: 400 (不正な形式の JSON、またはプライベート/予約済みターゲット)、401、413、502、503、または 504。ユーザー側またはプラットフォーム側の制限は、body 内にステータスが含まれた 200 として返されます。

サイトで複数の Auto 呼び出しが連続して失敗した場合、Auto は試行せずにしばらく即時応答します: "error": "target temporarily unservable, retry later"、"status": 503、および秒単位の retryAfter です。コストは発生しません。retryAfter 秒待機してください。

サブコールの 1 つによってプランの上限に達した場合も、HTTP 200 として返されます。body には拒否内容自体とその reason、さらに status と meta が含まれ、response には直接の拒否と同じ X-FourA-Limit header が付与されます:

{
  "status": 429,
  "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",
  "meta": { "rung": "fail", "solved": false, "attempts": 1, "credits": 0 }
}

ラダー全体を停止する制限と、特定のステップのみを終了する制限についてはSmart Fetch (Auto)で解説しています。

200 OK 内のターゲット側エラー

すべての障害が非 2xx HTTP ステータスとして現れるわけではありません。ターゲットが HTTP 200 を返しても FourA の応答に error が含まれる場合(validate ルールによってボディが拒否された場合など)、またはボディが FourA の認識するチェックページである場合、結果は application_error になります。ターゲットが validate ルールで許可されていない非 2xx を返した場合、結果は application_fail となり、ボディは変更されずに渡されます。

どちらの場合も課金対象外です。課金されるのは success のみです。FourA のすべての browser がビジー状態の場合、Browser は HTTP 200 とともに "error": "No available browser slot" を返すこともあります。これも課金されません。数秒後にリトライしてください。分類の詳細は Outcomes リファレンスを参照してください。

固定された proxy 経由の Single 呼び出しでも、ボディとともに HTTP 200 と "error": "The exit gave the same answer for <n> different sites" が返される場合があります。FourA がその exit が無関係なサイトに対しても同じページを返していることを検出した場合、そのページはリクエストしたものではなく exit 固有のものです。これは application_error となり課金されません。このような exit を自動的に回避する POST /api/proxy/ から新しい exit を取得してください。

レスポンスのエンコーディング

FourA はレスポンスボディを UTF-8 に自動デコードします。ターゲットが windows-1251、gbk、shift_jis、iso-8859-*、または Content-Type ヘッダーや HTML の <meta charset> タグで宣言されたその他の文字コードを返した場合でも、data (Single, Proxy) または body (Browser) フィールドでクリーンな UTF-8 文字列を受け取ります。

バイナリペイロード(画像、protobuf、生のオーディオなど)の場合は、リクエストで returnBuffer: true を設定してください。Single および Proxy は、文字コード変換を適用せず、生バイトを含むオブジェクト data ({"type": "Buffer", "data": [<byte values>]}) を返します。

リトライ戦略

実践的なリトライポリシー:

import time
import requests

# Plan limits that no short wait will clear: stop the run instead of retrying.
STOP_ON = {
    "plan_limit_feature",
    "plan_limit_premium",
    "plan_limit_browser_daily",
    "plan_limit_credits",
    "plan_limit_bandwidth",
}

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 {}
        request_id = resp.headers.get("X-FourA-Request-Id", "?")

        limit = resp.headers.get("X-FourA-Limit")
        if limit in STOP_ON:
            raise RuntimeError(f"{limit} (request {request_id}): {body.get('error')}")

        # Plan limits send Retry-After and retry_after_seconds; platform limits send retryAfter.
        header = resp.headers.get("Retry-After")
        retry_after = (
            int(header) if header and header.isdigit()
            else body.get("retry_after_seconds") or body.get("retryAfter") or 2 ** attempt
        )

        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/403/404 won't fix themselves
        raise RuntimeError(f"{resp.status_code} (request {request_id}): {body.get('error')}")

    raise RuntimeError(f"Exhausted {max_retries} retries")

Proxy失敗時のレポート仕様

試行回数を使い果たしたPOST /api/proxy/呼び出しは、HTTPエラーコードではなく、エラーエンベロープを含むHTTP 200として返されます。エラー文字列は短く常に同じ形式であるため、試行カウントを保持する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
}

エラーと併せて attemptReport.summary をログに記録すると、出口がブロックされたのか、停止していたのか、あるいは独自の validate ルールによって拒否されたページが返されたのかを把握できます。各カウントのフィールドリファレンスと対処方法: Why a Proxy Request Ran Out of Tries。

関連情報

最終更新日: 2026年9月30日