نتایج
نتایج، آن چیزی است که در نهایت به دنبالش بودید: آنچه گوگل در لحظهٔ اجرا نشان داده بود.
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 | قالب سازگار با SerpAPI | application/json |
GET /results/{task_id}/markdown | نمایش markdown | text/markdown |
GET /results/{task_id}/raw?page=N | DOM ثبتشدهٔ یک صفحه | 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[].kind | organic | 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.device | desktop یا 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", … } }
}
null هستند یا با پیوندی که سرویس برمیگرداند جایگزین شدهاند — هرگز ساختگی نیستند.
اگر null دیدید، یعنی قابل ساخت نبوده، نه اینکه از قلم افتاده باشد.مقدار status
| مقدار | یعنی چه |
|---|---|
Success | استخراج انجام شد |
Blocked | کپچا یا صفحهٔ بینراهی |
Error | خطای استخراج |
از روی همین فلگها مشخص میشود، حدس زده نمیشود.
Blocked یعنی کپچا، و فرستادن دوبارهٔ همان درخواست دوباره بلاک میشود. تلاش
مجدد را با تغییر در ورودی بفرستید — شهر دیگر، device متفاوت — نه با تکرار بیتغییر.افراد دیگر هم پرسیدهاند
related_questions همان بلوک «افراد دیگر هم پرسیدهاند» گوگل است.
| موضوع | رفتار |
|---|---|
| خود پرسشها | همیشه ثبت میشوند؛ در DOM اولیه هستند |
answer و answer_source | فقط وقتی در درخواست expand_answers فرستاده شده و پاسخها باز شده باشند |
| اگر بازکردن پاسخها درخواست نشده باشد | "" — و این خطا نیست |
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، و پیام صفحههایی را که دارید فهرست میکند |
items[] و rank تکیه کنید؛ از raw فقط برای فهمیدن دلیل خرابی استفاده کنید، نه بهعنوان مبنای منطق برنامه. اگر DOM عوض شود،
GET /selector-health هشدار میدهد.خلاصه
| کار | مسیر |
|---|---|
| فهرست نتایج | GET /results |
| یک سند نتیجه | GET /results/{task_id} |
| قالب SerpAPI | GET /results/{task_id}/serpapi?page=N |
| markdown | GET /results/{task_id}/markdown |
| HTML خام | GET /results/{task_id}/raw?page=N |