API Documentation

Complete reference for the BotAnon proxy API

Contents

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

ParameterDescription
urlTarget URL to fetch (http:// or https://)
apikeyYour API key

Request Options

ParameterDefaultDescription
methodGETHTTP method on target: GET or POST
post_dataRequest body for POST requests
post_typeformPOST encoding: form or json
headersCustom headers to forward to target (object, POST JSON only)
uaChrome 131User-Agent, or random for random modern browser UA
timeout30Timeout in seconds (max 120)
tagLabel for log filtering (e.g. seo_audit)
originator_ipEnd-user IP for per-user rate limiting

Proxy Options

ParameterDefaultDescription
proxyautoForce proxy: eris, zyte, scrapeowl, residential, browser, none
render_js0JS rendering via ScrapeOwl/Zyte (cheaper than browser)
premium0ScrapeOwl premium proxies
browser0Full headless browser via Cloudflare
countryGeo-target: ISO code (e.g. US, GB)

Cache Options

ParameterDefaultDescription
refresh0Bypass cache, fetch fresh
cacheOverride cache TTL in days (1–365)
compress0Gzip-compressed response
Cost: Default (free) → render_js=1 (API credits) → premium=1 (2x credits) → browser=1 (most expensive). Escalate only when needed.

Custom Headers

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

FieldTypeDescription
successboolAlways true
request_idstringUnique UUID for log correlation
urlstringThe URL you requested
final_urlstringURL after redirects
http_codeintHTTP status from target (200, 301, 404, etc.)
bodystringFull response body (HTML, JSON as string, text)
body_sizeintBody size in bytes
body_compressed_sizeintBody size after gzip
headersobjectResponse headers from target
timing.total_msfloatTotal request time (ms)
timing.connect_msfloatTCP connection time
timing.ssl_msfloatSSL handshake time
timing.transfer_msfloatTime to first byte
proxy.driverstringProxy used: eris, zyte, scrapeowl, residential, browser, none
proxy.namestringHuman-readable proxy name
proxy.proxy_idintInternal proxy ID
retriesintProxies that failed before success (0 = first try)
cachedbooltrue if from cache (timing = 0)
cache_expiresstringCache expiry datetime
compressedbooltrue if gzip-compressed
customer.requests_todayintYour requests today
customer.success_todayintSuccessful today
customer.failures_todayintFailed today
customer.requests_this_monthintRequests this month

Failure Fields

FieldTypeDescription
successboolAlways false
error_codestringMachine-readable error code
errorstringHuman-readable message
http_codeintHTTP code from last proxy (0 if connection failed)
last_proxy_errorstringLast proxy's error message
last_proxy_driverstringLast proxy driver tried
retriesintTotal proxies tried
proxies_triedarrayDrivers attempted, in order
limitintRate limit threshold (429 only)
currentintCurrent count in window (429 only)
retry_afterintSeconds 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

CodeHTTPMeaning
MISSING_URL400No url parameter
MISSING_APIKEY401No apikey parameter
INVALID_APIKEY403Key not found or customer inactive
INVALID_URL400Malformed, not http/https, or private IP
INVALID_TIMEOUT400Exceeds customer max
INVALID_METHOD400Not GET or POST
INVALID_PROXY400Unknown proxy driver
RATE_LIMIT_CUSTOMER429Hourly limit exceeded
RATE_LIMIT_CALLER_IP429Caller IP limit exceeded
RATE_LIMIT_ORIGINATOR_IP429Originator IP limit exceeded
RATE_LIMIT_DAILY429Daily limit exceeded
ALL_PROXIES_FAILED502Every proxy failed
PROXY_ERROR502Proxy returned non-2xx
INTERNAL_ERROR500Server error

Proxy Chain

Default failover chain (configurable per-customer):

#DriverTypeBest For
1erisCF WorkerMost sites, free, fast
2zytePremium APIAnti-bot, CAPTCHAs
3scrapeowlPremium APIJS rendering, premium proxies
4residentialResidential IPsDatacenter-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.

PrioritySourceDescription
1cache=NPer-request override (days)
2Cache rulesAdmin URL patterns (e.g. %facebook.com% = 30d)
3Global default7 days

Rate Limits

LimitDefaultScope
Customer/hour1,000Per API key
Caller IP/hour2,000Per server IP
Originator IP/hour100Per end-user IP
Customer/dayUnlimitedDaily 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")