Search API
Find relevant web pages by keyword before scraping. Search returns URLs, titles, and snippets — optionally scraping each result inline.
Discovery-First Workflow
Overview
Search
POST a query to /api/v1/search. Get back URLs, titles, and snippets from Google.
Scrape (Optional)
Set scrape_results: true to scrape every result page and get full content back.
Extract (Optional)
Pass an extraction_schema to pull structured data from every result in one call.
POST /v1/search
/api/v1/searchExecute a web search. Returns results synchronously (200) or, when scrape_results is true and there are more than 5 results, returns 202 with a search_id for polling.
curl -X POST https://api.alterlab.io/api/v1/search \
-H "X-API-Key: your_api_key" \
-H "Content-Type: application/json" \
-d '{
"query": "best headless browsers 2026",
"num_results": 10
}'Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
| query | string | Yes | Search terms (1-500 characters) |
| domain | string | No | Restrict results to a specific domain (applied as site: prefix) |
| num_results | integer | No | Number of results to return (1-50, default: 10) |
| country | string | No | ISO 3166-1 alpha-2 country code for geo-targeted results (e.g., US, GB, DE) |
| language | string | No | Language code for results (e.g., en, fr, de) |
| time_range | string | No | Filter by recency: hour, day, week, month, year |
| scrape_results | boolean | No | If true, scrape each result page and include full content (default: false) |
| formats | string[] | No | Output formats when scrape_results=true: text, json, json_v2, html, markdown |
| extraction_schema | object | No | JSON schema for structured extraction (when scrape_results=true) |
Search-Only Response (200)
When scrape_results is false (default), results are returned synchronously:
{
"search_id": "a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
"query": "best headless browsers 2026",
"results_count": 10,
"credits_used": 2000,
"results": [
{
"url": "https://example.com/headless-browsers-guide",
"title": "Top 10 Headless Browsers in 2026",
"snippet": "A comprehensive comparison of the best headless browsers for web scraping and automation...",
"position": 1,
"content": null
},
{
"url": "https://blog.example.com/playwright-vs-puppeteer",
"title": "Playwright vs Puppeteer in 2026",
"snippet": "Which headless browser framework should you choose? We compare performance, features...",
"position": 2,
"content": null
}
]
}Search + Scrape Response (202)
When scrape_results=true and there are more than 5 results, the response is 202 with a polling URL:
{
"search_id": "a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
"query": "best headless browsers 2026",
"status": "scraping",
"results_count": 10,
"credits_used": 52000,
"results": [ ... ],
"message": "Search complete. 10 results are being scraped. Poll GET /v1/search/a1b2c3d4-... for progress."
}Inline Scraping
scrape_results=true and there are 5 or fewer results, the API waits briefly and attempts to return content inline. For larger result sets, use the polling endpoint.GET /v1/search/{search_id}
/api/v1/search/{search_id}Poll for search + scrape progress. Returns the search results with scraped content populated as each job completes.
curl https://api.alterlab.io/api/v1/search/a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d \
-H "X-API-Key: your_api_key"Status Response
{
"search_id": "a1b2c3d4-...",
"query": "best headless browsers 2026",
"status": "completed",
"results_count": 10,
"completed": 10,
"credits_used": 52000,
"results": [
{
"url": "https://example.com/headless-browsers-guide",
"title": "Top 10 Headless Browsers in 2026",
"snippet": "A comprehensive comparison...",
"position": 1,
"content": {
"text": "Full article text here...",
"markdown": "# Top 10 Headless Browsers..."
}
}
]
}| Status | Meaning |
|---|---|
| scraping | Some result pages are still being scraped |
| completed | All result pages have been scraped |
24-Hour TTL
Credit Model
| Action | Cost | Notes |
|---|---|---|
| Search query | 2 credits | Flat fee per search, regardless of num_results |
| Scrape per result | 1-5 credits | Standard scrape pricing per URL (tier-based) |
| Extraction | Included | No extra charge when using extraction_schema with scrape_results |
BYOP Discount
Error Codes
| Status | Error | Description |
|---|---|---|
| 402 | insufficient_credits | Not enough credits for the search or scrape |
| 404 | search_not_found | Search ID not found or expired (polling endpoint) |
| 502 | search_provider_error | Upstream search provider returned an error |
| 503 | search_unavailable | Search service is not configured |
| 504 | search_timeout | Search provider timed out |
Examples
Basic Search
import requests
response = requests.post(
"https://api.alterlab.io/api/v1/search",
headers={"X-API-Key": "YOUR_API_KEY"},
json={
"query": "web scraping best practices",
"num_results": 5
}
)
data = response.json()
for result in data["results"]:
print(f"{result['position']}. {result['title']}")
print(f" {result['url']}")Domain-Scoped Search
# Search within a specific domain
response = requests.post(
"https://api.alterlab.io/api/v1/search",
headers={"X-API-Key": "YOUR_API_KEY"},
json={
"query": "authentication API",
"domain": "docs.github.com",
"num_results": 10
}
)
# All results will be from docs.github.com
data = response.json()
print(f"Found {data['results_count']} pages on docs.github.com")Search + Scrape with Extraction
import time
# Search and scrape with structured extraction
response = requests.post(
"https://api.alterlab.io/api/v1/search",
headers={"X-API-Key": "YOUR_API_KEY"},
json={
"query": "iPhone 16 Pro review",
"num_results": 5,
"scrape_results": True,
"formats": ["text", "markdown"],
"extraction_schema": {
"type": "object",
"properties": {
"rating": {"type": "number", "description": "Review rating out of 10"},
"pros": {"type": "array", "items": {"type": "string"}},
"cons": {"type": "array", "items": {"type": "string"}},
"verdict": {"type": "string", "description": "One-line summary"}
}
}
}
)
data = response.json()
# If 202, poll for results
if response.status_code == 202:
search_id = data["search_id"]
while True:
status = requests.get(
f"https://api.alterlab.io/api/v1/search/{search_id}",
headers={"X-API-Key": "YOUR_API_KEY"}
).json()
if status["status"] == "completed":
data = status
break
time.sleep(2)
# Process extracted data
for result in data["results"]:
if result.get("content") and result["content"].get("extraction"):
ext = result["content"]["extraction"]
print(f"{result['title']}: {ext.get('rating')}/10")
print(f" Verdict: {ext.get('verdict')}")