Lỗi API

Cách xử lý lỗi từ FourA API.

Định dạng Error Response

API trả về các đối tượng JSON phẳng cho tất cả các lỗi. Không có đối tượng error lồng nhau. Khi lỗi có mã máy có thể đọc được, mã đó sẽ là một trường ở cấp cao nhất: reason khi chạm giới hạn gói, code trên lệnh gọi proxy không có exit đủ điều kiện.

{
  "error": "Invalid API key"
}

Một số lỗi bao gồm các trường bổ sung như status, service, retryAfter, current, hoặc limits ở cấp cao nhất:

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

Theo dõi một Request

Mọi API response (thành công hoặc lỗi) đều bao gồm một header X-FourA-Request-Id chứa UUID cho lệnh gọi đó, ngoại trừ body mà FourA hoàn toàn không thể đọc (JSON không đúng định dạng, hoặc body vượt quá 100 KB): những trường hợp đó bị từ chối trước khi ID được gán. Hãy ghi log ID này ở phía bạn. Nếu bạn cần liên hệ hỗ trợ để hỏi về tình trạng của một request cụ thể, ID đó sẽ giúp chúng tôi tìm ra request.

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
# ...

Các loại lỗi

400: Bad Request

Request body thiếu các trường bắt buộc, chứa giá trị không hợp lệ hoặc chỉ định mục tiêu mà API từ chối tìm nạp.

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

Mã 400 tương tự cũng áp dụng cho cơ chế bảo vệ SSRF. Nếu url của bạn phân giải về một dải IP private, loopback hoặc được bảo lưu khác (RFC 5735, RFC 6598, các block IPv6 được bảo lưu), request sẽ bị từ chối trước khi rời khỏi mạng của 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> là địa chỉ, hoặc tên máy chủ (host name) và địa chỉ được phân giải từ đó. Một URL không thể phân tích cú pháp, hoặc không phải là http:// hay https://, sẽ nhận cùng mã lỗi 400.

Tên máy chủ không thể tra cứu sẽ không bị từ chối ngay. Lời gọi sẽ trả về HTTP 200 với status: 0 kèm theo lý do (could not resolve <host>: <reason>), tương tự như bất kỳ mục tiêu nào FourA không thể kết nối tới, và yêu cầu này không bị tính phí.

JSON sai định dạng trong body sẽ bị từ chối theo cách tương tự, trước khi bất kỳ trường nào được đọc:

{
  "error": "Invalid JSON in request body"
}

Các trường proxy và ignoreProxies có mã 400 riêng. Cả hai đều nhận các proxy ID dạng opaque do các response trước đó trả về, vì vậy bất kỳ giá trị nào khác đều không thể giải mã:

Message What happened
Invalid proxy format Giá trị proxy không phải là proxy ID do FourA phát hành. Địa chỉ proxy thô sẽ rơi vào lỗi này.
Invalid ignoreProxies format Một trong các mục trong ignoreProxies không phải là proxy ID.
Proxy not found ID đã được giải mã thành công nhưng không còn liên kết với exit node đang hoạt động. Hãy chọn một ID mới.
Managed exit: this proxy id cannot be pinned to a request Exit node tồn tại, nhưng FourA sẽ không duy trì mở cho một request cụ thể. ID của exit node cao cấp sẽ rơi vào trường hợp này khi gói của bạn không còn lưu lượng cao cấp khả dụng. Hãy tái sử dụng session trả về nó, hoặc thực hiện lệnh gọi qua POST /api/proxy/ và chấp nhận exit node được tự động chọn.

Cách khắc phục: Kiểm tra để đảm bảo request chứa đầy đủ các trường bắt buộc, URL sử dụng http:// hoặc https://, host phân giải về một địa chỉ công khai và mọi giá trị proxy là ID được sao chép nguyên văn từ response trước đó.

Đây là kết quả client_error: request chưa từng rời khỏi FourA, do đó tài khoản của bạn không bị tính phí.

401: Unauthorized

API key của bạn bị thiếu hoặc không hợp lệ.

Thiếu key:

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

Khóa không hợp lệ:

{
  "error": "Invalid API key"
}

Cách khắc phục: Xác minh header X-API-Key của bạn chứa key hợp lệ. Tạo key mới từ Dashboard nếu cần.

403: Không có trong gói của bạn

Lượt gọi đã yêu cầu một endpoint hoặc một tham số mà gói của bạn không bao gồm. Phản hồi sẽ đặt X-FourA-Limit và đưa cùng một mã này vào body trong trường 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 là plan_limit_feature đối với endpoint mà gói dịch vụ không bao gồm hoặc đối với exitCountries trên gói không có định vị địa lý, và là plan_limit_premium đối với exitClass: premium trên gói không có premium exit. Chuỗi error sẽ nêu rõ tên endpoint hoặc tham số.

Mã 403 từ FourA không bao giờ liên quan đến trang web đích: trang đích chưa từng được liên hệ. Mã 403 do trang đích trả về sẽ đến dưới dạng HTTP 200 với status: 403 bên trong body.

Cách khắc phục: Xóa tham số, gọi endpoint mà gói dịch vụ của bạn hỗ trợ, hoặc nâng cấp gói. Không có Retry-After nào được thiết lập, vì việc chờ đợi không làm thay đổi kết quả. Bạn không bị trừ chi phí: kết quả là rate_limit, và chỉ tính phí đối với success.

413: Payload Too Large

JSON request body lớn hơn mức FourA chấp nhận (100 KB). Phản hồi không phải là JSON và không chứa X-FourA-Request-Id, vì body bị từ chối trước khi được đọc.

Cách khắc phục: Gửi payload data nhỏ hơn. Không bị trừ chi phí.

429: Rate Limited

Hai bước kiểm tra khác nhau sẽ phản hồi bằng mã 429, và chúng không chứa các trường giống nhau.

Giới hạn riêng của gói dịch vụ. Phản hồi sẽ thiết lập header X-FourA-Limit nêu rõ giới hạn nào đã từ chối lệnh gọi và đặt cùng mã đó trong body dưới 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 là một trong các giá trị plan_limit_concurrency, plan_limit_rate, plan_limit_browser_daily, plan_limit_credits, hoặc plan_limit_bandwidth. Khi việc chờ đợi có tác dụng, thời gian chờ sẽ nằm trong retry_after_seconds và trong header Retry-After, không bao giờ nằm trong retryAfter. plan_limit_browser_daily không chứa cả hai, vì hạn mức sẽ được làm mới vào lúc nửa đêm UTC chứ không phải theo từng giây. Không có chi phí nào phát sinh: kết quả là rate_limit, và chỉ success mới bị tính phí.

Hạn mức chia sẻ của nền tảng. Không có header X-FourA-Limit, và thời gian chờ nằm trong retryAfter:

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

current và limits mô tả trạng thái dịch vụ trên toàn bộ lưu lượng, không phải tài khoản của bạn. Việc bị từ chối ở đây có nghĩa là FourA đang bận.

Cách xử lý: Chờ theo giá trị mà Retry-After, retry_after_seconds, hoặc retryAfter trong response trả về. Khi gặp giới hạn concurrency hoặc rate limit, hãy giới hạn số lượng request mở thay vì gửi lại loạt request bị từ chối. Khi gặp giới hạn hàng ngày hoặc chu kỳ thanh toán, hãy dừng tiến trình. Xem Rate Limits để biết chi tiết từng trường và Run Requests in Parallel để xem mẫu xử lý.

500: Server Error

Đã xảy ra lỗi từ phía chúng tôi.

Cách xử lý: Thử lại request sau một khoảng thời gian ngắn. Nếu lỗi vẫn tiếp diễn, hãy kiểm tra trang trạng thái hoặc liên hệ hỗ trợ kèm theo X-FourA-Request-Id từ response bị lỗi.

502: Upstream Unavailable

FourA đã kết nối được với engine nội bộ nhưng không thể sử dụng phản hồi nhận được.

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

Cách xử lý: Thử lại với khoảng thời gian chờ ngắn (short backoff). Lỗi này xuất phát từ phía chúng tôi, vì vậy bạn sẽ không mất phí: kết quả là service_error và chỉ tính phí cho success.

504: Upstream Timeout

Engine không hoàn thành trong khoảng thời gian cho phép của request này.

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

Lỗi 504 liên quan đến thời gian xử lý tác vụ, không phải do key, tham số hay proxy của bạn. Nguyên nhân thường gặp là mục tiêu phản hồi chậm, giải quyết challenge lần đầu (cold challenge) hoặc trang có dung lượng lớn.

Cách khắc phục: Tăng timeout_ms trong request (Single nhận tối đa 120000, Browser tối đa 120000, Auto tối đa 180000), hoặc thử lại. FourA sẽ đợi theo khoảng thời gian bạn đã khai báo cộng thêm một biên độ nhỏ, do đó yêu cầu thêm thời gian thực sự giúp kéo dài thời gian chờ.

503: Dịch vụ bị vô hiệu hóa hoặc đã đạt giới hạn dung lượng

Lỗi 503 có nghĩa là dịch vụ tạm thời không khả dụng để bảo trì hoặc giới hạn concurrency của nền tảng đã đầy. Cả hai trường hợp đều chứa các key giống nhau: error, status, service, retryAfter, current và limits. Phân biệt chúng dựa vào chuỗi error, không dựa vào các trường hiện có.

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

Service disabled là trạng thái bảo trì và current trả về 0 cho cả hai bộ đếm, vì request đã bị từ chối trước khi có bất kỳ dữ liệu nào được đo lường. Service at capacity là dạng concurrency, và ở đó current hiển thị mức sử dụng thực tế của nền tảng. Xem Rate Limits để biết cấu trúc đó.

Cách khắc phục: Chờ retryAfter giây rồi thử lại. Trang trạng thái có danh sách các khoảng thời gian bảo trì đang hoạt động.

Dạng 503 thứ ba không có retryAfter. Điều này có nghĩa là engine phía sau endpoint của bạn đang khởi động lại khi lệnh gọi đến:

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

Thử lại sau một hoặc hai giây.

Đọc lỗi từ /api/auto/

POST /api/auto/ trả về HTTP 200 bất cứ khi nào ladder đã chạy, ngay cả khi mọi rung đều thất bại. Kết quả thực sự nằm trong body:

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

status là trạng thái cuối cùng mà mục tiêu phản hồi, hoặc 502 khi không có lần thử nào tiếp cận được mục tiêu (504 khi hết ngân sách thời gian trước). Một trường request mà Auto không thể chấp nhận (chẳng hạn timeout_ms dưới 5000 hoặc trên 180000) cũng được trả về theo cách tương tự: HTTP 200 với "status": 400 và lý do nằm trong error, trước khi bất kỳ lần thử nào được thực hiện và hoàn toàn không tính phí.

Vì vậy, không phân nhánh logic dựa trên trạng thái transport cho Auto. Thay vào đó, hãy đọc status và error từ body. Mã non-200 thực sự từ /api/auto/ có nghĩa là FourA đã từ chối lệnh gọi trước khi thang ladder bắt đầu, hoặc không thể hoàn tất nó: 400 (JSON sai định dạng, hoặc mục tiêu riêng tư/dành riêng), 401, 413, 502, 503 hoặc 504. Các giới hạn, của bạn hoặc của nền tảng, được trả về bên trong mã 200 với trạng thái tương ứng trong body.

Khi một trang web gặp lỗi liên tiếp trong nhiều lệnh gọi Auto, Auto sẽ phản hồi ngay lập tức trong một khoảng thời gian mà không thử lại: "error": "target temporarily unservable, retry later", "status": 503 và retryAfter tính bằng giây. Yêu cầu này không tốn phí; hãy chờ retryAfter giây.

Giới hạn gói dịch vụ mà một trong các lệnh gọi phụ chạm phải cũng được trả về dưới dạng HTTP 200. Body chính là nội dung từ chối, đi kèm với reason của nó, cùng với status và meta, đồng thời response mang header X-FourA-Limit tương tự như một lệnh từ chối trực tiếp:

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

Các giới hạn nào dừng toàn bộ thang bậc và giới hạn nào chỉ đóng một bậc được trình bày trong Smart Fetch (Auto).

Lỗi phía mục tiêu bên trong 200 OK

Không phải mọi lỗi đều hiển thị dưới dạng mã trạng thái HTTP không phải 2xx. Khi mục tiêu phản hồi HTTP 200 nhưng phản hồi của FourA mang error (ví dụ: quy tắc validate của bạn từ chối nội dung body) hoặc body là trang kiểm tra mà FourA nhận diện, kết quả sẽ là application_error. Khi mục tiêu trả về mã không phải 2xx mà các quy tắc validate của bạn không chấp nhận, kết quả sẽ là application_fail và phần body được giữ nguyên.

Cả hai trường hợp đều không tính phí: chỉ success mới bị tính phí. Browser cũng có thể phản hồi HTTP 200 với "error": "No available browser slot" khi tất cả các trình duyệt của FourA đều đang bận. Trường hợp này không tính phí; hãy thử lại sau vài giây. Tài liệu tham khảo Outcomes bao gồm toàn bộ hệ thống phân loại.

Một lệnh gọi Single thông qua proxy bạn đã ghim cũng có thể phản hồi HTTP 200 kèm "error": "The exit gave the same answer for <n> different sites" bên cạnh body. FourA phát hiện exit đó phân phát cùng một trang cho các trang web không liên quan, vì vậy trang này là của chính exit đó chứ không phải trang bạn đã yêu cầu. Đây là application_error và không bị tính phí. Hãy lấy một exit mới từ POST /api/proxy/, cơ chế này sẽ tự động bỏ qua exit như vậy.

Mã hóa phản hồi (Response Encoding)

FourA tự động giải mã body của response sang UTF-8. Nếu mục tiêu phân phát windows-1251, gbk, shift_jis, iso-8859-* hoặc bất kỳ bảng mã nào khác được khai báo trong header Content-Type hoặc thẻ HTML <meta charset>, bạn sẽ nhận được chuỗi UTF-8 sạch trong trường data (single, proxy) hoặc body (browser).

Đối với payload nhị phân (hình ảnh, protobuf, âm thanh thô), hãy đặt returnBuffer: true trên request. Single và Proxy sau đó sẽ trả về data dưới dạng một đối tượng chứa các byte thô, {"type": "Buffer", "data": [<byte values>]}, mà không áp dụng chuyển mã bảng mã (charset transcoding).

Chiến lược thử lại (Retry Strategy)

Chính sách thử lại thực tế:

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

Lỗi Proxy Đi Kèm Báo Cáo

Một lệnh gọi POST /api/proxy/ khi hết số lần thử sẽ trả về mã HTTP 200 kèm một envelope chứa lỗi, chứ không phải mã lỗi HTTP. Chuỗi lỗi ngắn và luôn có cùng cấu trúc, do đó một đối tượng attemptReport sẽ đi kèm với số lượng đếm:

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

Ghi log attemptReport.summary cùng với lỗi để biết liệu các exit proxy bị chặn, không phản hồi hay trả về các trang bị quy tắc validate của bạn từ chối. Tài liệu tham khảo về trường và cách xử lý từng số lượng: Tại sao proxy request hết số lần thử.

Liên quan

Cập nhật: 30 tháng 9, 2026