نتایج گوگل

پرس‌وجوها

اجرای یک‌بارهٔ یک خزش در سرویس نتایج گوگل، مدل وضعیت پنج‌حالته، معنای deduped و اینکه چطور برای نتیجه صبر کنید.

یک پرس‌وجو یک اجرای خزش است. برخلاف هدف، هیچ زمان‌بندی‌ای نمی‌سازد و هیچ اثر جانبی ندارد.

اگر همین حالا یک بررسی می‌خواهید، POST /queries همان است.

اجرای یک پرس‌وجو

POST /queries → 201 — نیازمند queries:write.

یک خزش را همین حالا راه می‌اندازد. نه هدفی می‌سازد، نه زمان‌بندی پنهانی، و هیچ اثر جانبی ندارد. اگر همین حالا یک بررسی می‌خواهید، همین اندپوینت است.

نمونهٔ درخواست:

curl -s -X POST -H "X-API-Key: $KEY" -H 'Content-Type: application/json' \
  -d '{"domain":"example.ir","keyword":"shoes","city":"tehran"}' \
  https://serp.rasaad.ir/api/v1/operator/queries

بدنه فقط دو فیلد لازم دارد: domain و keyword. بقیه اختیاری‌اند — city پیش‌فرض "" است (یعنی کل ایران)، pages پیش‌فرض ۱ و expand_answers پیش‌فرض ۰. سقف هر دو را GET /capabilities می‌گوید.

نمونهٔ پاسخ:

{"ok": true, "task_id": "5fa4d2c1", "status": "queued",
 "created": true, "deduped": false, "reason": "created"}

task_id شناسهٔ همین اجراست و کلید خواندن نتیجه در ادامه. status در لحظهٔ ثبت queued است، چون کار ناهمگام است؛ وضعیت نهایی را باید بعداً بخوانید.

فیلدیعنی چه
createdیک اجرای تازه ساخته شد
dedupedدرخواست تکراری بود و اجرای تازه‌ای ساخته نشد
reasonچرا تکراری بود یا نه — جدول پایین

معنای reason:

مقداریعنی چه
createdوظیفهٔ تازه در صف نشست و بعداً اجرا می‌شود
already_activeبرای این پرس‌وجو از قبل یک وظیفه در صف یا در حال اجرا هست؛ task_id همان است
recently_doneیک اجرا در dedup_after_hours (پیش‌فرض ۲۴ ساعت) اخیراً تمام شده
تکراری بودن درخواست خطا نیست و همچنان 201 برمی‌گردد. یعنی کاری که می‌خواستید از قبل در حال انجام است، پس چیزی برای گزارش خطا وجود ندارد. اگر کلاینت شما در حلقهٔ retry است، reason تعیین می‌کند چه کند: already_active یعنی صبر کنید، recently_done یعنی نتیجهٔ ذخیره‌شده را بخوانید، و created یعنی تازه شروع شده.
این مهم‌ترین تفاوت رفتاری با مسیر قدیمی است. در مسیر قدیمی POST /api/query فرستادن cadence_hours بزرگ‌تر از صفر بی‌سروصدا یک هدف دائمی می‌ساخت. اینجا چنین چیزی وجود ندارد؛ cadence_hours را اصلاً نفرستید. جزئیات در مسیرهای قدیمی.

مدل وضعیت

پرس‌وجو از پنج وضعیت می‌گذرد:

وضعیتیعنی چه
queuedدر صف است و هنوز شروع نشده
claimedدر حال اجراست
doneتمام شد
failedشکست خورد
expiredپیش از اجرا منقضی شد
done بدون نتیجه ممکن است. پنج وضعیت بالا دربارهٔ وظیفه است، نه دربارهٔ اینکه گوگل چیزی برگرداند. یک اجرای done می‌تواند کپچا خورده باشد یا هیچ نتیجهٔ ارگانیکی نداشته باشد — هر دو در نتایج با فلگ‌های flags.blocked و flags.no_results تفکیک می‌شوند.

فهرست پرس‌وجوها

GET /queries — نیازمند results:read.

همهٔ پرس‌وجوهای شما، جدیدترین اول. برای دیدن نتیجهٔ یک پرس‌وجوی خاص لازم نیست؛ همین فهرست را با فیلتر بخوانید.

نمونهٔ درخواست:

curl -s -H "X-API-Key: $KEY" \
  "https://serp.rasaad.ir/api/v1/operator/queries?status=done&limit=50"
پارامتریعنی چه
statusفیلتر وضعیت. چند مقدار با ویرگول جدا می‌شوند؛ مقدار ناشناخته 400 است
limit, cursorطبق صفحه‌بندی

نمونهٔ پاسخ:

{"ok": true, "rows": [ … ], "total": 412, "next_cursor": "…"}

هر ردیف یک پرس‌وجوست:

{"task_id": "5fa4d2c1", "keyword": "shoes", "city": "tehran", "domain": "example.ir",
 "status": "done", "created": 1767222000.0, "finished": 1767222400.0,
 "pages": 2, "expand_answers": 0, "position": 2, "bot_id": "tehran-…",
 "out_path": "…"}
فیلدیعنی چه
positionرتبهٔ دامنهٔ دنبال‌شده؛ اگر در صفحه‌هایی که بررسی شد نبود null
finishedزمان پایان؛ تا پیش از آن null
out_pathاستفاده نکنید؛ نتیجه را با task_id بخوانید
position یعنی «در عمقی که نگاه کردیم پیدا نشد». این با «نتیجه‌ای وجود ندارد» فرق دارد. اگر فقط صفحهٔ اول بررسی شده و دامنه در صفحهٔ چهارم است، position شما null است — نه اینکه گوگل آن را نداشته باشد.

گرفتن یک پرس‌وجو

GET /queries/{task_id} — نیازمند results:read.

یک پرس‌وجو را با task_id برمی‌گرداند. برای فهمیدن وضعیتش همین را فراخوانی کنید.

نمونهٔ درخواست:

curl -s -H "X-API-Key: $KEY" \
  https://serp.rasaad.ir/api/v1/operator/queries/5fa4d2c1

نمونهٔ پاسخ:

{"ok": true, "query": { … }}

ساختار query همان ردیف فهرست بالاست.

چطور صبر کنید

چون کار ناهمگام (async) است، به یک حلقهٔ ساده نیاز دارید. نمونه‌کد پیشنهادی:

TASK=$(curl -s -X POST -H "X-API-Key: $KEY" -H 'Content-Type: application/json' \
  -d '{"domain":"example.ir","keyword":"shoes","city":"tehran"}' \
  https://serp.rasaad.ir/api/v1/operator/queries \
  | jq -r '.task_id')

# صبر کن تا کار تمام شود
until [ "$(curl -s -H "X-API-Key: $KEY" \
  "https://serp.rasaad.ir/api/v1/operator/queries/$TASK" \
  | jq -r '.query.status')" = "done" ]; do
  sleep 10
done

# حالا نتیجه را بخوان
curl -s -H "X-API-Key: $KEY" \
  "https://serp.rasaad.ir/api/v1/operator/results/$TASK"
برای این حلقهٔ انتظار سقف بگذارید. وضعیت‌های failed و expired هرگز به done نمی‌رسند، پس شرط بالا بدون سقف زمان می‌تواند برای همیشه بچرخد. یک مهلت بگذارید و در صورت عبور از آن، همان task_id را نگه دارید تا بعداً بتوانید بررسی کنید چه شد.

اگر نتیجه نگرفتید

وضعیتیعنی چهکار کنید
queued طولانی می‌ماندهنوز شروع نشدهصبر کنید؛ task_id را نگه دارید
expiredپیش از اجرا منقضی شددوباره بفرستید
failedاجرا شکست خوردنتیجه را ببینید؛ flags علت را می‌گوید
done ولی نتیجه 404هیچ سندی ذخیره نشدflags را بررسی کنید
قبل از ساختن سیستم پایش، GET /usage را ببینید. می‌گوید در دورهٔ جاری چند پرس‌وجو ساخته‌اید، چند تا تمام شده و چند تا شکست خورده — یعنی قبل از اینکه در گزارش خودتان متوجه شوید، می‌توانید نرخ شکست را ببینید. در مصرف توضیح داده شده.