كل استجابة تعود من CaptchaAI API تقع في واحدة من أربع حالات فقط: مهمة لا تزال قيد المعالجة، أو نجاح يبدأ بالبادئة OK|، أو رمز خطأ نصّي، أو رقم يمثل رصيدك. ما إن تتعرّف على بادئة السطر حتى تكون قد حلّلت الاستجابة عمليًا. تعتمد الخدمة على ردود نصية قصيرة بدل JSON الثقيل، وهو ما يجعل الفحص عبر curl أو من سكربت صغير سريعًا، لكنه يفرض عليك التمييز الدقيق بين الانتظار والنجاح والفشل حتى لا تهدر الرصيد أو تُسيء تفسير النتيجة. يشرح هذا الدليل كل أشكال الاستجابة على نقطتي in.php وres.php، مع قوالب تحليل جاهزة للنسخ.
البنية العامة: أربع حالات لكل استجابة
ثبّت هذه الخريطة الذهنية أولًا. أي جسم استجابة يصلك ينتمي إلى إحدى الحالات التالية، وشجرة قرار واحدة تكفي للتعامل معها جميعًا:
- قيد المعالجة: الجسم يساوي
CAPCHA_NOT_READYبالضبط — انتظر ثم استطلع مرة أخرى. - نجاح: الجسم يبدأ بـ
OK|، وما بعد الفاصل هو الحمولة الفعلية (معرّف مهمة، أو توكن، أو نص مُتعرَّف عليه، أو حقول منظمة). - خطأ: الجسم رمز يبدأ عادةً بـ
ERROR_— راجع جدول الأخطاء واتخذ الإجراء المناسب. - قيمة مباشرة: كما في طلب الرصيد، حيث تعود الاستجابة رقمًا عشريًا بلا بادئة.
القاعدة الذهبية: افحص البادئة أولًا، ثم فسّر ما بعدها. النوع الوحيد الذي يتغيّر بين reCAPTCHA والصور وGeeTest وCloudflare هو محتوى الحمولة بعد OK|، أما منطق التفريع فيبقى ثابتًا.
الوضع النصي مقابل json=1
تقبل نقاط النهاية معاملًا اختياريًا اسمه json=1 يحوّل الرد من نص مفصول بالفاصل | إلى كائن JSON منظّم. الوضعان يحملان البيانات نفسها؛ اختر ما يناسب لغتك وأدواتك:
| الوضع | متى تستخدمه | مثال نجاح | مثال خطأ |
|---|---|---|---|
| النصي (افتراضي) | سكربتات الصدفة، أصغر حمولة، ملائم للتعابير النمطية | OK\|73548291 |
ERROR_ZERO_BALANCE |
json=1 |
المحلّلات المنمّطة، التسجيل المنظّم، المكتبات | {"status": 1, "request": "73548291"} |
{"status": 0, "request": "ERROR_ZERO_BALANCE"} |
في وضع JSON تكون status هي 1 عند النجاح و0 عند أي فشل (بما في ذلك CAPCHA_NOT_READY)، بينما يحمل الحقل request المحتوى المفيد: معرّف المهمة أو التوكن أو نص الصورة أو رمز الخطأ.
استجابة نقطة الإرسال in.php
عند إرسال مهمة جديدة تحصل على أحد ردّين فقط: تأكيد بالاستلام يحمل معرّف المهمة، أو رمز خطأ يوقف المسار من البداية.
النجاح: استلام المعرّف
OK|TASK_ID
مثال: OK|73548291
الجزء الذي يلي OK| هو معرّف المهمة الذي ستستخدمه لاحقًا في الاستطلاع عن النتيجة.
الخطأ: رفض الطلب
ERROR_CODE
مثال: ERROR_WRONG_USER_KEY
تحليل استجابة الإرسال
افحص البادئة OK| أولًا؛ فإن غابت فأنت أمام خطأ يجب رفعه فورًا بدل المتابعة إلى مرحلة الاستطلاع:
resp = requests.get("https://ocr.captchaai.com/in.php", params={...})
if resp.text.startswith("OK|"):
task_id = resp.text.split("|")[1]
else:
error = resp.text
raise Exception(f"Submit failed: {error}")
const resp = await axios.get("https://ocr.captchaai.com/in.php", { params });
if (resp.data.startsWith("OK|")) {
const taskId = resp.data.split("|")[1];
} else {
throw new Error(`Submit failed: ${resp.data}`);
}
استجابة نقطة الاستطلاع res.php
بعد الحصول على معرّف المهمة، تستطلع النتيجة على فترات. هنا تظهر أغنى مجموعة من الأشكال، لأن كل عائلة تحديات تعيد حمولتها الخاصة بعد OK|.
حالة الانتظار
CAPCHA_NOT_READY
المهمة لا تزال قيد المعالجة. انتظر 5 ثوانٍ ثم استطلع مجددًا. لاحظ الإملاء المقصود CAPCHA (بلا حرف T) — قارن السلسلة حرفيًا كما هي، لا كما تتوقعها.
حالات النجاح حسب نوع التحدي
يتغيّر ما بعد OK| بحسب عائلة التحدي، بينما يبقى منطق التفريع ثابتًا.
تحديات قائمة على التوكن
بالنسبة إلى reCAPTCHA v2/v3 وCloudflare Turnstile وما شابهها من التحديات القائمة على توكن، تعود الحمولة سلسلة واحدة طويلة:
OK|03AGdBq24PBCbw...long_token_string
هذا التوكن هو ما تحقنه في الحقل المتوقَّع داخل نموذج الصفحة الهدف.
تحديات الصور وOCR
OK|abc123
النص بعد OK| هو النص الذي تم التعرّف عليه من الصورة، وتُرسله كإجابة نصية مباشرة.
GeeTest
OK|challenge:abc123,validate:def456,seccode:ghi789
هنا لا تكون الحمولة توكنًا واحدًا بل ثلاثة حقول مفصولة بفاصلة، وتحتاج إلى تحليل كل حقل على حدة:
if result.text.startswith("OK|"):
data = result.text.split("|")[1]
parts = dict(item.split(":") for item in data.split(","))
challenge = parts["challenge"]
validate = parts["validate"]
seccode = parts["seccode"]
Cloudflare Challenge
تعود قيمة ملف تعريف الارتباط cf_clearance مع وكيل المستخدم، وتضبطهما معًا في عميل HTTP لديك:
OK|cf_clearance=abc123;user_agent=Mozilla/5.0...
الخطأ
ERROR_CODE
قالب موحّد لتحليل النتيجة
بدل تكرار منطق التفريع في كل موضع، اجمعه في دالة واحدة تعيد حالة صريحة — انتظار أو حل أو خطأ:
def parse_result(response_text):
if response_text == "CAPCHA_NOT_READY":
return {"status": "pending"}
if response_text.startswith("OK|"):
return {"status": "solved", "result": response_text.split("|", 1)[1]}
return {"status": "error", "error": response_text}
استجابة طلب الرصيد
GET https://ocr.captchaai.com/res.php?key=API_KEY&action=getbalance
الرد:
1.234
رقم عشري يمثل رصيدك بالدولار الأمريكي (USD)، بلا بادئة OK|. تعامل معه كقيمة مباشرة وحوّله إلى عدد قبل استخدامه:
balance = float(requests.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY, "action": "getbalance"
}).text)
print(f"Balance: ${balance:.2f}")
نقاط الإبلاغ عن النتائج
طرق الإبلاغ عن الحل
تقرير جيد (حل صحيح)
GET https://ocr.captchaai.com/res.php?key=API_KEY&action=reportgood&id=TASK_ID
الرد: OK_REPORT_RECORDED
الإبلاغ عن خطأ (حل غير صحيح)
GET https://ocr.captchaai.com/res.php?key=API_KEY&action=reportbad&id=TASK_ID
الرد: OK_REPORT_RECORDED
يساعد الإبلاغ عن النتائج غير الصحيحة على تحسين الجودة التشغيلية، وقد يدخل في آلية التعويض بحسب حالة المهمة ونوع التحدي.
رموز الأخطاء الشائعة
| رمز الخطأ | المعنى | الإجراء |
|---|---|---|
ERROR_WRONG_USER_KEY |
مفتاح API غير صالح | تحقق من مفتاحك |
ERROR_KEY_DOES_NOT_EXIST |
المفتاح غير مسجل | تحقق من لوحة التحكم |
ERROR_ZERO_BALANCE |
الرصيد غير كافٍ | أضف رصيدًا إلى الحساب |
ERROR_NO_SLOT_AVAILABLE |
لا توجد سعة متاحة حاليًا | أعد المحاولة بعد 5 ثوانٍ |
ERROR_CAPTCHA_UNSOLVABLE |
التحدي تعذر حله | أرسل مهمة جديدة إذا كان ذلك مناسبًا |
ERROR_BAD_DUPLICATES |
تم رفض المهمة المكررة | انتظر قليلًا قبل إعادة الإرسال |
ERROR_WRONG_CAPTCHA_ID |
معرف المهمة غير صالح | تحقق من قيمة معرف المهمة |
ERROR_EMPTY_ACTION |
معلمة action مفقودة |
أضف action=get |
IP_BANNED |
كثرة الطلبات الخاطئة من عنوان IP | أوقف الطلبات مؤقتًا، راجع الإعدادات، ثم أعد المحاولة لاحقًا |
صنّف الخطأ قبل أن تتصرف: أخطاء المصادقة والحساب مثل ERROR_WRONG_USER_KEY وERROR_ZERO_BALANCE وIP_BANNED تعني «توقف وأصلح الإعداد أو اشحن الرصيد» ولا تُعاد المحاولة عليها؛ والأخطاء المؤقتة مثل ERROR_NO_SLOT_AVAILABLE تُعالَج بتراجع أُسّي ثم إعادة المحاولة؛ والأخطاء الخاصة بالمهمة مثل ERROR_CAPTCHA_UNSOLVABLE تُحل بإرسال مهمة جديدة.
مثال كامل للاستطلاع الدوري
يجمع المثال التالي كل ما سبق: إرسال المهمة، ثم الاستطلاع كل 5 ثوانٍ حتى النجاح أو انتهاء المهلة:
import requests
import time
API_KEY = "YOUR_API_KEY"
def solve_captcha(submit_params, timeout=300):
"""Generic solver with proper response handling."""
submit_params["key"] = API_KEY
# Submit
resp = requests.get("https://ocr.captchaai.com/in.php", params=submit_params)
if not resp.text.startswith("OK|"):
raise Exception(f"Submit error: {resp.text}")
task_id = resp.text.split("|")[1]
# Poll
deadline = time.time() + timeout
while time.time() < deadline:
time.sleep(5)
result = requests.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY,
"action": "get",
"id": task_id
})
parsed = parse_result(result.text)
if parsed["status"] == "pending":
continue
elif parsed["status"] == "solved":
return parsed["result"]
else:
raise Exception(f"Solve error: {parsed['error']}")
raise TimeoutError(f"Task {task_id} timed out after {timeout}s")
سيناريو عملي: مراقبة دورية لبوابة حجوزات
تخيّل أنك تبني فحصًا آليًا لبوابة حجوزات إقليمية تعمل خلف Cloudflare Turnstile، للتأكد كل ساعة من أن مسار الدفع يعمل. الفخ الشائع هو معاملة CAPCHA_NOT_READY كأنها فشل، فيتوقف الفحص قبل أن تجهز النتيجة ويطلق إنذارًا كاذبًا. عندما تفصل حالة الانتظار عن رمز الخطأ — كما في parse_result أعلاه — يواصل السكربت الاستطلاع حتى يصل التوكن، ولا يوقظ فريقك إلا عند خطأ حقيقي مثل ERROR_ZERO_BALANCE.
الأسئلة الشائعة
ماذا يعني CAPCHA_NOT_READY وكم أنتظر؟
تعني أن المهمة ما زالت قيد المعالجة ولم تكتمل بعد. لا تعامله كخطأ ولا توقف المسار؛ انتظر نحو 5 ثوانٍ ثم أعد الاستطلاع. حدّد مهلة قصوى معقولة (مثل 300 ثانية) حتى لا يعلق السكربت إلى الأبد إن تعذّر الحل نهائيًا.
كيف أميّز الخطأ الذي يستوجب التوقف من الخطأ المؤقت؟
اقرأ رمز الخطأ وصنّفه: أخطاء المصادقة والرصيد مثل ERROR_WRONG_USER_KEY وERROR_ZERO_BALANCE تستوجب التوقف وإصلاح الإعداد، بينما الأخطاء المؤقتة مثل ERROR_NO_SLOT_AVAILABLE تُحل بتراجع أُسّي ثم إعادة المحاولة.
هل أستخدم الوضع النصي أم json=1؟
إن كنت تعمل من سكربت صدفة أو تفحص يدويًا عبر curl، فالوضع النصي أخفّ وأسرع في القراءة. أما مع عميل منمّط يفضّل الكائنات المنظمة والتسجيل المهيكل، فأضف json=1 وتعامل مع الحقلين status وrequest مباشرة. البيانات نفسها في الحالتين.
كيف أتجنّب قطع التوكن أثناء التحليل؟
استخدم دائمًا split("|", 1) بأقصى تقسيم واحد. توكنات reCAPTCHA قد تصل إلى نحو 500 حرف وقد تحتوي على رموز متنوعة، فالتقسيم غير المحدود قد يمزّق التوكن نفسه إذا احتوى على محارف غير متوقعة. حدّد التقسيم عند أول فاصل فقط.
كيف أفصل أخطاء الشبكة عن أخطاء الخدمة؟
أخطاء الشبكة (مثل ConnectionError أو Timeout) تحدث قبل أن تصل الخدمة، فلفّ نداءات واجهة البرمجة داخل try/except وأعد المحاولة عليها بمنطق منفصل. أما أخطاء الخدمة فتصلك كنص داخل جسم استجابة صحيح (رمز ERROR_* أو CAPCHA_NOT_READY)، وفصل المسارين يجعل تشخيص الأعطال أوضح.