API Documentation
Complete guide to SearchCans SERP API and Reader API
API Overview - Dual Engine Platform
SearchCans provides two powerful APIs for AI applications:
SERP API
Get real-time SERP data from Google and via our Bing Search API. Perfect for SEO tools, market research, and AI applications that need to search the web.
View SERP API Docs →Reader API
Our URL to Markdown API turns any web page into clean structured markdown. Ideal for RAG pipelines and processing web content.
View Reader API Docs →Quick Start
Get started with SearchCans API in minutes:
- Sign up for a free account
- Get your API key from the Dashboard
- Make your first API call
Authentication
SearchCans API uses Bearer Token authentication. Include your API key in the request header:
Authorization: Bearer YOUR_API_KEY
SERP API - Google & Bing Search API
Endpoint
https://www.searchcans.com/api/search https://www.searchcans.com/api/search Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| s | string | Yes | Search keyword |
| d | number | No | Timeout in milliseconds, default 10000. Recommended: 20000ms for production |
| w | number | No | Maximum API wait time (ms), default 10000. Recommended: 3000-5000ms Note: The request continues to occupy a Parallel Search Lane while waiting. Tip: Set client-side timeout to 25s+ to prevent premature disconnects. |
| p | number | No | Page number, default 1 |
Request Examples
curl -X POST https://www.searchcans.com/api/search \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"s": "SpaceX Starship",
"t": "google",
"p": 1,
"d": 20000,
"w": 3000
}'Response Example
{
"code": 0,
"msg": "Success",
"data": [
{
"title": "Search result title",
"url": "https://example.com",
"content": "Search result summary content..."
}
]
}
Production-Ready Code Example
Batch keyword search tool with automatic retry and result saving.
import requests
import json
import time
import os
from datetime import datetime
# Configuration
API_KEY = "YOUR_API_KEY"
API_URL = "https://www.searchcans.com/api/search"
KEYWORDS_FILE = "keywords.txt" # One keyword per line
OUTPUT_DIR = "serp_results"
SEARCH_ENGINE = "google" # or "bing"
MAX_RETRIES = 3
def load_keywords(filepath):
"""Load keywords from file"""
if not os.path.exists(filepath):
print(f" Error: {filepath} not found")
return []
keywords = []
with open(filepath, 'r', encoding='utf-8') as f:
for line in f:
keyword = line.strip()
if keyword and not keyword.startswith('#'):
keywords.append(keyword)
return keywords
def search_keyword(keyword, page=1):
"""Search single keyword with SERP API"""
headers = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json"
}
payload = {
"s": keyword,
"t": SEARCH_ENGINE,
"d": 20000, # 20s server timeout
"p": page
}
try:
response = requests.post(API_URL, headers=headers, json=payload, timeout=30)
result = response.json()
if result.get("code") == 0:
print(f" Success: {len(result.get('data', []))} results")
return result
else:
print(f" Failed: {result.get('msg')}")
return None
except Exception as e:
print(f" Error: {e}")
return None
def search_with_retry(keyword):
"""Search with automatic retry"""
for attempt in range(MAX_RETRIES):
if attempt > 0:
print(f" Retry {attempt}/{MAX_RETRIES-1}...")
time.sleep(2)
result = search_keyword(keyword)
if result:
return result
print(f" Failed after {MAX_RETRIES} attempts")
return None
def save_result(keyword, result, output_dir):
"""Save search result"""
# Create safe filename
safe_name = "".join(c if c.isalnum() or c in (' ', '-', '_') else '_' for c in keyword)
safe_name = safe_name[:50]
# Save as individual JSON
json_file = os.path.join(output_dir, f"{safe_name}.json")
with open(json_file, 'w', encoding='utf-8') as f:
json.dump(result, f, ensure_ascii=False, indent=2)
# Also save to summary JSONL
jsonl_file = os.path.join(output_dir, "all_results.jsonl")
with open(jsonl_file, 'a', encoding='utf-8') as f:
record = {
"keyword": keyword,
"timestamp": datetime.now().isoformat(),
"result": result
}
f.write(json.dumps(record, ensure_ascii=False) + "
")
print(f" Saved: {safe_name}.json")
def main():
"""Main execution"""
print(" SearchCans SERP API Batch Search Tool")
print("=" * 60)
# Load keywords
keywords = load_keywords(KEYWORDS_FILE)
if not keywords:
return
total = len(keywords)
print(f" Loaded {total} keywords
")
# Create output directory
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
output_dir = f"{OUTPUT_DIR}_{timestamp}"
os.makedirs(output_dir, exist_ok=True)
print(f" Output: {output_dir}/
")
# Process each keyword
completed = 0
for index, keyword in enumerate(keywords, 1):
print(f"[{index}/{total}] Keyword: {keyword}")
result = search_with_retry(keyword)
if result:
save_result(keyword, result, output_dir)
# Extract and display URLs
urls = [item.get("url", "") for item in result.get("data", [])]
if urls:
print(f" {len(urls)} URLs found")
for i, url in enumerate(urls[:3], 1):
print(f" {i}. {url[:70]}...")
completed += 1
# Concurrency Control (Free tier has 1 lane, so we process serially)
if index < total:
time.sleep(1)
# Summary
print("
" + "=" * 60)
print(f" Summary: {completed}/{total} successful")
print(f" Results saved to: {output_dir}/")
if __name__ == "__main__":
main()
- Create
keywords.txtwith one keyword per line - Replace
YOUR_API_KEYwith your actual API key - Run:
python serp_search.py - Results saved as individual JSON files + summary JSONL
Features: Automatic retry on failure, progress tracking, batch result export.
Related Guides & Tutorials
Tutorials:
Also Available:
- • Reader API →
- Turn any URL into clean, LLM-ready Markdown. Perfect for RAG pipelines and AI training data.
Reader API - URL to Markdown Conversion
Why Reader API? The LLM-Ready Solution
Our URL to Markdown solution provides clean, structured, LLM-ready Markdown - the universal format for AI applications:
→ Why Markdown is the universal language for AI applications
Endpoint
POST https://www.searchcans.com/api/url
GET https://www.searchcans.com/api/url
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| s | string | Yes | URL to extract content from |
| t | string | Yes | Type, fixed value: url |
| w | number | No | Wait time after opening URL (milliseconds), default 3000. Recommended: 5000ms+ for slow JavaScript-heavy sites Note: The request continues to occupy a Parallel Search Lane while waiting. |
| d | number | No | Maximum wait time for API (milliseconds), default 20000. Recommended: 20000-30000ms for complex pages |
| b | boolean | No | Use browser rendering, default true. Set to true for JavaScript-heavy sites (SPAs, React, Vue apps) |
| proxy | number | No | Bypass Mode (Advanced): Set to 1 to enable. Uses enhanced network infrastructure to overcome access restrictions, with 98% success rate. Costs 5 credits (vs 2 credits for normal mode). Try normal mode first, use bypass only when needed. |
Bypass Mode (Advanced Feature)
When you encounter access restrictions on certain URLs, enable bypass mode by adding proxy: 1 to your request.
What is Bypass Mode?
Bypass mode uses an enhanced network infrastructure to overcome access restrictions on challenging URLs. It successfully handles 98% of sites that may block standard requests.
Credit Consumption:
- • Normal mode: 2 credits per request
- • Bypass mode: 5 credits per request
Best Practice (Cost Optimization):
Always try normal mode first. Only enable bypass mode when you receive an error response. This cost-effective approach maximizes success while minimizing expenses.
Example Code (Python):
# Try normal mode first (2 credits)
payload = {
"s": "https://example.com",
"t": "url",
"b": True,
"w": 3000, # Wait 3s for JS (use 5000ms+ for heavy sites)
"d": 20000
}
response = requests.post(api_url, json=payload, headers=headers)
result = response.json()
# If failed, retry with bypass mode (5 credits)
if result.get("code") != 0:
print("Normal mode failed, enabling bypass mode...")
payload["proxy"] = 1 # Enable enhanced access
response = requests.post(api_url, json=payload, headers=headers)
result = response.json() Request Examples
curl -X POST https://www.searchcans.com/api/url \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"s": "https://www.searchcans.com/?utm_source=api_docs&utm_medium=demo_code&utm_campaign=reader_api_test",
"t": "url",
"w": 3000,
"d": 20000,
"b": true
}'Response Example
{
"code": 0,
"msg": "Success",
"data": {
"title": "SearchCans: Google SERP API ($0.56/1K)",
"description": "The only SERP API with Parallel Search Lanes. Scrape Google/Bing & convert URLs to Markdown.",
"markdown": "[](https://www.searchcans.com/)\n\n# SERP API + Reader API\n\nOur dual-engine platform offers a complete solution for AI Agents.\n\n## Key Features\n\n* **10x Cheaper**: Industry-lowest pricing at $0.56 per 1,000 searches.\n* **URL to Markdown**: Converts messy web pages into clean structured data.\n\n[Get Started Free](https://www.searchcans.com/register/)\n\n...(content truncated for brevity)",
"html": "<!DOCTYPE html><html lang=\"en-US\">...</html>"
},
"requestId": "req_7f8a9b2c3d"
}
Parameter Usage Tips
- w (wait time): 3000ms (Default) is suitable for most modern websites. Increase to 5000ms+ for heavy SPAs (React/Vue) that load slowly.
- d (timeout): Set to 20000-30000ms for complex pages with slow loading. If you encounter timeout errors, increase this value.
- b (browser mode):
- •
true: Full JavaScript rendering, slower but complete (2-5s). Recommended for SPAs and dynamic content. - •
false: Fast static extraction, may miss JS-generated content (0.5-1s). Use for simple HTML pages.
- •
Production-Ready Example with Error Handling
import requests
import json
import time
def extract_content(url, retry=3):
"""
Extract content with optimal settings and error handling
"""
api_url = "https://www.searchcans.com/api/url"
headers = {
"Authorization": f"Bearer YOUR_API_KEY",
"Content-Type": "application/json"
}
payload = {
"s": url,
"t": "url",
"w": 3000, # Wait 3s for JS (use 5000ms+ for heavy sites)
"d": 20000, # 20s server timeout
"b": True # Enable browser mode
}
for attempt in range(retry):
try:
# Set timeout higher than 'd' parameter (30s) to avoid premature disconnection
response = requests.post(
api_url,
headers=headers,
json=payload,
timeout=35
)
result = response.json()
if result["code"] == 0:
# Parse the data (may be string or object)
data = result["data"]
if isinstance(data, str):
data = json.loads(data)
return {
"title": data.get("title", ""),
"markdown": data.get("markdown", ""),
"html": data.get("html", ""),
"description": data.get("description", "")
}
else:
print(f"API Error: {result.get('msg')}")
if attempt < retry - 1:
time.sleep(2) # Wait before retry
continue
return None
except requests.exceptions.Timeout:
print(f"Timeout on attempt {attempt + 1}/{retry}")
if attempt < retry - 1:
time.sleep(2)
continue
return None
except Exception as e:
print(f"Error: {str(e)}")
return None
return None
# Usage Example
if __name__ == "__main__":
url = "https://example.com/article"
content = extract_content(url)
if content:
print(f"[SUCCESS] Title: {content['title']}")
print(f"[INFO] Content length: {len(content['markdown'])} characters")
# Save to file
with open("output.md", "w", encoding="utf-8") as f:
f.write(f"# {content['title']}\n\n")
f.write(content['markdown'])
print("[SUCCESS] Saved to output.md")
else:
print("[ERROR] Extraction failed")
Complete Data Structure
The Reader API returns a rich data structure with multiple formats:
{
"title": "Article Title", // Page title
"description": "Meta description", // SEO description
"markdown": "# Clean content...", // LLM-ready Markdown
"html": "<html>...</html>" // Full HTML source
} Save in Multiple Formats:
Perfect for LLM training, RAG pipelines, and documentation
Full page source for archiving and analysis
Structured data with all metadata for databases
Pro Tip: Save all three formats for maximum flexibility. Markdown for AI, HTML for backup, JSON for metadata.
Common Errors & Solutions
- • Increase
dparameter to 25000-30000ms - • Check if target website is accessible
- • Set your code's timeout higher than
dparameter
- • Check if
response.datais a string, useJSON.parse() - • Validate the response structure before accessing fields
- • Set
b: trueto enable browser rendering - • Increase
wto 5000ms for heavy JavaScript sites - • Note: Some websites have advanced anti-scraping protection. For sites with high-level anti-bot measures, we cannot guarantee 100% success rate in content extraction.
Related Guides & Tutorials
Learn More:
Also Available:
- • SERP API (Search API) →
- Get real-time search results from Google and Bing. Perfect for SEO tools and market research.
Response Codes & Error Handling
All API responses include a code field in the JSON body to indicate the request status. Understanding these codes helps you handle errors effectively.
| Code | Meaning | Description |
|---|---|---|
200 | Success | Request processed successfully. |
1010 | Lane Limit Exceeded | Concurrency limit reached. Solution: Pause briefly (100-500ms) and retry (Client-side Queueing). |
1001 | Response Timeout | Request exceeded wait time. Solution: Increase `d` parameter (e.g., 20000ms). |
401 | Unauthorized | Missing or invalid API key. Check Authorization header. |
403 | Forbidden | Quota exceeded or insufficient permissions. Check Dashboard. |
443 | Connection Error | SSL/Network handshake failed. Verify network connectivity. |
10054 | Connection Reset | Remote host closed connection. Solution: Retry request. |
1009 | Endpoint Lane Limit | Too many requests to this specific endpoint. Reduce parallelism here. |
1006 | User Frequency Limit | Burst speed too high. Add small delay between requests. |
1004 | API Frequency Limit | Global API protection triggered. Retry later. |
1003 | IP Frequency Limit | IP request rate too high. Rotate IPs or slow down. |
1005 | IP Endpoint Limit | IP rate limit for this endpoint. |
1002 | System Busy | System under high load. Please retry later. |
500 | Internal Server Error | Unexpected server error. Retry or contact support. |
9999 | No Results Found | Google hasn't returned any results for this query. Try different keywords or check if the query is too specific. |
Pro Tip: Handling Concurrency Limits (1010)
If you receive error 1010, it means your AI agent is running faster than your plan's Parallel Search Lanes allow.
- Don't Stop: This is not a fatal error.
- Client-side Queueing: Simply pause for 100ms-500ms and retry the request.
- Maximize Throughput: This "retry" pattern allows you to fully utilize 100% of your lane capacity without dropping requests.
Best Practices for Error Handling
- Always check the
codefield before processingdata - Implement retry logic with exponential backoff for timeout errors (1001)
- Log error codes and messages for debugging
- Set appropriate timeout values based on your use case (SERP API: 10-15s, Reader API: 15-30s)
Technical Support
If you encounter any issues, please contact us:
- Visit our API Playground for online testing
- Check the Pricing page for more package information
- Read our FAQ for common questions
- Submit a technical support request through the Dashboard