Home ·
Examples ·
API Docs ·
Get Started ·
Downloads
Endpoint
GET https://www.botanon.com/api/fetch?url=...&apikey=...
POST https://www.botanon.com/api/fetch (JSON body)
Both methods accept the same parameters. Use POST when sending custom headers or complex POST data.
Authentication
Every request requires an apikey parameter. Contact [email protected] to get a key.
GET /api/fetch?url=https://example.com&apikey=YOUR_KEY
Parameters
Required
Parameter Description
url Target URL to fetch (http:// or https:// )
apikey Your API key
Request Options
Parameter Default Description
method GET HTTP method on target: GET or POST
post_data Request body for POST requests
post_type form POST encoding: form or json
headers Custom headers to forward to target (object, POST JSON only)
ua Chrome 131 User-Agent, or random for random modern browser UA
timeout 30 Timeout in seconds (max 120)
tag Label for log filtering (e.g. seo_audit )
originator_ip End-user IP for per-user rate limiting
Proxy Options
Parameter Default Description
proxy auto Force proxy: eris , zyte , scrapeowl , residential , browser , none
render_js 0 JS rendering via ScrapeOwl/Zyte (cheaper than browser)
premium 0 ScrapeOwl premium proxies
browser 0 Full headless browser via Cloudflare
country Geo-target: ISO code (e.g. US , GB )
Cache Options
Parameter Default Description
refresh 0 Bypass cache, fetch fresh
cache Override cache TTL in days (1–365)
compress 0 Gzip-compressed response
Cost: Default (free) → render_js=1 (API credits) → premium=1 (2x credits) → browser=1 (most expensive). Escalate only when needed.
Forward API keys, auth tokens, etc. to the target via POST JSON body:
cURL
curl -s -X POST https://www.botanon.com/api/fetch \
-H 'Content-Type: application/json' \
-d '{
"url": "https://api.bing.microsoft.com/v7.0/search?q=test",
"apikey": "YOUR_KEY",
"headers": {"Ocp-Apim-Subscription-Key": "YOUR_BING_KEY"},
"tag": "bing"
}' | jq .
Python
resp = requests.post("https://www.botanon.com/api/fetch", json={
"url": "https://api.bing.microsoft.com/v7.0/search?q=test",
"apikey": "YOUR_KEY",
"headers": {"Ocp-Apim-Subscription-Key": "YOUR_BING_KEY"},
"tag": "bing",
})
PHP
$ch = curl_init('https://www.botanon.com/api/fetch');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode([
'url' => 'https://api.bing.microsoft.com/v7.0/search?q=test',
'apikey' => 'YOUR_KEY',
'headers' => ['Ocp-Apim-Subscription-Key' => 'YOUR_BING_KEY'],
'tag' => 'bing',
]),
]);
Supported by all drivers: Eris (via x-fwd- prefix), Zyte, ScrapeOwl, Residential, Direct. Not supported by Browser (CF headless).
Response Format
Success (HTTP 200)
{
"success": true,
"request_id": "a7c3e9f2-4b1d-4e8a-9f2c-1a2b3c4d5e6f",
"url": "https://example.com",
"final_url": "https://www.example.com/",
"http_code": 200,
"body": "<!doctype html>\n<html>...",
"body_size": 45230,
"body_compressed_size": 8102,
"headers": {
"Content-Type": "text/html; charset=UTF-8",
"Server": "nginx"
},
"timing": {
"total_ms": 1230.5,
"connect_ms": 150.2,
"ssl_ms": 80.1,
"transfer_ms": 320.0
},
"proxy": {
"driver": "eris",
"name": "Eris Worker",
"proxy_id": 1
},
"retries": 0,
"cached": false,
"cache_expires": "2026-04-18 14:30:00",
"compressed": false,
"customer": {
"requests_today": 142,
"success_today": 140,
"failures_today": 2,
"requests_this_month": 4235
}
}
Failure (HTTP 502)
{
"success": false,
"error_code": "ALL_PROXIES_FAILED",
"error": "All proxies failed",
"url": "https://blocked-site.com",
"http_code": 403,
"last_proxy_error": "HTTP 403",
"last_proxy_driver": "residential",
"retries": 3,
"proxies_tried": ["eris", "zyte", "scrapeowl", "residential"]
}
Rate Limited (HTTP 429)
{
"success": false,
"error_code": "RATE_LIMIT_CUSTOMER",
"error": "Customer rate limit exceeded: 1001/1000 requests per hour",
"limit": 1000,
"current": 1001,
"retry_after": 60
}
Response Field Reference
Success Fields
Field Type Description
success bool Always true
request_id string Unique UUID for log correlation
url string The URL you requested
final_url string URL after redirects
http_code int HTTP status from target (200, 301, 404, etc.)
body string Full response body (HTML, JSON as string, text)
body_size int Body size in bytes
body_compressed_size int Body size after gzip
headers object Response headers from target
timing.total_ms float Total request time (ms)
timing.connect_ms float TCP connection time
timing.ssl_ms float SSL handshake time
timing.transfer_ms float Time to first byte
proxy.driver string Proxy used: eris, zyte, scrapeowl, residential, browser, none
proxy.name string Human-readable proxy name
proxy.proxy_id int Internal proxy ID
retries int Proxies that failed before success (0 = first try)
cached bool true if from cache (timing = 0)
cache_expires string Cache expiry datetime
compressed bool true if gzip-compressed
customer.requests_today int Your requests today
customer.success_today int Successful today
customer.failures_today int Failed today
customer.requests_this_month int Requests this month
Failure Fields
Field Type Description
success bool Always false
error_code string Machine-readable error code
error string Human-readable message
http_code int HTTP code from last proxy (0 if connection failed)
last_proxy_error string Last proxy's error message
last_proxy_driver string Last proxy driver tried
retries int Total proxies tried
proxies_tried array Drivers attempted, in order
limit int Rate limit threshold (429 only)
current int Current count in window (429 only)
retry_after int Seconds to wait (429 only)
JSON body: When the target returns JSON, the body field is a JSON string. Parse it: json.loads(data["body"]) (Python), json_decode($data['body'], true) (PHP), JSON.parse(data.body) (JS).
Error Codes
Code HTTP Meaning
MISSING_URL 400 No url parameter
MISSING_APIKEY 401 No apikey parameter
INVALID_APIKEY 403 Key not found or customer inactive
INVALID_URL 400 Malformed, not http/https, or private IP
INVALID_TIMEOUT 400 Exceeds customer max
INVALID_METHOD 400 Not GET or POST
INVALID_PROXY 400 Unknown proxy driver
RATE_LIMIT_CUSTOMER 429 Hourly limit exceeded
RATE_LIMIT_CALLER_IP 429 Caller IP limit exceeded
RATE_LIMIT_ORIGINATOR_IP 429 Originator IP limit exceeded
RATE_LIMIT_DAILY 429 Daily limit exceeded
ALL_PROXIES_FAILED 502 Every proxy failed
PROXY_ERROR 502 Proxy returned non-2xx
INTERNAL_ERROR 500 Server error
Proxy Chain
Default failover chain (configurable per-customer):
# Driver Type Best For
1 eris CF Worker Most sites, free, fast
2 zyte Premium API Anti-bot, CAPTCHAs
3 scrapeowl Premium API JS rendering, premium proxies
4 residential Residential IPs Datacenter-blocked sites
Special: browser (CF headless), none (direct from server).
Timeouts: Total defaults to 30s (max 120s). Connect capped at 10s — fails fast to try next proxy.
Caching
Default TTL: 7 days. Cache key = URL + method + POST body.
Priority Source Description
1 cache=N Per-request override (days)
2 Cache rules Admin URL patterns (e.g. %facebook.com% = 30d)
3 Global default 7 days
Rate Limits
Limit Default Scope
Customer/hour 1,000 Per API key
Caller IP/hour 2,000 Per server IP
Originator IP/hour 100 Per end-user IP
Customer/day Unlimited Daily cap
Code Examples
Simple GET
cURL
curl -s 'https://www.botanon.com/api/fetch?url=https://example.com&apikey=YOUR_KEY&tag=test&ua=random' | jq .
PHP
$params = http_build_query(['url' => 'https://example.com', 'apikey' => 'YOUR_KEY', 'tag' => 'test', 'ua' => 'random']);
$data = json_decode(file_get_contents("https://www.botanon.com/api/fetch?{$params}"), true);
echo $data['body'];
Python
import requests
resp = requests.get("https://www.botanon.com/api/fetch", params={
"url": "https://example.com", "apikey": "YOUR_KEY", "tag": "test", "ua": "random",
}, timeout=35)
data = resp.json()
print(data["body"])
Node.js
const params = new URLSearchParams({url: 'https://example.com', apikey: 'YOUR_KEY', tag: 'test', ua: 'random'});
const data = await (await fetch(`https://www.botanon.com/api/fetch?${params}`)).json();
console.log(data.body);
With Custom Headers (Bing API)
Python
import json
resp = requests.post("https://www.botanon.com/api/fetch", json={
"url": "https://api.bing.microsoft.com/v7.0/search?q=test",
"apikey": "YOUR_KEY",
"headers": {"Ocp-Apim-Subscription-Key": "YOUR_BING_KEY"},
"proxy": "none",
"tag": "bing",
})
results = json.loads(resp.json()["body"])
JS Rendering + Geo-targeting
cURL
curl -s 'https://www.botanon.com/api/fetch?url=https://spa-app.com&apikey=YOUR_KEY&proxy=scrapeowl&render_js=1&premium=1&country=US&cache=30' | jq .
POST Data to Target
cURL
curl -s -X POST https://www.botanon.com/api/fetch \
-H 'Content-Type: application/json' \
-d '{"url":"https://api.example.com/search","apikey":"YOUR_KEY","method":"POST","post_data":"{\"q\":\"test\"}","post_type":"json","tag":"search"}' | jq .
Compressed + Custom Cache TTL
cURL
curl -s --compressed 'https://www.botanon.com/api/fetch?url=https://example.com&apikey=YOUR_KEY&compress=1&cache=30' | jq .
Error Handling with Retry
Python
import requests, time
def fetch(url, tag="agent", retries=3):
for i in range(retries):
resp = requests.get("https://www.botanon.com/api/fetch", params={
"url": url, "apikey": "YOUR_KEY", "tag": tag, "ua": "random",
"refresh": 1 if i > 0 else 0,
}, timeout=35)
data = resp.json()
if data["success"]:
return data
if data.get("error_code", "").startswith("RATE_LIMIT"):
time.sleep(data.get("retry_after", 60))
continue
if data.get("http_code") in [404, 401, 403]:
return data # don't retry client errors
time.sleep(2 ** i)
raise Exception(f"Failed after {retries} attempts")