أخطاء 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 }
}

تتبع الطلب

تتضمن كل استجابة API (نجاح أو خطأ) header X-FourA-Request-Id يحتوي على UUID لذلك الطلب. قم بتسجيله في نظامك. إذا كنت بحاجة إلى سؤال الدعم عما حدث لطلب معين، فإن هذا المعرف يتيح لنا العثور عليه.

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: طلب غير صالح

يفتقد جسم الطلب إلى حقول مطلوبة، أو يحتوي على قيم غير صالحة، أو يحدد هدفاً يرفض API جلبه.

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

يغطي الخطأ 400 نفسه أيضا حماية SSRF. إذا كان url الخاص بك يحل إلى نطاق IP خاص أو loopback أو محجوز بطريقة أخرى (RFC 5735، RFC 6598، الكتل المحجوزة لـ IPv6)، فسيتم رفض الطلب قبل أن يغادر شبكة FourA:

{
  "error": "Target <ip> resolves to a private/reserved IP"
}

يتم رفض JSON غير الصالح في المحتوى بنفس الطريقة، قبل قراءة أي حقل:

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

تحتوي الحقول proxy و ignoreProxies على أخطاء 400 الخاصة بها. يأخذ كلاهما proxy IDs المبهمة التي أرجعتها الاستجابات السابقة، لذلك سيفشل فك تشفير أي شيء آخر:

الرسالة ما حدث
Invalid proxy format قيمة proxy ليست proxy ID أصدرته FourA. عنوان proxy الخام ينتهي هنا.
Invalid ignoreProxies format أحد الإدخالات في ignoreProxies ليس proxy ID.
Proxy not found تم فك تشفير الـ ID بنجاح ولكنه لم يعد يحل إلى مخرج نشط. اختر واحدا جديدا.

الإصلاح: تحقق من أن الـ request الخاص بك يتضمن جميع الحقول المطلوبة، وأن عناوين URL تستخدم http:// أو https://، وأن المضيف يحل إلى عنوان عام، وأن أي قيمة proxy هي ID منسوخ حرفيا من استجابة سابقة.

401: غير مصرح به

مفتاح API الخاص بك مفقود أو غير صالح.

مفتاح مفقود:

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

مفتاح غير صالح:

{
  "error": "Invalid API key"
}

الإصلاح: تحقق من أن الـ header X-API-Key الخاص بك يحتوي على مفتاح صالح. قم بإنشاء مفتاح جديد من لوحة التحكم إذا لزم الأمر.

429: Rate Limited

لقد أرسلت الكثير من الـ requests في فترة قصيرة.

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

الإصلاح: انتظر لعدد الثواني في retryAfter قبل إرسال المزيد من الطلبات. راجع Rate Limits للتفاصيل.

500: خطأ في الخادم

حدث خطأ ما من جانبنا.

الإصلاح: أعد محاولة الطلب بعد تأخير قصير. إذا استمر الخطأ، فتحقق من صفحة الحالة أو تواصل مع الدعم الفني مقدما X-FourA-Request-Id من الاستجابة الفاشلة.

502: Upstream غير متوفر

وصل FourA إلى المحرك الخاص به ولكنه لم يتمكن من استخدام الرد.

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

الإصلاح: أعد المحاولة مع تأخير زمني قصير. المشكلة من جانبنا، لذا لن تكلفك شيئًا: النتيجة هي service_error ويتم فوترة success فقط.

504: انتهاء مهلة Upstream

لم ينهِ المحرك العمل خلال الميزانية الزمنية المخصصة لهذا الطلب.

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

يتعلق الخطأ 504 بالمدة التي استغرقها العمل، وليس بمفتاحك أو المعلمات أو الـ proxy الخاص بك. الأهداف البطيئة، وحل التحديات الباردة، والصفحات الكبيرة هي الأسباب المعتادة.

الإصلاح: قم بزيادة timeout_ms في الـ request (يقبل Single حتى 120000، و Browser حتى 120000، و Auto حتى 180000)، أو أعد المحاولة. تنتظر FourA الوقت الذي حددته بالإضافة إلى هامش صغير، لذا فإن طلب المزيد من الوقت يمنحك المزيد من الوقت فعليا.

503: الخدمة معطلة أو بكامل طاقتها

يعني الخطأ 503 أن الخدمة غير متاحة مؤقتا للصيانة أو أنك وصلت إلى حد التزامن. تتضمن كلتا الاستجابتين حقل retryAfter. يتضمن نموذج التزامن أيضا current و limits.

{
  "error": "Service disabled",
  "status": 503,
  "retryAfter": 60
}

الإصلاح: انتظر retryAfter ثانية، ثم أعد المحاولة. تدرج صفحة الحالة فترات الصيانة النشطة.

الشكل الثالث لخطأ 503 لا يحتوي على retryAfter. وهذا يعني أن المحرك الموجود خلف endpoint الخاص بك كان يعيد التشغيل عند وصول طلبك:

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

أعد المحاولة بعد ثانية أو ثانيتين.

قراءة حالات الفشل من /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. اقرأ status و error من النص بدلا من ذلك. الاستجابة الحقيقية غير 200 من /api/auto/ تعني أن FourA رفضت الاستدعاء قبل بدء التسلسل: 401، 400، 429، أو 503، وجميعها موثقة أعلاه.

إخفاقات جانب الهدف داخل 200 OK

لا يظهر كل إخفاق كحالة HTTP غير 2xx. عندما يعيد الموقع الهدف HTTP 200 مع حمولة خطأ، تسلمك FourA النص ولكنها تصنف الطلب على أنه application_error. عندما يعيد الهدف حالة غير 2xx لا تقبلها قواعد validate الخاصة بك، تكون النتيجة application_fail ويمر النص دون تغيير.

كلتا الحالتين قابلة للفوترة كما لو كان الطلب قد نجح على مستوى الشبكة. يغطي المرجع Outcomes التصنيف بالكامل.

تشفير الاستجابة

تقوم FourA بفك تشفير نصوص الاستجابة تلقائيا إلى UTF-8. إذا كان الهدف يخدم windows-1251 أو gbk أو shift_jis أو iso-8859-* أو أي مجموعة أحرف أخرى مصرح بها في ترويسة Content-Type أو علامة <meta charset> في HTML، ستتلقى سلسلة UTF-8 نظيفة في الحقل data (مفرد، proxy) أو body (متصفح).

بالنسبة للحمولات الثنائية (الصور، protobuf، الصوت الخام)، قم بتعيين returnBuffer: true في الطلب. يعود النص كمخزن 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")

ذات صلة

آخر تحديث: 12 أغسطس 2026