كم طلبًا في الدقيقة تسمح به واجهة LinkHarbor API، وما الحجم الأقصى للطلب، وكيف تقرأ الترويسات، وكيف تتعامل مع الخطأ 429 بشكل صحيح.
لكي تبقى الـ API سريعة للجميع، يحدّ LinkHarbor عدد الطلبات في الدقيقة. ولا تعتمد الحدود على خطتك، فلا توجد حصة شهرية من استدعاءات الـ API. تسرد هذه المقالة كل الحدود وتبيّن كيف تتعامل شيفرتك معها بشكل سليم.
كيف تُحسب الطلبات
- يقع كل طلب في حاوية (bucket) واحدة بالضبط، مثل user للطلبات المصادَق عليها أو token لاستبدال الرمز
- يجري العدّ لكل دقيقة كاملة (UTC)؛ ويبدأ العدّاد من الصفر مع كل دقيقة جديدة
- حسب الحاوية، تعدّ الـ API لكل مساحة عمل أو لكل شخص أو لكل اتصال أو لكل عنوان IP
- الطلبات التي تفشل مبكرًا عند الرمز أو النطاق أو المخطط (401، و403 insufficient_scope، و400، و422 validation_failed من فحص المخطط) لا تُحسب؛ أما الأخطاء اللاحقة مثل 403 feature_not_available فتُحسب
كل الحدود
| الحاوية (X-RateLimit-Bucket) | الحد | يُحسب لكل | يسري على |
|---|---|---|---|
| user | 600 في الدقيقة | مساحة العمل لرموز مفاتيح API (كل المفاتيح وأدوات الذكاء الاصطناعي في مساحة العمل معًا)؛ والشخص لتسجيلات الدخول إلى لوحة التحكم | كل الطلبات المصادَق عليها إلى /v1 التي ليس لها حاوية خاصة بها |
| token | 30 في الدقيقة | عنوان IP | POST /v1/token |
| public | 120 في الدقيقة | عنوان IP | الطلبات إلى الـ API دون مصادقة، مثل GET /v1/plans وGET /v1/pages/available |
| callback | 120 في الدقيقة | عنوان IP | POST /v1/callbacks/sale |
| internal | 120 في الدقيقة | عنوان IP | رابط الاستقبال لتكامل Custom (API) (POST /v1/ingest/{id}) |
| mcp | 120 في الدقيقة | اتصال الذكاء الاصطناعي (رمز الوصول) | خادم MCP على /mcp |
| oauth | 300 في الدقيقة | عنوان IP | تسجيل الدخول عبر OAuth والاكتشاف لمساعدي الذكاء الاصطناعي |
| auth | 30 في الدقيقة | عنوان IP | تسجيل الدخول والتسجيل وإعادة تعيين كلمة المرور (لوحة التحكم والتطبيق) |
| refresh | 120 في الدقيقة | عنوان IP | تمديد جلسة لوحة التحكم |
| discover | 60 في الدقيقة | الشخص، أو عنوان IP عند عدم تسجيل الدخول | قسم «استكشاف» في التطبيق |
| subs_read | 120 في الدقيقة | الشخص، أو عنوان IP عند عدم تسجيل الدخول | قراءة الصفحات التي تتابعها (التطبيق) |
| subs_write | 20 في الدقيقة | الشخص، أو عنوان IP عند عدم تسجيل الدخول | تغيير الصفحات التي تتابعها (التطبيق) |
ميزانية واحدة لكل مساحة عمل
كل رموز مفاتيح API في مساحة العمل نفسها تتشارك الـ 600 طلب في الدقيقة، بغض النظر عن عدد المفاتيح التي تنشئها. وتُحسب هنا أيضًا أدوات مساعدي الذكاء الاصطناعي المتصلين، لأنها تستدعي الـ API داخليًا. لذا لا ترفع المفاتيح الإضافية الحد. وإذا توزّع حِمل كبير على عدة برامج، فخطّط على أساس الميزانية المشتركة.
الترويسات
- X-RateLimit-Limit
- حد الحاوية في الدقيقة، مثل 600.
- X-RateLimit-Remaining
- عدد الطلبات المتبقية في الدقيقة الحالية.
- X-RateLimit-Bucket
- الحاوية التي وقع فيها الطلب، مثل user.
- Retry-After
- مع 429 rate_limited فقط: عدد الثواني حتى بداية الدقيقة التالية. ومع api_key_locked يكون وقت الانتظار في details.retry_after_sec.
عند بلوغ الحد
عند بلوغ الحد، تستجيب الـ API بـ HTTP 429 وبهذا الجسم. ونص message يأتي بلغة الحساب (انظر تنسيق أخطاء الـ API ورموز الأخطاء)، وهو هنا بالإنجليزية، أما الرمز rate_limited فيبقى ثابتًا:
HTTP/1.1 429 Too Many Requests
Retry-After: 37
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 0
X-RateLimit-Bucket: user
{ "error": "rate_limited", "message": "Too many requests. Please try again in a moment.", "retry_after_sec": 37 } - اقرأ Retry-After أو retry_after_sec
- انتظر هذا العدد من الثواني على الأقل، مع قدر صغير من العشوائية حتى لا تبدأ كل العمليات من جديد في اللحظة نفسها
- أعد محاولة الطلب بضع مرات على الأكثر
- إذا استمر ظهور 429، فوزّع العمل على وقت أطول
const BASE = 'https://dock.at-harbor.com';
let cached = null; // { token, expiresAt }
async function getToken() {
if (cached && Date.now() < cached.expiresAt - 60_000) return cached.token;
const res = await fetch(BASE + '/v1/token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
key_id: process.env.LH_KEY_ID,
key_secret: process.env.LH_KEY_SECRET,
}),
});
if (!res.ok) throw new Error('Token exchange failed: HTTP ' + res.status);
const data = await res.json();
cached = { token: data.token, expiresAt: Date.now() + data.expires_in * 1000 };
return cached.token;
}
async function lh(method, path, body, attempt = 0) {
const headers = { Authorization: 'Bearer ' + (await getToken()) };
if (body !== undefined) headers['Content-Type'] = 'application/json';
const res = await fetch(BASE + path, {
method,
headers,
body: body === undefined ? undefined : JSON.stringify(body),
});
if (res.status === 429 && attempt < 3) {
const wait = Number(res.headers.get('Retry-After') ?? 5);
await new Promise((r) => setTimeout(r, (wait + Math.random() * 2) * 1000));
return lh(method, path, body, attempt + 1);
}
if (res.status === 401 && attempt === 0) {
cached = null; // token expired – try once more with a new token
return lh(method, path, body, attempt + 1);
}
const data = await res.json().catch(() => null);
if (!res.ok) throw new Error((data && data.error) + ' (HTTP ' + res.status + ')');
return data;
}
// Example: const { items } = await lh('GET', '/v1/pages'); نصائح للكميات الكبيرة من البيانات
- خزّن الرمز مؤقتًا، ولا تجدّده إلا قبل انتهاء صلاحيته بعد 15 دقيقة بقليل
- راقب X-RateLimit-Remaining وأبطئ عندما تنخفض القيمة
- أرسل الطلبات واحدًا تلو الآخر بدلًا من عشرات الاتصالات المتوازية
- حمّل القوائم مرة واحدة وصفّها محليًا بدلًا من الاستعلام عن كل صفحة على حدة
- وزّع عمليات المزامنة الكبيرة على عدة دقائق
- أعد محاولة أخطاء 5xx بفواصل متزايدة، لكن لا تعد محاولة أخطاء 4xx
حدود أخرى
| ماذا | الحد | الاستجابة |
|---|---|---|
| تسجيلات الدخول الفاشلة | قفل بعد 5 و10 و20 محاولة فاشلة لكل عنوان بريد إلكتروني وعنوان IP: لمدة 60 ثانية، و5 دقائق، و30 دقيقة | 429 account_locked |
| إرسالات النماذج | 10 في الساعة لكل عنوان IP، عبر كل النماذج | 429 form_rate_limited |
المحاولات الفاشلة عند استبدال الرمز تقفل المفتاح وعنوان IP مؤقتًا، انظر مفاتيح API ورموز الوصول. أما الإشعارات الفورية فترسلها في لوحة التحكم فقط؛ وحدودها مشروحة في المتابعون والإشعارات الفورية.
حدود المحتوى في خطتك، كالصفحات والروابط والكتل والروابط المختصرة وغيرها، ليست حدود معدل بل سقوفًا ثابتة. وهي مذكورة في مقارنة الخطط.
حجم الطلب
يُقاس كل جسم قبل أن تقرأه الـ API. والحجم المسموح يعتمد على نقطة النهاية:
| نقاط النهاية | أكبر جسم |
|---|---|
| تسجيل الدخول والتسجيل والاستدعاءات الأخرى غير المصادَق عليها تحت /v1/auth، وPOST /v1/token، وPOST /v1/oauth/exchange، ونقاط نهاية OAuth لمساعدي الذكاء الاصطناعي تحت /oauth والموافقة الخاصة بها، وPOST /v1/ingest/{id}، والدفع في كتلة «ادعم»، واستدعاءات الاشتراك في التطبيق (المتابعة، والمجموعات، وأجهزة الإشعارات الفورية)، وتبديل مساحات العمل، وPOST /v1/auth/logout-all، وحذف حساب، وإعداد المصادقة الثنائية، وقبول دعوة، وتفعيل برنامج الإحالة، وخطوة العودة عند ربط تكامل | 64 KB |
| كل نقاط نهاية JSON الأخرى، بما في ذلك إرسال نموذج على صفحة | 1 MB |
| POST /v1/webhooks/{id}/verify-signature وخادم MCP على /mcp | 2 MB |
| رفع الملفات (POST /v1/assets وPOST /v1/pages/{id}/uploads، بصيغة multipart/form-data) | 11 MB للطلب كله، والملف نفسه 10 MB كحد أقصى |
إذا كان الجسم أكبر، تستجيب الـ API بالخطأ 413 request_too_large؛ ويذكر details.max_bytes وdetails.max_kb الحد. والملف الأكبر من اللازم في عملية رفع يؤدي إلى 413 payload_too_large. ونقاط النهاية التي ليس لها جسم، مثل استدعاءات DELETE، لا تقبل جسمًا أو تقبل {} فقط.
HTTP/1.1 413 Payload Too Large
{ "error": "request_too_large", "message": "…", "details": { "max_bytes": 65536, "max_kb": 64 }, "request_id": "…" } وداخل الجسم أيضًا حدود: لكل نص طول أقصى، ولكل قائمة عدد أقصى من العناصر، والحقول غير المعروفة تُرفض. والقيم مذكورة لكل نقطة نهاية في مرجع الـ API. والمخالفة تؤدي إلى 422 validation_failed، ويذكر details الحقل والقاعدة والحد. ولا يتجاوز طول المسار وسلسلة الاستعلام (query string) معًا 16 KB.
قد يفيدك أيضًا
لم تجد ما تبحث عنه؟
أخبرنا بما تحتاج إليه، وسيساعدك فريق الدعم لدينا شخصيًا.