-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserp_basic.py
More file actions
153 lines (118 loc) · 5.08 KB
/
Copy pathserp_basic.py
File metadata and controls
153 lines (118 loc) · 5.08 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
"""Basic SERP scraping examples using the AlterLab API.
Demonstrates how to:
- Execute a Google SERP scrape with ad extraction
- Parse organic results and ad placements
- Handle common errors (401, 402, 422, 503)
- Poll for async results
"""
import time
from typing import Any, Dict
import requests
API_KEY = "sk_test_..."
BASE_URL = "https://api.alterlab.io/v1/serp"
def serp_search(query: str, **kwargs: Any) -> Dict[str, Any]:
"""Execute a SERP scrape and return the full response.
The /v1/serp endpoint uses browser-tier rendering (T4) to capture ads,
organic results, and rich SERP data from Google. Cost: ~$0.004/request.
Args:
query: Search query string (max 500 chars).
**kwargs: Additional SerpRequest fields (search_engine, device, country,
include_ads, brand_domain, resolve_redirects, etc.)
Returns:
Full SerpResponse dict with organic_results, ads, rich results, and cost.
Raises:
requests.HTTPError: On API errors (401, 402, 422, 503).
"""
payload = {"query": query, **kwargs}
response = requests.post(
BASE_URL,
headers={
"X-API-Key": API_KEY,
"Content-Type": "application/json",
},
json=payload,
timeout=60,
)
response.raise_for_status()
return response.json()
def poll_serp_result(serp_id: str, max_wait: int = 60) -> Dict[str, Any]:
"""Poll for an async SERP result by serp_id.
Use this when the initial request returns a 202 (async) or when using
webhook_url and want to manually check status.
Args:
serp_id: The serp_id returned from the initial request.
max_wait: Maximum seconds to poll before giving up.
Returns:
Full SerpResponse when complete.
"""
url = f"{BASE_URL}/{serp_id}"
start = time.time()
while time.time() - start < max_wait:
resp = requests.get(url, headers={"X-API-Key": API_KEY}, timeout=30)
resp.raise_for_status()
data = resp.json()
if data.get("status") == "completed":
return data
elif data.get("status") == "failed":
raise RuntimeError(f"SERP scrape failed: {data.get('error')}")
time.sleep(2)
raise TimeoutError(f"SERP result not ready after {max_wait}s")
def handle_errors_example() -> None:
"""Demonstrate error handling for common SERP API errors."""
try:
result = serp_search("best crm software")
print(f"Success! Found {result['organic_count']} organic results")
except requests.HTTPError as e:
status = e.response.status_code
if status == 401:
print("Authentication failed — check your API key")
elif status == 402:
err = e.response.json()
topup_url = err.get("topup_url", "https://alterlab.io/dashboard/billing")
print(f"Insufficient credits — add funds at: {topup_url}")
elif status == 422:
# Validation error — the request body has invalid fields
errors = e.response.json().get("detail", [])
print(f"Validation error: {errors}")
elif status == 503:
print("Service temporarily unavailable — retry after a few seconds")
else:
print(f"Unexpected error {status}: {e.response.text}")
def main() -> None:
"""Run basic SERP examples."""
# 1. Simple Google SERP search with ads
print("=== Basic SERP Search ===")
result = serp_search("best project management tools 2026")
print(f"Query: {result['query']}")
print(f"Organic results: {result['organic_count']}")
print(f"Ads found: {result['ads_count']}")
print(f"Cost: ${result['cost_breakdown']['total_cost_usd']:.4f}")
# Print first 3 organic results
for i, item in enumerate(result["organic_results"][:3], 1):
print(f" {i}. {item['title']} — {item['url']}")
# 2. SERP search with ads breakdown by placement
print("\n=== Ad Placements ===")
result = serp_search("buy running shoes online", include_ads=True)
placements = result.get("ads_by_placement", {})
print(f"Top ads: {placements.get('top', 0)}")
print(f"Bottom ads: {placements.get('bottom', 0)}")
print(f"Shopping ads: {placements.get('shopping', 0)}")
for ad in result["ads"][:3]:
print(f" [{ad['placement']}] {ad['headline']} — {ad['display_url']}")
# 3. Mobile SERP (different ad layout)
print("\n=== Mobile SERP ===")
result = serp_search("pizza delivery near me", device="mobile", country="US")
print(f"Mobile organic results: {result['organic_count']}")
print(f"Mobile ads: {result['ads_count']}")
if result.get("local_results"):
print(f"Local pack results: {len(result['local_results'])}")
# 4. SERP without ads (faster, organic only)
print("\n=== Organic Only (no ads) ===")
result = serp_search("python asyncio tutorial", include_ads=False)
print(f"Organic results: {result['organic_count']}")
print(f"Ads: {result['ads_count']} (should be 0)")
# 5. Error handling
print("\n=== Error Handling ===")
handle_errors_example()
if __name__ == "__main__":
main()