نتایج گوگل

نتایج

سند نتیجه در سرویس نتایج گوگل، قالب سازگار با SerpAPI، نمایش markdown و HTML خام، و صفحه‌بندی روی مرزهای واقعی صفحات.

نتایج، آن چیزی است که در نهایت به دنبالش بودید: آنچه گوگل در لحظهٔ اجرا نشان داده بود.

GET /results/{task_id} سند اصلی است. سه خروجی دیگر هم از همان سند ساخته می‌شوند، پس می‌توانید سند اصلی را کش (cache) کنید و بقیه را خودتان از رویش بسازید، بدون خزش دوباره.

فهرست نتایج

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

فقط اجراهای تمام‌شده‌ای که واقعاً سندی ساخته‌اند، جدیدترین اول.

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

curl -s -H "X-API-Key: $KEY" \
  "https://serp.rasaad.ir/api/v1/operator/results?domain=example.ir&keyword=shoes"
پارامتریعنی چه
target_idهمهٔ اجراهای یک هدف
domain, keyword, cityفیلترهای تطبیق دقیق. keyword نرمالایز می‌شود
since, untilثانیهٔ epoch روی finished. فیلتر هستند، نه کرسر
limit, cursorطبق صفحه‌بندی

نمونهٔ پاسخ:

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

هر ردیف یک نتیجه است:

{"task_id": "5fa4d2c1", "keyword": "shoes", "norm_keyword": "shoes", "city": "tehran",
 "domain": "example.ir", "bot_id": "tehran-…", "position": 2, "pages": 2,
 "finished": 1767222400.0, "out_path": "…"}

گرفتن یک نتیجه

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

سند کامل یک اجرا را برمی‌گرداند. این همان سندی است که سه خروجی بعدی از رویش ساخته می‌شوند.

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

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

نمونهٔ پاسخ:

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

ساختار کامل result در بخش سند نتیجه آمده است.

اگر برای یک تسک سندی ذخیره نشده باشد، هر چهار مسیر نتایج 404 با پیام task has no stored results می‌دهند. این شامل حالتی هم می‌شود که اجرا done شده ولی چیزی برای ذخیره پیدا نشده — برای فهمیدن دلیل، به GET /queries/{task_id} و flags نگاه کنید.

سه خروجی دیگر از همین سند

GET /results/{task_id} سند اصلی است. سه مسیر زیر خروجی همان سند هستند:

مسیرچه برمی‌گرداندنوع محتوا
GET /results/{task_id}/serpapi?page=Nقالب سازگار با SerpAPIapplication/json
GET /results/{task_id}/markdownنمایش markdowntext/markdown
GET /results/{task_id}/raw?page=NDOM ثبت‌شدهٔ یک صفحهtext/html
اگر برای یک تسک سندی ذخیره نشده باشد، هر چهار مسیر 404 با پیام task has no stored results می‌دهند. این شامل حالتی هم می‌شود که اجرا done شده ولی چیزی برای ذخیره پیدا نشده — برای فهمیدن دلیل، به GET /queries/{task_id} و flags نگاه کنید.

سند نتیجه

ساختار سند نتیجه این است:

{
  "task_id": "5fa4…",
  "ts": "2026-10-02T02:06:17Z",
  "domain": "example.ir",
  "keyword": "shoes",
  "city": "tehran",
  "bot_id": "tehran-ali-e782b0",
  "rank": 2,
  "url": "https://www.google.com/search?q=shoes&hl=fa&gl=ir",
  "items": [
    {
      "position": 1, "title": "…", "url": "https://example.ir/shoes",
      "display_url": "example.ir", "snippet": "…", "kind": "organic",
      "page": 1, "redirect_link": "https://www.google.com/url?q=…",
      "date": "2 هفته پیش", "snippet_highlighted_words": ["…"],
      "read_more_link": "…#:~:text=…", "about_page_link": "…", "source": "example.ir"
    }
  ],
  "flags": {"pages": 2, "device": "desktop", "html_len": 520122},
  "meta": { "search_information": {…}, "google": {…}, "related_questions": […] }
}

فیلدهای قابل اتکا

فیلدیعنی چه
rankرتبهٔ اولین نتیجه‌ای که با domain می‌خواند (شمارش از ۱)
items[].kindorganic | ad | featured | local | knowledge | social | other
items[].urlیک URL معتبر http(s)، وقتی خالی نباشد
items[].pageصفحه‌ای از گوگل که نتیجه برای اولین بار در آن دیده شده
flags.pagesنتایج از چند صفحهٔ گوگل آمده‌اند
rank وقتی null است یعنی «در صفحه‌هایی که بررسی شد نبود». این با «نتیجه‌ای وجود ندارد» فرق دارد. برای اینکه بفهمید دامنه اصلاً دیده نشده یا فقط بیرون از عمق بررسی بوده، flags.pages را هم ببینید.
items[].url گاهی خالی است و این خطا نیست. کارت‌هایی مثل کارت اطلاعاتی، لینک واقعی ندارند؛ در این حالت url: "" است ولی متن نمایشیشان باقی می‌ماند. قبل از استفاده از لینک، معتبر بودن url را بررسی (validate) کنید.

items[].page مرزهای واقعی است

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

انتظار نادرسترفتار واقعی
هر صفحه دقیقاً ۱۰ نتیجهیک صفحه می‌تواند کمتر از ۱۰ ردیف داشته باشد
نتیجهٔ ۱۱ یعنی صفحهٔ ۲نتیجهٔ شمارهٔ ۱۱ ممکن است هنوز روی صفحهٔ ۱ باشد
اگر رتبه را نمایش می‌دهید، items[].page را نادیده نگیرید و از تقسیم position بر ۱۰ استفاده نکنید. برای هر ردیف، position جای واقعی و page شمارهٔ واقعی صفحه است.

فلگ‌های flags

فلگیعنی چه
flags.blocked: trueکپچا یا صفحهٔ بین‌راهی — نه مجموعهٔ نتایج خالی
flags.no_results: trueاستخراج چیزی پیدا نکرد
flags.pagesنتایج از چند صفحهٔ گوگل آمده‌اند
flags.html_lenطول HTML خام
flags.devicedesktop یا mobile
گوگل می‌تواند بی‌دلیل هیچ نتیجهٔ ارگانیکی برنگرداند.no_results: true شکست پرس‌وجو نیست؛ وضعیت واقعیِ همان لحظه است.

meta

meta اطلاعات صفحهٔ اول را دارد: داده‌های جستجو، لینک‌های صفحه‌بندی خود گوگل، AI overview و تصاویر داخل صفحه.

فیلدهای raw_html و raw_pages گاهی در سند می‌آیند، ولی برای خواندن مستقیم نیستند. برای گرفتن HTML از مسیر raw استفاده کنید.

قالب SerpAPI

GET /results/{task_id}/serpapi?page=N — قالبی سازگار با SerpAPI برمی‌گرداند، تا کد کلاینت موجودتان فقط با عوض کردن آدرس (URL) کار کند.

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

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

نمونهٔ پاسخ:

{
  "search_metadata": {
    "id": "5fa4…", "status": "Success",
    "json_endpoint": "https://serp.rasaad.ir/api/v1/operator/results/5fa4…/serpapi",
    "markdown_endpoint": "…/markdown",
    "created_at": "2026-10-02 02:06:17 UTC", "processed_at": "2026-10-02 02:06:17 UTC",
    "google_url": "https://www.google.com/search?q=shoes&hl=fa&gl=ir",
    "raw_html_file": "…/raw",
    "total_time_taken": 3.42, "bot_id": "tehran-ali-e782b0",
    "domain": "example.ir", "rank": 2, "pages_crawled": 2, "paa_expanded": 0
  },
  "search_parameters": {
    "engine": "google", "q": "shoes", "google_domain": "google.com",
    "location_requested": "iran", "location_used": "iran",
    "hl": "fa", "gl": "ir", "device": "desktop", "num": 20
  },
  "search_information": {
    "query_displayed": "shoes", "total_results": "…",
    "time_taken_displayed": "…", "organic_results_state": "…"
  },
  "inline_images": [ { "thumbnail": "…", "original": "…", "title": "…" } ],
  "ai_overview": { "text": "…", "links": [ … ], "page_token": null, "serpapi_link": null },
  "related_questions": [ { "question": "…", "serp_link": "…", "answer": "", "answer_source": "" } ],
  "related_searches": [ { "query": "…", "link": "https://www.google.com/search?q=…" } ],
  "organic_results": [ { "position": 1, "title": "…", "link": "…", "page": 1, … } ],
  "pagination": { "current": 1, "next": "…", "other_pages": {"3": "…"}, "api_pagination": null },
  "serpapi_pagination": { "2": { "start": 10, "q": "shoes", … } }
}
فقط آنچه گوگل واقعاً نمایش داده پر است. چند URL که فقط خود SerpAPI می‌تواند بسازد null هستند یا با پیوندی که سرویس برمی‌گرداند جایگزین شده‌اند — هرگز ساختگی نیستند. اگر null دیدید، یعنی قابل ساخت نبوده، نه اینکه از قلم افتاده باشد.

مقدار status

مقداریعنی چه
Successاستخراج انجام شد
Blockedکپچا یا صفحهٔ بین‌راهی
Errorخطای استخراج

از روی همین فلگ‌ها مشخص می‌شود، حدس زده نمی‌شود.

وضعیت Blocked یعنی کپچا، و فرستادن دوبارهٔ همان درخواست دوباره بلاک می‌شود. تلاش مجدد را با تغییر در ورودی بفرستید — شهر دیگر، device متفاوت — نه با تکرار بی‌تغییر.

افراد دیگر هم پرسیده‌اند

related_questions همان بلوک «افراد دیگر هم پرسیده‌اند» گوگل است.

موضوعرفتار
خود پرسش‌هاهمیشه ثبت می‌شوند؛ در DOM اولیه هستند
answer و answer_sourceفقط وقتی در درخواست expand_answers فرستاده شده و پاسخ‌ها باز شده باشند
اگر بازکردن پاسخ‌ها درخواست نشده باشد"" — و این خطا نیست
این بلوک جمع‌شده نمایش داده می‌شود، پس پاسخ‌ها داخل HTML نیستند. به همین دلیل اگر expand_answers نفرستاده باشید، پاسخ خالی می‌گیرید. این رفتار درست است، نه نقص.

search_metadata.paa_expanded می‌گوید واقعاً چند پاسخ ثبت شد. این عدد می‌تواند کمتر از درخواستی باشد: پرسشی که باز نشود رد می‌شود و دوباره تلاش نمی‌شود، و اگر وسط کار کپچا بیاید، همان‌جا متوقف می‌شود.

اگر paa_expanded غیرصفر است ولی بعضی answerها خالی‌اند، یعنی «ناقص»، نه «ناموفق». برای اینکه بفهمید پاسخ‌ها کامل‌اند یا نه، paa_expanded را با تعداد پرسش‌ها مقایسه کنید.

related_searches فهرست جستجوهای مرتبط پایین صفحه است و اغلب خالی است؛ گوگل آن را در بسیاری از پرس‌وجوها نشان نمی‌دهد.

هر دو همیشه فهرست‌اند، هرگز null.

صفحه‌بندی خروجی serpapi

?page=N از ۱ شروع می‌شود و organic_results را بر اساس مرزهای واقعی برش می‌زهد. یعنی صفحهٔ ۲ از هر شماره‌ای شروع می‌شود که صفحهٔ ۱ واقعاً به آن رسیده، نه از عدد ثابتی مثل ۱۱.

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

?page=N از ۱ شروع می‌شود و organic_results را بر اساس مرزهای واقعی برش می‌زند. یعنی صفحهٔ ۲ از هر شماره‌ای شروع می‌شود که صفحهٔ ۱ واقعاً به آن رسیده، نه از عدد ثابتی مثل ۱۱.

# کل اجرا را پیمایش کن
DEPTH=$(curl -s -H "X-API-Key: $KEY" \
  "https://serp.rasaad.ir/api/v1/operator/results/$TASK/serpapi" \
  | jq '.search_metadata.pages_crawled')

for p in $(seq 1 "$DEPTH"); do
  curl -s -H "X-API-Key: $KEY" \
    "https://serp.rasaad.ir/api/v1/operator/results/$TASK/serpapi?page=$p" \
    | jq '.organic_results[]'
done

نمونهٔ پاسخ — هر درخواست یک صفحه از organic_results برمی‌گرداند:

{"position": 11, "title": "…", "link": "…", "page": 2, "position_in_page": 1}
منبعچه می‌گوید
search_metadata.pages_crawledعمق واقعی. چند صفحه واقعاً بررسی شده
pagination.next و other_pagesپیوندهای خود گوگل، آن‌طور که از صفحهٔ ۱ ثبت شده‌اند
pagination.next می‌تواند به فراتر از pages_crawled اشاره کند. این لینک‌ها نشان می‌دهند گوگل چه پیشنهاد داده، نه اینکه چه چیزی بررسی شده. پرسیدن فراتر از عمق بررسی‌شده organic_results خالی برمی‌گرداند، نه خطا.

هر ردیف organic_results[] شمارهٔ صفحهٔ خودش را در page دارد و raw_html_file به همان صفحه اشاره می‌کند — یعنی ?page=2 هرگز صفحهٔ ۳ را برنمی‌گرداند.

خروجی markdown

GET /results/{task_id}/markdown — همان سند را به‌صورت markdown برمی‌گرداند. برای نمایش سریع نتیجه به انسان، بدون پارس کردن JSON.

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

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

نمونهٔ پاسخ:

# shoes

> 2. example.ir/shoes — کفش فروشی آنلاین
> …خلاصهٔ نتیجه…

1. [example.ir](https://example.ir/shoes) — کفش فروشی آنلاین
2. [digikala.com](https://digikala.com) — خرید کفش

متن ساده است، نه JSON. برای نمایش سریع به انسان، بدون پارس کردن.

HTML خام

GET /results/{task_id}/raw?page=N — همان DOMای را برمی‌گرداند که گوگل فرستاده، به‌علاوهٔ یک تگ <base> که فقط به پاسخ اضافه می‌شود.

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

curl -s -H "X-API-Key: $KEY" \
  "https://serp.rasaad.ir/api/v1/operator/results/5fa4d2c1/raw?page=1"

نمونهٔ پاسخ (ابتدای فایل):

<base href="https://www.google.com/">

DOM گوگل از hrefهای نسبی ریشه استفاده می‌کند: کنترل صفحهٔ بعد، هر پرسش «افراد دیگر هم پرسیده‌اند»، و شبکهٔ جستجوهای مرتبط. بدون <base>، مرورگر این‌ها را نسبت به مبدأی که سند را از آن خوانده‌اید حل می‌کند و همه 404 می‌شوند.

موضوعرفتار
فشرده‌سازیبا Content-Encoding: gzip وقتی Accept-Encoding را اعلام کنید
کشبا Vary: Accept-Encoding تا کش‌ها نسخهٔ درست را نگه دارند
Accept-Encoding: identityبایت‌های یکسان، فشرده‌نشده
صفحه‌ای که ثبت نشده404، و پیام صفحه‌هایی را که دارید فهرست می‌کند
HTML خام را برای پایش بلندمدت ذخیره نکنید. ساختار DOM گوگل بدون اطلاع تغییر می‌کند. برای استفادهٔ بلندمدت به items[] و rank تکیه کنید؛ از raw فقط برای فهمیدن دلیل خرابی استفاده کنید، نه به‌عنوان مبنای منطق برنامه. اگر DOM عوض شود، GET /selector-health هشدار می‌دهد.

خلاصه

کارمسیر
فهرست نتایجGET /results
یک سند نتیجهGET /results/{task_id}
قالب SerpAPIGET /results/{task_id}/serpapi?page=N
markdownGET /results/{task_id}/markdown
HTML خامGET /results/{task_id}/raw?page=N
قدم بعدی:مصرف. قبل از اینکه صفحه‌بندی و حلقهٔ انتظار را در برنامهٔ خودتان بسازید، ببینید در یک دوره چند اجرا داشته‌اید و چند تایشان واقعاً سند ساخته‌اند.