المشاكل الشائعة

حلول لأكثر المشاكل شيوعاً عند استخدام FourA API.

محتوى فارغ أو غير مكتمل

العَرَض: تُرجع الـ API حالة 200 ولكن حقل data فارغ أو يفتقر إلى المحتوى المتوقع.

السبب: تستخدم الصفحة المستهدفة JavaScript لعرض المحتوى بعد تحميل الصفحة الأولي.

الحل: التبديل من الـ single endpoint إلى الـ browser endpoint. استخدم checkText للتحقق من تحميل المحتوى:

curl -X POST https://eu.api.foura.ai/api/browser/ \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/products",
    "timeout_ms": 15000,
    "checkText": "product-list"
  }'

ملاحظة: يعيد browser endpoint المحتوى في حقل body (وليس data).

403 Forbidden أو صفحات التحقق

العَرَض: يعيد API كود HTML يحتوي على صفحة تحقق أو صفحة تم رفض الوصول.

السبب: اكتشف الموقع المستهدف أن request آلي وقام بحظره.

الحل: استخدم proxy endpoint للتدوير التلقائي لعنوان IP:

curl -X POST https://eu.api.foura.ai/api/proxy/ \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "maxTries": 5,
    "request": {
      "method": "GET",
      "url": "https://example.com/prices",
      "unblocker": true
    }
  }'

إذا استمرت المشكلة، فقم بزيادة maxTries لمنح تدوير proxy المزيد من المحاولات.

يصل خطأ 403 الذي يعيده الهدف كاستجابة HTTP 200 مع status: 403 داخل body. أما خطأ 403 على الاستدعاء نفسه، مع ترويسة X-FourA-Limit، فهو أمر مختلف: راجع 403 Not in Your Plan.

أخطاء انتهاء المهلة (Timeout Errors)

العَرَض: تفشل الطلبات مع ظهور خطأ انتهاء المهلة (timeout error).

السبب: تستغرق الصفحة المستهدفة وقتًا أطول في التحميل مقارنة بمهلة timeout المحددة.

الحل: قم بزيادة timeout_ms (القيمة الافتراضية هي 15 ثانية لـ single، و30 ثانية لـ browser، و45 ثانية لـ proxy):

curl -X POST https://eu.api.foura.ai/api/browser/ \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://slow-site.com",
    "timeout_ms": 60000
  }'

بالنسبة لطلبات المتصفح، تحقق أيضاً من أن قيمة checkText تظهر بالفعل على الصفحة. أي خطأ مطبعي يؤدي إلى فشل الاستدعاء مع الخطأ checkText:<your text> not found.

403 Not in Your Plan

العَرَض: تُرجع الـ API الرمز 403 مع ترويسة X-FourA-Limit وreason بقيمة plan_limit_feature أو plan_limit_premium.

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

السبب: خطتك لا تتضمن الـ endpoint الذي تم استدعاؤه أو الـ parameter الذي أرسلته. يغطي plan_limit_feature استدعاء endpoint مستبعد وexitCountries بدون الاستهداف الجغرافي (geo targeting)؛ ويغطي plan_limit_premium استخدام exitClass: premium بدون منافذ خروج مميزة (premium exits). لم يتم الاتصال بالهدف إطلاقا، ولم يتم استهلاك أي رصيد.

الحل: قم بإزالة الـ parameter، أو استدع endpoint تتضمنه خطتك، أو قم بترقية الخطة. توضح علامة تبويب Limits & Features في Usage & Limits ما تتضمنه خطتك. لا تقم بإعادة المحاولة دون تغيير: لم يتم تعيين Retry-After لأن الانتظار لن يغير النتيجة.

429 Too Many Requests

العَرَض: الـ API يرجع 429.

السبب: رفض أحد الفحصين الاستدعاء، وتوضح لك الاستجابة أيهما المسؤول. إذا كانت تتضمن header باسم X-FourA-Limit، فهذا يعني أنه تم الوصول إلى أحد حدود خطتك: الطلبات المتزامنة أو الطلبات في الدقيقة على هذا الـ endpoint، أو طلبات Browser لليوم، أو الرصيد أو سعة النطاق الترددي لدورة الفوترة. إذا لم يكن هناك مثل هذا الـ header، فإن الحصة المشتركة للمنصة في الدقيقة لهذه الخدمة كانت ممتلئة، وهو أمر يتعلق بحركة مرور FourA وليس بحسابك.

الحل: اقرأ X-FourA-Limit أولا. انتظر عندما يكون الحد على بعد ثوان، وتوقف عندما لا يكون كذلك. حدود الخطة التي يزيلها الانتظار تضع الثواني في الـ header باسم Retry-After وفي retry_after_seconds؛ بينما يضعها الحد المشترك في retryAfter:

import time
import requests

# Plan limits that come back in hours or days, not seconds.
STOP_ON = {"plan_limit_browser_daily", "plan_limit_credits", "plan_limit_bandwidth"}

def make_request(endpoint_url, payload, retries=3):
    for i in range(retries):
        resp = requests.post(
            endpoint_url,
            headers={"X-API-Key": "YOUR_API_KEY", "Content-Type": "application/json"},
            json=payload
        )
        if resp.status_code == 429:
            limit = resp.headers.get("X-FourA-Limit")
            if limit in STOP_ON:
                raise Exception(f"stopped by {limit}: {resp.json().get('error')}")
            header = resp.headers.get("Retry-After")
            body = resp.json()
            wait = (
                int(header) if header and header.isdigit()
                else body.get("retry_after_seconds") or body.get("retryAfter") or 2 ** i
            )
            time.sleep(wait)
            continue
        return resp
    raise Exception("Rate limit not resolved after retries")

# Example: single request
make_request(
    "https://eu.api.foura.ai/api/single/",
    {"method": "GET", "url": "https://example.com"}
)

إذا كانت قيمة الـ header هي plan_limit_concurrency أو plan_limit_rate، فإن الحل هو تحديد سقف لعدد الطلبات المفتوحة في الوقت نفسه وعدد الطلبات التي تبدؤها في الدقيقة، بدلا من تكرار المحاولات بكثافة. إعادة إرسال دفعة مرفوضة على الفور ستؤدي إلى رفض الدفعة بالكامل مرة أخرى. لا تُحسب الطلبات المرفوضة ضمن الحد المسموح به لكل دقيقة، ولكن إذا استمر وصولها بمعدل يتجاوز ضعف ذلك الحد، فستتحول حالات الرفض إلى فترة تهدئة (cooldown): يتضمن نص استجابة 429 القيمة cooldown: true ويطلب منك التوقف مؤقتا لمدة 30 ثانية (retry_after_seconds: 30). يوضح Run Requests in Parallel هذا النمط، كما يعرض قسم Usage & Limits في Dashboard عدادات الاستخدام المباشرة بجانب الحدود القصوى الخاصة بك.

503 Service Unavailable

العَرَض: تُرجع الـ API الحالة 503.

السبب: يحدث هذا في حالتين:

  1. الخدمة وصلت إلى كامل طاقتها الاستيعابية. تقوم FourA بالفعل بتشغيل أقصى عدد مسموح به من الـ requests في الوقت نفسه على ذلك المحرك، محسوبة عبر إجمالي حركة المرور وليس حركتك وحدك. تظهر Service at capacity في حقل error. وعادة ما يزول ذلك خلال ثوانٍ.
  2. الخدمة معطلة مؤقتا. هناك فترة صيانة جارية. تظهر Service disabled في حقل error.

تتضمن كلتا الحالتين حقل retryAfter في الـ response. لا تمثل أي منهما حدا من حدود خطتك: حدود خطتك الخاصة تجيب دائما باستخدام X-FourA-Limit header مع 403 أو 429، ولا تجيب أبدا بـ 503.

الحل: انتظر لمدة retryAfter ثانية، ثم أعد المحاولة:

import time
import requests

def make_request_with_retry(endpoint_url, payload, retries=3):
    for i in range(retries):
        resp = requests.post(
            endpoint_url,
            headers={"X-API-Key": "YOUR_API_KEY", "Content-Type": "application/json"},
            json=payload
        )
        if resp.status_code in (429, 503):
            header = resp.headers.get("Retry-After")
            body = resp.json()
            wait = (
                int(header) if header and header.isdigit()
                else body.get("retry_after_seconds") or body.get("retryAfter") or 2 ** i
            )
            time.sleep(wait)
            continue
        return resp
    raise Exception("Request not resolved after retries")

يعني رمز 503 عند بلوغ السعة القصوى أن FourA مشغول، لذا فإن التراجع وإعادة المحاولة هو الحل الكامل. إذا تم رفض طلبك برمز 429 مع X-FourA-Limit بدلا من ذلك، فإن المشكلة من جانبك: قلل عدد الطلبات المتوازية في مسار المعالجة لديك.

504 Upstream Timeout

العَرَض: تُرجع الـ API الرمز 504 مع {"error": "Upstream timeout"}.

السبب: لم يكتمل العمل ضمن المهلة الزمنية المحددة للطلب. قد يحدث ذلك بسبب هدف بطيء، أو حل تحدٍ غير مجهز مسبقا، أو صفحة كبيرة جدا. ليست المشكلة في المفتاح الخاص بك، أو المعلمات، أو الـ proxy.

الحل: امنح الاستدعاء مزيدا من الوقت، أو أعد المحاولة. ينتظر FourA قيمة timeout_ms الخاصة بك بالإضافة إلى هامش صغير، لذا فإن زيادتها تمدد فترة الانتظار فعليا:

{
  "url": "https://slow-site.com/report",
  "timeout_ms": 90000
}

بالنسبة إلى /api/auto/ على هدف محمي، قد يستغرق أول استدعاء بارد عشرات الثواني. يغطي timeout_ms الخاص به كامل مسار المحاولات ويقبل حتى 180000.

عندما ينفد ذلك الوقت المحدد من /api/auto/ نفسه، يستجيب الاستدعاء برمز HTTP 200. يحتوي جسم الاستجابة على error يبدأ بـ time budget exhausted، وتكون قيمة status عادة 504 (قد تترك محاولة فاشلة سابقة رمز حالتها هناك بدلا من ذلك). ارفع قيمة timeout_ms أو أعد المحاولة.

502 Upstream Unavailable

الأعراض: تعرض واجهة API الرمز 502 مع {"error": "Upstream unavailable"}، أو 503 مع {"error": "Backend service unavailable"}.

السبب: وصل FourA إلى محركه الخاص ولكنه لم يتمكن من استخدام الرد، عادة بسبب إعادة تشغيل إحدى النسخ (instance).

الحل: أعد المحاولة مع فترة انتظار تراجعي قصيرة (short backoff). يتم تصنيف الحالتين كـ service_error، ويتم احتساب التكلفة فقط لـ success، لذا لن تكلفك إعادة المحاولة أي رسوم إضافية. إذا استمر الأمر أكثر من دقيقة أو دقيقتين، فتحقق من صفحة الحالة.

401 Authentication Errors

الأعراض: يعيد كل request الرمز 401 Unauthorized.

قائمة التحقق:

  1. تحقق من أن header هو X-API-Key: YOUR_API_KEY (وليس Authorization: Bearer أو Api-Key)
  2. تحقق من عدم وجود مسافات بيضاء أو أسطر جديدة زائدة في API key الخاص بك
  3. أنشئ مفتاحا جديدا من لوحة التحكم إذا كان المفتاح الحالي معرضا للاختراق

400 Target Resolves to a Private or Reserved IP

الأعراض: تعيد واجهة API الرمز 400 مع Refusing to fetch <target>: target resolves to a private or reserved IP range قبل أن يغادر request شبكة FourA.

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

الحل: اطلب URL عاما. إذا كنت تجري اختبارا، فاستخدم هدفا عاما مثل https://example.com أو https://httpbin.org/get. إذا كان الهدف المقصود خدمة تديرها بنفسك، فقم بإتاحتها عبر اسم مضيف عام أولا.

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

لا يتم رفض اسم المضيف الذي يتعذر البحث عنه. يعود الطلب بالحالة HTTP 200 مع status: 0 والسبب (could not resolve <host>: <reason>)، مثل أي هدف يتعذر على FourA الوصول إليه، ولا تتم فوترته.

no_eligible_proxy عند استخدام exitCountries

العَرَض: يعيد استدعاء /api/proxy/ مع exitCountries استجابة HTTP 200 مع غلاف خطأ JSON:

{
  "error": "No eligible proxy found for exit countries: CZ, GB",
  "code": "no_eligible_proxy",
  "details": { "exitCountries": ["CZ", "GB"] },
  "total": 0.084
}

السبب: لا يحتوي تجمع proxy الحالي على أي منفذ خروج نشط يتطابق بلده الظاهر للهدف مع قائمتك المسموحة (allowlist). لا يلجأ FourA أبدًا إلى بلد غير مطلوب عند تعيين exitCountries.

الحل: احتفظ بالنطاق المطلوب وأعد المحاولة لاحقًا. يتم تحديث التجمع كل 10 دقائق تقريبًا، لذلك فإن البلد الذي لا يتوفر له تطابق حاليًا غالبًا ما يتوفر له منفذ في غضون ساعة.

import time, requests

def fetch_scoped(url, countries, max_attempts=6, wait_sec=600):
    for _ in range(max_attempts):
        r = requests.post("https://eu.api.foura.ai/api/proxy/",
            headers={"X-API-Key": "YOUR_API_KEY", "Content-Type": "application/json"},
            json={"maxTries": 5, "exitCountries": countries,
                  "request": {"method": "GET", "url": url}}).json()
        if r.get("code") == "no_eligible_proxy":
            time.sleep(wait_sec)
            continue
        return r
    raise RuntimeError(f"no eligible exit in {countries} after {max_attempts} attempts")

لا تقم بتوسيع قائمة البلدان إلا إذا تغيرت متطلبات البلد الخاصة بمسار عملك فعليا. يمكن أن يؤدي الرجوع التلقائي الصامت إلى بلدان أخرى إلى تعطيل المنطق المعتمد على الموقع الجغرافي لاحقا.

يظهر نص جسم الـ response مشوها

العَرَض: يحتوي data (أو body) الخاص بالـ response على أحرف غير مقروءة أو مشوهة (mojibake) عندما يستخدم الهدف ترميز أحرف غير UTF-8.

السبب: افتراضيا، يقوم FourA بفك ترميز نصوص الـ response تلقائيا إلى UTF-8 بناء على ترويسة Content-Type الخاصة بالهدف أو وسم <meta charset> في الـ HTML. إذا كان الهدف يرسل معلومات غير دقيقة حول ترميز الأحرف، فستحصل على نص مشوه.

الحل: بالنسبة للحمولات الثنائية (الصور، protobuf، ملفات الصوت الخام)، قم بتعيين returnBuffer: true في الـ request. يقوم Single و Proxy بعد ذلك بإرجاع data ككائن يحتوي على البايتات الخام، {"type": "Buffer", "data": [<byte values>]}، دون تطبيق أي تحويل لترميز الأحرف.

{
  "method": "GET",
  "url": "https://example.com/image.png",
  "returnBuffer": true
}

بالنسبة للأهداف النصية التي تصرّح عن charset الخاص بها بشكل خاطئ، فك ترميز البايتات الأولية بنفسك: قم بالجلب باستخدام returnBuffer: true، واقرأ قيم البايت في data.data، ثم فك ترميزها باستخدام charset الصحيح.

استلام HTML غير متوقع بدلاً من JSON

العَرَض: كنت تتوقع JSON من الموقع المستهدف ولكنك استلمت HTML.

السبب: قد يقدم الموقع المستهدف محتوى مختلفاً بناءً على headers.

الحل: أضف Accept header وفعّل unblocker للحصول على headers متصفح واقعية:

curl -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://api.example.com/data",
    "headers": [["Accept", "application/json"]],
    "unblocker": true
  }'

يمكنك أيضاً تعيين tryJsonData إلى true ليقوم FourA بتحليل استجابات JSON تلقائياً.

محتوى الاستجابة (Body) هو صفحة اختبار أمني وليس المحتوى المطلوب

العَرَض: نجح الطلب، وقيمة status هي 200، ولكن data (أو body) عبارة عن اختبار تحقق من الروبوتات (bot check) بدلاً من الصفحة المطلوبة.

السبب: أجرى الموقع المستهدف اختبار تحقق من الروبوتات واجهه FourA ولكنه لم يتمكن من تجاوزه. توضح الاستجابة ذلك: يُرجع Single وProxy القيمة defense مع solved: false، بينما يُرجع Browser القيمة defenseSolved: false مع اسم المزوّد في defenses.present.

الحل: تحقق من defense.vendor أولاً، ثم صعّد المحاولة. جرب ملف تعريف متصفح (browser profile) مختلفاً في Single، أو انتقل إلى Proxy للحصول على نقطة خروج مختلفة، أو استخدم Browser لتشغيل JavaScript. الدليل المرجعي الكامل للحقول وقائمة المزوّدين: Site checks.

أضف سلسلة نصية فرعية عبر validate.data.accept لا تتواجد إلا في الصفحة الحقيقية. صفحة التحقق التي يتعرف عليها FourA لا تُعتبر نجاحاً أبداً: حيث ترجع مع ترويسة X-FourA-Check-Page ولا يتم احتساب تكلفتها. بدون validate، فإن صفحة التحقق التي لا يتعرف عليها FourA، والتي ترجع برمز HTTP 200، ستُحسب كعملية ناجحة، وستكتشف ذلك في مرحلة لاحقة في نظامك بدلاً من اكتشافه فور إتمام الطلب.

ما زلت تواجه مشكلة؟

إذا لم تنجح أي من الحلول المذكورة أعلاه:

  1. تحقق من صفحة الحالة لمعرفة ما إذا كانت هناك أي حوادث جارية
  2. راجع مقاييس طلباتك في لوحة التحكم (Dashboard)
  3. تواصل مع الدعم الفني عبر support@foura.ai مع تزويدنا بتفاصيل طلبك (قم بتضمين X-FourA-Request-Id من الاستجابة الفاشلة)

الخطوات التالية

آخر تحديث: 30 سبتمبر 2026