حدود معدل الطلبات
يمر كل طلب FourA API بثلاثة فحوصات قبل أن يصل إلى المحرك: حدود خطتك الخاصة، ثم الحصة المشتركة للمنصة الخاصة بنقطة النهاية (endpoint) التي طلبتها، ثم الحصة المشتركة للمنصة لجميع حركات المرور. يمكن لكل فحص رفض الطلب بشكل مستقل، ويستجيب كل منها بجسم استجابة (body) مختلف.
الفحوصات الثلاثة، بالترتيب
- حدود الخطة. ما تسمح به خطتك الخاصة: نقاط النهاية والمعلمات المضمنة، وعدد الطلبات التي يمكن تشغيلها في الوقت نفسه لكل endpoint، والعدد المسموح به في الدقيقة، وعدد طلبات المتصفح يوميا، والأرصدة والنطاق الترددي المتاحان في فترة الفوترة.
- حد المنصة العام. كل ما يتعامل معه مضيف API الذي استدعيته في تلك اللحظة، بغض النظر عن endpoint التي توجهت إليها حركة المرور. الرفض هنا يبلغ عن
"service": "api". - حد المنصة لكل endpoint. حركة المرور على خدمة single أو proxy أو browser التي طلبتها.
يتم تقييم خطتك أولا، وهذا الترتيب هو العقد المعتمد وليس مجرد تفصيل تنفيذي. الحصص المشتركة هي ملكية عامة، لذا فإن الطلب الذي كانت المنصة سترفضه حتما يجب ألا يستهلكها أثناء عملية رفضه. الحساب الذي يرسل أكثر بكثير مما تسمح به خطته يتم إيقافه قبل أن يمس أي مورد يعتمد عليه الآخرون.
الفحصان 2 و3 يحسبان إجمالي حركة مرور FourA، وليس حركة مرورك أنت. اعتبر الرفض من أي منهما بمثابة "FourA مشغولة"، وليس "أرسلت طلبات كثيرة جدا". الفحص 1 يخص حسابك وحده، ولا يتأثر بأي شيء آخر على المنصة.
الرفض من أي من الفحصين المشتركين يعيد لحسابك كل ما احتسبه قبوله، سواء حصة الدقيقة أو حصة المتصفح اليومية، لأن الطلب لم يصل أبدا إلى الواجهة الخلفية (backend). كما أنه لا يُحسب ضمن فترة إيقاف إعادة المحاولة الموضحة في الطلبات في الدقيقة أيضا: قدرة FourA الاستيعابية هي التي رفضته، وليست خطتك.
لا تحتجز POST /api/auto/ أي حصة خاصة بها. تمر الاستدعاءات الفرعية لخدمات Single وProxy وBrowser التي تجريها نيابة عنك عبر الفحوصات الثلاثة كأي طلب آخر، لذا فإن مجموعة متوازية من الاستدعاءات التلقائية تُحسب ضمن خطتك من خلال استدعاءاتها الفرعية. (تعتمد إحصاءات طلباتك ومعدل النجاح على الاستدعاء التلقائي نفسه مرة واحدة، وتظهر الاستدعاءات الفرعية كمحاولات تابعة له).
حدود الخطة
تستجيب حدود الخطة مع ترويسة X-FourA-Limit تحدد الحد الذي رفض الطلب. يوجد نفس الرمز في جسم الاستجابة تحت reason، حتى تتمكن من التعامل معه برمجيا دون قراءة الترويسات. يحمل كل جسم استجابة لحدود الخطة الحقول error وreason وdocumentation، بينما تعتمد بقية الحقول على نوع الحد.
X-FourA-Limit |
الحالة | ما الذي تم استنفاذه |
|---|---|---|
plan_limit_feature |
403 | الـ endpoint الذي تم استدعاؤه، أو المعامل exitCountries، غير مشمول في خطتك |
plan_limit_premium |
403 | exitClass: premium غير مشمول في خطتك |
plan_limit_concurrency |
429 | الطلبات المتزامنة على هذا الـ endpoint |
plan_limit_rate |
429 | عدد الطلبات في الدقيقة على هذا الـ endpoint |
plan_limit_browser_daily |
429 | طلبات المتصفح لليوم |
plan_limit_credits |
429 | الأرصدة المفوترة لفترة الفاتورة |
plan_limit_bandwidth |
429 | سعة النطاق الترددي لفترة الفاتورة |
الأرقام الخاصة بكل حد تابعة لخطتك، وتسردها علامة التبويب Limits & Features في Usage & Limits بجانب استهلاكك المباشر. لا تقم بتضمينها برمجيا بشكل ثابت: كل طلب مرفوض يتضمن الحد الأقصى الذي تسبب في رفضه.
الطلب المرفوض لا يستهلك أي شيء. النتيجة هي rate_limit، وتتم فوترة success فقط.
Endpoint أو معامل غير مشمول في الخطة
الحالة 403 مع plan_limit_feature تعني أن الاستدعاء طلب شيئا غير مشمول في الخطة. يتم إجراء هذا التحقق قبل احتساب أي شيء، وبالتالي فإن الاستدعاء المرفوض لا يؤثر على معدل الطلبات أو العدادات اليومية.
{
"error": "The browser endpoint is not included in your plan (Free). Upgrade to use it.",
"reason": "plan_limit_feature",
"documentation": "https://foura.ai/prices"
}
تُرجع الشيفرة والحالة السابقتان نفسيهما استجابةً لطلب POST /api/proxy/ الذي يُحدد exitCountries ضمن خطة لا تتضمن الاستهداف الجغرافي. تُحدد سلسلة 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 له نفس الهيكل بالنسبة لـ exitClass: premium في خطة لا تتضمن premium exits. قد تقوم FourA بدلا من ذلك بخدمة مثل هذا الـ request من المجموعة القياسية (standard pool) والإبلاغ عن exitClass: standard في الـ response، لذا تعامل مع كلتا الإجابتين. لا يستهلك أي منهما premium exit. راجع exitClass.
لا يقوم أي من خطأي 403 بتعيين Retry-After. لن يؤدي الانتظار إلى تغيير النتيجة.
Simultaneous requests
يتم احتساب التزامن (Concurrency) لكل endpoint: تتضمن خطتك حدا أقصى واحدا لـ Single، وواحدا لـ Proxy، وواحدا لـ Browser. يتم إرجاع الـ request الذي يتجاوز الحد كرمز 429 مع Retry-After: 1:
{
"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.
الحل هو تقييد التوازي (parallelism) لديك بدلاً من زيادة وتيرة المحاولة. الرد على 429 بإعادة إرسال نفس الحزمة فوراً يؤدي إلى إنتاج 429 أخرى لكل استدعاء فيها. راجع تشغيل Requests بالتوازي للاطلاع على نمط عمل تطبيقي.
Requests لكل دقيقة
يتضمن كل من Single و Proxy حداً مسموحاً به في الدقيقة، يُحسب على مدار دقيقة منزلقة (sliding minute). فقط الـ requests المقبولة هي التي تُحسب ضمنه: يتم استبعاد الـ 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 إضافي واحد، إذا لم تقم بإرسال أي شيء آخر في هذه الأثناء: ثانية واحدة على الأقل و120 ثانية كحد أقصى. يحمل الـ header Retry-After نفس القيمة.
إعادة محاولة الـ requests المرفوضة بسرعة أكبر من ذلك تخضع لقاعدة خاصة بها. عندما تتجاوز الـ requests التي رفضها هذا المخصص خلال الدقيقة المتحركة ضعف المخصص، يتم رفض الاستدعاء مع فترة توقف مؤقت مدتها 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
}
لا تُحتسب عمليات الرفض أثناء فترة الإيقاف المؤقت، لذا ينتهي الإيقاف تلقائيًا مع مرور الدقيقة، حتى بالنسبة للعميل الذي يواصل إعادة المحاولة. للتمييز بين فترة الإيقاف المؤقت والحصة العادية، اقرأ cooldown بدلاً من نص error.
طلبات المتصفح يوميًا
لا يملك Browser حصة لكل دقيقة. حد الخطة الخاص به هو عدد طلبات المتصفح يوميًا، محسوبة بدءًا من منتصف الليل بتوقيت UTC، ويحسب العداد كل طلب متصفح مقبول، وليس الطلبات الناجحة فقط.
{
"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، لأن فترة الانتظار بالساعات وليست بالثواني. تعامل معه كإيقاف وجدول التشغيل التالي عند منتصف الليل بتوقيت UTC.
Credits لفترة الفوترة
يتم احتساب الـ credits المفوترة فقط، ما يعني الطلبات الناجحة فقط. عندما يصل الإجمالي المفوتر إلى الـ credits المتاحة لك في هذه الفترة، سيتم رفض أي طلبات إضافية حتى تتم إعادة تعيين الفترة أو تقوم بشراء المزيد.
{
"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 هو عدد الرصيد المحتسب الذي تتوقف عنده الـ requests خلال هذه الفترة. اقرأه من الـ body بدلاً من حسابه: فهو يتضمن بالفعل أي رصيد اشتريته بالإضافة إلى الخطة.
Bandwidth لفترة الفوترة
الخطط التي تتضمن حد أقصى للـ bandwidth ترفض الـ requests بمجرد وصول حركة المرور القياسية لهذه الفترة إليه. حركة مرور Premium لها حصتها الخاصة ولا تُحتسب ضمن هذا الحد الأقصى. يُحتسب الـ bandwidth المشترى تماماً مثل الـ bandwidth المضمن، ويعرض نص 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 | التزامن، المعدل، الرصيد، سعة النطاق | مدة الانتظار. نفس قيمة الترويسة Retry-After. |
limit |
number | التزامن | عدد الطلبات المتزامنة التي تسمح بها الخطة على نقطة النهاية تلك |
in_flight |
number | التزامن | الطلبات قيد التشغيل على نقطة النهاية تلك لحسابك، بما فيها الطلب المرفوض |
limit_per_minute |
number | المعدل | عدد الطلبات في الدقيقة التي تسمح بها الخطة على نقطة النهاية تلك |
current_rate |
number | المعدل | الطلبات المحتسبة خلال الدقيقة المنزلقة، بما فيها الطلب المرفوض |
refused_last_minute |
number | إيقاف المعدل مؤقتا | الطلبات التي رفضها حد الدقيقة خلال الدقيقة المنزلقة. يظهر فقط عند الإيقاف المؤقت لمدة 30 ثانية. |
cooldown |
boolean | إيقاف المعدل مؤقتا | true عند الإيقاف المؤقت لمدة 30 ثانية بسبب إعادة المحاولة بسرعة كبيرة. غير موجود عند الرفض العادي لكل دقيقة. |
limit_per_day |
number | المتصفح اليومي | طلبات المتصفح التي تسمح بها الخطة يوميا |
used_today |
number | المتصفح اليومي | طلبات المتصفح المحتسبة اليوم، بما فيها الطلب المرفوض |
used |
number | الرصيد | الرصيد المحتسب حتى الآن خلال هذه الفترة |
hard_stop |
number | الرصيد | الرصيد المحتسب الذي تتوقف عنده الطلبات خلال هذه الفترة |
used_bytes |
number | سعة النطاق | حركة المرور القياسية حتى الآن خلال هذه الفترة، بالبايت. لا تشمل حركة المرور المتميزة. |
limit_bytes |
number | سعة النطاق | البايتات المتاحة خلال هذه الفترة |
resets_at |
string | الرصيد، سعة النطاق | طابع زمني بتنسيق ISO 8601 لنهاية الفترة |
تستخدم حدود الخطة retry_after_seconds. وتستخدم حدود المنصة أدناه retryAfter. يجب أن تقرأ أداة إعادة المحاولة المساعدة كلا الحقلين، أو تقرأ الترويسة Retry-After التي تحددها حدود الخطة فقط.
حدود المنصة
تتبع عمليات فحص المنصة أمرين لكل خدمة وأمرا إضافيا عبر جميع الخدمات:
- التزامن: عدد الطلبات التي ينفذها FourA في الوقت نفسه.
- RPM: عدد الطلبات التي استقبلها FourA في آخر 60 ثانية.
يشترك جميع مستخدمي تلك الخدمة في كلا العدادين. يصف كل من current وlimits في الاستجابات أدناه المنصة، وليس حسابك. إذا كنت تريد معرفة أرقامك الخاصة، فاقرأ in_flight من استجابة حد الخطة، أو افتح الاستخدام والحدود في لوحة التحكم.
429: تم تجاوز RPM
{
"error": "Rate limit exceeded",
"status": 429,
"service": "single",
"retryAfter": 5,
"current": {
"concurrency": 12,
"rpm": 3000
},
"limits": {
"maxConcurrency": 500,
"maxRpm": 3000
}
}
استنفدت الخدمة عدد الـ requests المسموح به خلال الدقيقة الأخيرة. انتظر retryAfter ثانية.
503: تم تجاوز حد التزامن (Concurrency Exceeded)
{
"error": "Service at capacity",
"status": 503,
"service": "proxy",
"retryAfter": 2,
"current": {
"concurrency": 500,
"rpm": 1200
},
"limits": {
"maxConcurrency": 500,
"maxRpm": 3000
}
}
يقوم service بتشغيل أكبر عدد ممكن من requests المسموح له بتشغيلها في وقت واحد. تتم معالجة هذا خلال ثوانٍ.
Service Disabled
عند إيقاف service مؤقتًا للصيانة، يرجع 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 | عدد الـ requests التي كانت الخدمة تشغلها على مستوى المنصة عند الرفض |
current.rpm |
number | عدد الـ requests التي استقبلتها الخدمة على مستوى المنصة في آخر 60 ثانية |
limits.maxConcurrency |
number | حد التزامن المسموح به للخدمة على مستوى المنصة |
limits.maxRpm |
number | الحد المسموح به للخدمة في الدقيقة على مستوى المنصة |
معالجة كل حالات الرفض باستخدام Helper واحد
يتم تعيين 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 من جسم الاستجابة إذا كنت ترغب في جدولة التشغيل التالي.
نصائح
- حدد الحد الأقصى لعدد الطلبات قيد التنفيذ بدلا من إعادة محاولة إرسال دفعة مرفوضة. عاصفة إعادات المحاولة تحول خطأ 429 واحدا إلى أخطاء كثيرة.
- اقرأ
X-FourA-Limitأولا. يخبرك في نص واحد ما إذا كان الحد خاصا بك أو بالمنصة، ولا يضبطه أي رفض صادر عن المنصة. - لا تدرج الأرقام في الكود بشكل ثابت. تحمل كل استجابة لحدود الخطة الحد الأقصى الذي تسبب في الرفض، وتعرض صفحة الاستخدام والحدود جميع هذه الحدود.
- يتم تحديد قيمة
retryAfterعند حدود المنصة حسب النوع: ثانيتان للتزامن، 5 لعدد الطلبات في الدقيقة (RPM)، 60 للصيانة. - طابق قيمة
errorللتمييز بين نوعي أخطاء 503. يحمل كلا النموذجين الحقلينcurrentوlimits، لذا فإن مجرد التحقق من "هل هذه الحقول موجودة؟" سيفسر الصيانة على أنها مشكلة تزامن. - خطأ 403 مع
X-FourA-Limitيتعلق بخطتك، وليس بالموقع المستهدف. الموقع المستهدف لم يستجب من الأساس.
يحتوي منفذ Proxy على أرقامه الخاصة
كل ما سبق يختص بواجهة برمجة التطبيقات JSON API. حركة المرور التي ترسلها عبر proxy.foura.ai تخضع لمجموعة منفصلة من أرقام الخطة، وبوحدة قياس مختلفة: الأنفاق المفتوحة في وقت واحد، ومرات فتح الأنفاق في الدقيقة، وحركة المرور القياسية لفترة الفاتورة. تأتي عمليات الرفض هذه كحالة HTTP مع ترويسة X-Foura-Error بدلا من جسم JSON، لأن طلب CONNECT لا يحتوي على جسم لوضعها فيه. راجع منفذ Proxy للاطلاع على جدول الحالات و كيفية قياس استهلاك خطتك لمعرفة الحصة التي تُخصم منها غيغابايتات المنفذ.
مواضيع ذات صلة
- تشغيل الطلبات بالتوازي: نمط عملي للتزامن المقيد
- الاستخدام والحدود: كل حدود الخطة بجانب استخدامك المباشر
- نقاط نهاية API: مرجع المعلمات الكامل
- معالجة الأخطاء: جميع أنواع الأخطاء والاستجابات
- ترويسات الاستجابة:
X-FourA-Limit، وRetry-After، وبقية الترويسات - استكشاف الأخطاء وإصلاحها: المشكلات الشائعة والحلول