ZeroBot ZeroBot API Reference
Docs Dashboard Get Started

ZeroBot REST API

Programmatically manage authorized domains, custom rules, traffic logs, IP allow/blocklists, your shortener links, and your private hosting (buy, change domain, allowed countries, Under Attack mode, subdomains, cPanel login, visitor logs). Test any endpoint live from this page — paste your license key in the top bar, fill in the parameters, and click Send Request.

v3 Stable <100ms p50 Bearer Auth JSON in/out CORS Enabled Live Tester

Quick Start

  1. Grab your license key from your dashboard and paste it into the key field at the top right — it's saved locally and reused for every test on this page.
  2. Send it as Authorization: Bearer YOUR_LICENSE_KEY on every API request, or use the X-License-Key header / ?license= query parameter.
  3. All responses use the same envelope: { "status": "ok", "data": ..., "meta": ... }.
  4. Errors return { "status": "error", "message": "..." } with a matching HTTP status code.
  5. Lists are paginated via ?page=1&per_page=25.

Open AntiBot Check

GET /v3/openapi

Lightweight external API for IP intelligence and bot detection. Pass an IP and optionally a domain and User-Agent. Returns IP classification (VPN, Tor, Datacenter), ISP, geolocation, and a bot verdict — without requiring custom rules.

Parameters
ipREQUIRED string IP address to check.
domainREQUIRED string An authorized domain on your account. Required.
useragent string Visitor User-Agent string for bot pattern detection.
vpn integer 1 = block VPN (default), 0 = allow.
datacenter integer 1 = block datacenter (default), 0 = allow.
residential_proxy integer 1 = block residential-proxy IPs, 0 = allow (default).
Warning: residential-proxy IPs are shared with real users — blocking can affect legitimate visitors.
curl https://api.zerobot.info/v3/openapi -G --data-urlencode "ip=185.220.101.1" --data-urlencode "domain=https://example.com" \
     -H "Authorization: Bearer YOUR_LICENSE_KEY"
const res = await fetch("https://api.zerobot.info/v3/openapi?" +
  new URLSearchParams({
    ip:     "185.220.101.1",
    domain: "https://example.com"
  }), {
    headers: { "Authorization": "Bearer YOUR_LICENSE_KEY" }
  });
const data = await res.json();
r = requests.get("https://api.zerobot.info/v3/openapi",
  headers={"Authorization": "Bearer YOUR_LICENSE_KEY"},
  params={
    "ip":     "185.220.101.1",
    "domain": "https://example.com"
  })
Try it
GET /v3/openapi
— —
200 Example response
{
  "username": "aBcDeFgHiJk...",
  "asn":      "AS15169",
  "country_name": "United States",
  "isp":      "Google LLC",
  "is_bot":   false,
  "reason":   "ISP",
  "risk_score": 0,
  "vpn":      false,
  "tor":      false,
  "datacenter": false,
  "residential_proxy": false
}

Account stats & usage

GET /v3/account/stats

Returns the authenticated user's account info, plan, quota, lifetime/24h/7d traffic counters, and resource counts (domains, rules, whitelist, blacklist, links). The fastest way to power a "Status" widget in your own app.

Errors
401 Invalid or missing license key.
402 Plan expired — renew to continue.
403 Account suspended or unverified.
429 Quota exhausted.
curl https://api.zerobot.info/v3/account/stats \
     -H "Authorization: Bearer YOUR_LICENSE_KEY"
const res = await fetch("https://api.zerobot.info/v3/account/stats", {
  headers: { "Authorization": "Bearer YOUR_LICENSE_KEY" }
});
const { data } = await res.json();
r = requests.get("https://api.zerobot.info/v3/account/stats",
  headers={"Authorization": "Bearer YOUR_LICENSE_KEY"})
Try it
GET /v3/account/stats
— —
200 Example response
{
  "status": "ok",
  "data": {
    "account": {
      "username":  "yourname",
      "plan":      "Premium",
      "days_left": 287
    },
    "quota": { "remaining": "unlimited" },
    "lifetime": { "bots_blocked": 1001399, "humans_passed": 511864 },
    "last_24h": { "bots": 1240, "humans": 8765 },
    "resources": { "authorized_domains": 5, "custom_rules": 12 }
  }
}

List authorized domains

GET /v3/account/domains

Returns a paginated list of all domains authorized for your account.

Parameters
page integer Page number, default 1.
per_page integer Items per page (1-100), default 25.
curl https://api.zerobot.info/v3/account/domains \
     -H "Authorization: Bearer YOUR_LICENSE_KEY"
fetch("https://api.zerobot.info/v3/account/domains", {
  headers: { "Authorization": "Bearer YOUR_LICENSE_KEY" }
})
requests.get("https://api.zerobot.info/v3/account/domains",
  headers={"Authorization": "Bearer YOUR_LICENSE_KEY"})
Try it
GET /v3/account/domains
— —

Add an authorized domain

POST /v3/account/domains

Adds a domain to your authorized domains. Once added, you can attach rules and traffic will be tracked.

Parameters
domainREQUIRED string Domain to add (protocol stripped automatically).
Errors
400 Invalid domain format.
409 Domain already authorized.
curl -X POST https://api.zerobot.info/v3/account/domains \
     -H "Authorization: Bearer YOUR_LICENSE_KEY" \
     -H "Content-Type: application/json" \
     -d '{"domain":"example.com"}'
fetch("https://api.zerobot.info/v3/account/domains", {
  method: "POST",
  headers: {
    "Authorization": "Bearer YOUR_LICENSE_KEY",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({ domain: "example.com" })
})
requests.post("https://api.zerobot.info/v3/account/domains",
  headers={"Authorization": "Bearer YOUR_LICENSE_KEY"},
  json={"domain": "example.com"})
Try it
POST /v3/account/domains
— —

Remove an authorized domain

DELETE /v3/account/domains

Removes a domain from your authorized list. Any rules attached to it should be deleted separately.

Parameters
domainREQUIRED string Domain to remove.
curl -X DELETE https://api.zerobot.info/v3/account/domains \
     -H "Authorization: Bearer YOUR_LICENSE_KEY" \
     -H "Content-Type: application/json" \
     -d '{"domain":"example.com"}'
fetch("https://api.zerobot.info/v3/account/domains", {
  method: "DELETE",
  headers: { "Authorization": "Bearer YOUR_LICENSE_KEY", "Content-Type": "application/json" },
  body: JSON.stringify({ domain: "example.com" })
})
requests.delete("https://api.zerobot.info/v3/account/domains",
  headers={"Authorization": "Bearer YOUR_LICENSE_KEY"},
  json={"domain": "example.com"})
Try it
DELETE /v3/account/domains
— —

List custom rules

GET /v3/account/rules

Returns paginated custom rules. Each rule defines blocking behavior for a specific authorized domain — VPN/Tor/datacenter blocking, allowed countries/devices, risk threshold, and edge cache TTL.

curl https://api.zerobot.info/v3/account/rules \
     -H "Authorization: Bearer YOUR_LICENSE_KEY"
fetch("https://api.zerobot.info/v3/account/rules", { headers: { "Authorization": "Bearer YOUR_LICENSE_KEY" } })
requests.get("https://api.zerobot.info/v3/account/rules", headers={"Authorization": "Bearer YOUR_LICENSE_KEY"})
Try it
GET /v3/account/rules
— —

Create a custom rule

POST /v3/account/rules

Creates a custom rule for one of your authorized domains. The domain must already be in your authorized domains list. All fields are optional except link — only send the ones you want to set. Fields are organized into 4 groups: Basic Information, Blocking Controls, Detection & Grabber, and Analytics.

Parameters
linkREQUIRED string Basic Info → Antibot link / authorized domain (e.g. example.com or full URL). Domain must already be in your authorized domains list.
redirect_link string Basic Info → Where blocked visitors are sent. Accepts either a full URL (e.g. https://redirect.com) or a local file uploaded to your account (e.g. page.php, blocked.html).
block_vpn boolean Blocking → Block VPN traffic.
block_tor boolean Blocking → Block Tor traffic.
block_datacenter boolean Blocking → Block datacenter / hosting IPs.
block_residential_proxy boolean Blocking → Block residential-proxy exit IPs (daily-refreshed list).
Default false (allowed).
⚠ Warning: these are mostly real consumer/mobile IPs (mobile carriers, home ISPs) that are only temporarily used as proxies and rotate daily — enabling this can block some real visitors.
Leave off unless you accept that trade-off.
fingerprint_mode boolean Blocking → Enable advanced browser fingerprint protection.
wildcard boolean Blocking → Apply rule to all subdomains of the authorized domain.
allowed_countries string Devices & Countries → Comma-separated ISO codes (e.g. US,GB,DE). Empty = allow all countries.
allowed_devices string Devices & Countries → Comma-separated device types: desktop,mobile,tablet. Empty = allow all.
check_red_page boolean Detection & Grabber → Enable Red Page checking — get a Telegram alert if your redirect link gets flagged.
autograbber boolean Detection & Grabber → Enable autograbber — automatically capture URL parameters (e.g. ?email=...) and append them to the redirect.
autograbber_code string Detection & Grabber → Separator character used to append captured values. Default is #.
views_file_name string Analytics → Name of the views/visitors file on your server (e.g. views.php).
captcha_activation boolean Captcha → Enable Cloudflare Turnstile captcha challenge.
captcha_key string Captcha → Cloudflare Turnstile site key (e.g. 0x4AAAAAAA...).
captcha_domain string Captcha → Captcha domain name (optional, e.g. example.com).
captcha_logo string Captcha → URL to your logo for the captcha page (optional).
telegram_token string Telegram → Telegram bot token for visit notifications.
telegram_chat_id string Telegram → Telegram chat ID to send notifications to.
Errors
403 Domain not in your authorized domains.
409 A rule already exists for this domain.
curl -X POST https://api.zerobot.info/v3/account/rules \
     -H "Authorization: Bearer YOUR_LICENSE_KEY" \
     -H "Content-Type: application/json" \
     -d '{
       "link": "example.com",
       "redirect_link": "https://redirect.com",
       "block_vpn": true,
       "block_tor": true,
       "block_datacenter": true,
       "fingerprint_mode": true,
       "allowed_countries": "US,GB,DE",
       "allowed_devices": "desktop,mobile",
       "captcha_activation": true,
       "captcha_key": "0x4AAAAAAA..."
     }'
fetch("https://api.zerobot.info/v3/account/rules", {
  method: "POST",
  headers: { "Authorization": "Bearer YOUR_LICENSE_KEY", "Content-Type": "application/json" },
  body: JSON.stringify({
    link:               "example.com",
    redirect_link:      "https://redirect.com",
    block_vpn:          true,
    block_tor:          true,
    block_datacenter:   true,
    fingerprint_mode:   true,
    allowed_countries:  "US,GB,DE",
    allowed_devices:    "desktop,mobile",
    captcha_activation: true,
    captcha_key:        "0x4AAAAAAA..."
  })
})
requests.post("https://api.zerobot.info/v3/account/rules",
  headers={"Authorization": "Bearer YOUR_LICENSE_KEY"},
  json={
    "link":               "example.com",
    "redirect_link":      "https://redirect.com",
    "block_vpn":          True,
    "block_tor":          True,
    "block_datacenter":   True,
    "fingerprint_mode":   True,
    "allowed_countries":  "US,GB,DE",
    "captcha_activation": True,
    "captcha_key":        "0x4AAAAAAA..."
  })
Try it
POST /v3/account/rules
— —

Update a custom rule

PUT /v3/account/rules

Updates one or more fields of an existing rule. Only the fields you send are modified — others stay unchanged.

Parameters
idREQUIRED integer ID of the rule to update.
block_vpn boolean Any field from POST /v3/account/rules can be updated — send only the ones you want to change.
curl -X PUT https://api.zerobot.info/v3/account/rules \
     -H "Authorization: Bearer YOUR_LICENSE_KEY" \
     -H "Content-Type: application/json" \
     -d '{"id":42,"block_vpn":false,"allowed_countries":"US,CA"}'
fetch("https://api.zerobot.info/v3/account/rules", {
  method: "PUT",
  headers: { "Authorization": "Bearer YOUR_LICENSE_KEY", "Content-Type": "application/json" },
  body: JSON.stringify({ id: 42, block_vpn: false, allowed_countries: "US,CA" })
})
requests.put("https://api.zerobot.info/v3/account/rules",
  headers={"Authorization": "Bearer YOUR_LICENSE_KEY"},
  json={"id": 42, "block_vpn": False, "allowed_countries": "US,CA"})
Try it
PUT /v3/account/rules
— —

Delete a custom rule

DELETE /v3/account/rules

Permanently deletes a custom rule. The associated authorized domain stays intact.

Parameters
idREQUIRED integer ID of the rule to delete.
curl -X DELETE https://api.zerobot.info/v3/account/rules \
     -H "Authorization: Bearer YOUR_LICENSE_KEY" \
     -H "Content-Type: application/json" \
     -d '{"id":42}'
fetch("https://api.zerobot.info/v3/account/rules", {
  method: "DELETE",
  headers: { "Authorization": "Bearer YOUR_LICENSE_KEY", "Content-Type": "application/json" },
  body: JSON.stringify({ id: 42 })
})
requests.delete("https://api.zerobot.info/v3/account/rules",
  headers={"Authorization": "Bearer YOUR_LICENSE_KEY"},
  json={"id": 42})
Try it
DELETE /v3/account/rules
— —

Query traffic logs

GET /v3/account/traffic

Returns paginated traffic events seen by your domains. Supports filters by date range, country, bot status, and domain. Pass summary=1 to also get aggregate bot/human counts in meta.summary.

Parameters
page integer Page number.
per_page integer Items per page (max 100).
from date Start date YYYY-MM-DD.
to date End date YYYY-MM-DD.
is_bot boolean Filter to bots (true) or humans (false).
country string ISO country code.
domain string Filter by domain.
summary integer Set to 1 for aggregate counts.
curl https://api.zerobot.info/v3/account/traffic -G --data-urlencode "from=2026-04-01" --data-urlencode "to=2026-04-07" --data-urlencode "summary=1" \
     -H "Authorization: Bearer YOUR_LICENSE_KEY"
const params = new URLSearchParams({ from: "2026-04-01", to: "2026-04-07", summary: 1 });
fetch("https://api.zerobot.info/v3/account/traffic?" + params, {
  headers: { "Authorization": "Bearer YOUR_LICENSE_KEY" }
})
requests.get("https://api.zerobot.info/v3/account/traffic",
  params={"from":"2026-04-01", "summary": 1},
  headers={"Authorization": "Bearer YOUR_LICENSE_KEY"})
Try it
GET /v3/account/traffic
— —

List trusted IPs

GET /v3/account/whitelist

Returns paginated list of IPs in your whitelist. Whitelisted IPs always pass through, even if they would otherwise be blocked.

curl https://api.zerobot.info/v3/account/whitelist \
     -H "Authorization: Bearer YOUR_LICENSE_KEY"
fetch("https://api.zerobot.info/v3/account/whitelist", { headers: { "Authorization": "Bearer YOUR_LICENSE_KEY" } })
requests.get("https://api.zerobot.info/v3/account/whitelist", headers={"Authorization": "Bearer YOUR_LICENSE_KEY"})
Try it
GET /v3/account/whitelist
— —

Add to whitelist (IP / Range / ASN)

POST /v3/account/whitelist

Whitelist supports three entry types: IP, CIDR Range, and ASN. Pass any combination of single values (ip, range, asn) or arrays (ips, ranges, asns). Up to 1000 entries per call. Duplicates skipped, invalid entries reported.

Parameters
ip string Single IPv4/IPv6 (e.g. 8.8.8.8).
ips array Array of IPs (max 1000).
range string Single CIDR range (e.g. 8.8.8.0/24).
ranges array Array of CIDR ranges (max 1000).
asn string Single ASN (e.g. AS15169 or 15169).
asns array Array of ASNs (max 1000).
curl -X POST https://api.zerobot.info/v3/account/whitelist \
     -H "Authorization: Bearer YOUR_LICENSE_KEY" \
     -H "Content-Type: application/json" \
     -d '{"ips":["1.2.3.4","5.6.7.8"]}'
fetch("https://api.zerobot.info/v3/account/whitelist", {
  method: "POST",
  headers: { "Authorization": "Bearer YOUR_LICENSE_KEY", "Content-Type": "application/json" },
  body: JSON.stringify({ ips: ["1.2.3.4"] })
})
requests.post("https://api.zerobot.info/v3/account/whitelist",
  headers={"Authorization": "Bearer YOUR_LICENSE_KEY"},
  json={"ips": ["1.2.3.4"]})
Try it
POST /v3/account/whitelist
— —

Remove from whitelist (IP / Range / ASN)

DELETE /v3/account/whitelist

Remove entries by their type. Pass ip/ips, range/ranges, or asn/asns — same shape as POST.

curl -X DELETE https://api.zerobot.info/v3/account/whitelist \
     -H "Authorization: Bearer YOUR_LICENSE_KEY" \
     -H "Content-Type: application/json" \
     -d '{"ip":"1.2.3.4"}'
fetch("https://api.zerobot.info/v3/account/whitelist", {
  method: "DELETE",
  headers: { "Authorization": "Bearer YOUR_LICENSE_KEY", "Content-Type": "application/json" },
  body: JSON.stringify({ ip: "1.2.3.4" })
})
requests.delete("https://api.zerobot.info/v3/account/whitelist",
  headers={"Authorization": "Bearer YOUR_LICENSE_KEY"},
  json={"ip": "1.2.3.4"})
Try it
DELETE /v3/account/whitelist
— —

List blocked IPs

GET /v3/account/blacklist

Returns paginated list of IPs in your blacklist. Blacklisted IPs are always blocked regardless of other rules.

curl https://api.zerobot.info/v3/account/blacklist \
     -H "Authorization: Bearer YOUR_LICENSE_KEY"
fetch("https://api.zerobot.info/v3/account/blacklist", { headers: { "Authorization": "Bearer YOUR_LICENSE_KEY" } })
requests.get("https://api.zerobot.info/v3/account/blacklist", headers={"Authorization": "Bearer YOUR_LICENSE_KEY"})
Try it
GET /v3/account/blacklist
— —

Add to blacklist (IP / Range / ASN)

POST /v3/account/blacklist

Blacklist supports three entry types: IP, CIDR Range, and ASN. Pass any combination of single values (ip, range, asn) or arrays (ips, ranges, asns). Up to 1000 entries per call.

Parameters
ip string Single IPv4/IPv6 to block.
ips array Array of IPs (max 1000).
range string Single CIDR range (e.g. 185.220.101.0/24).
ranges array Array of CIDR ranges (max 1000).
asn string Single ASN to block (e.g. AS14061).
asns array Array of ASNs (max 1000).
curl -X POST https://api.zerobot.info/v3/account/blacklist \
     -H "Authorization: Bearer YOUR_LICENSE_KEY" \
     -H "Content-Type: application/json" \
     -d '{"ips":["185.220.101.1"]}'
fetch("https://api.zerobot.info/v3/account/blacklist", {
  method: "POST",
  headers: { "Authorization": "Bearer YOUR_LICENSE_KEY", "Content-Type": "application/json" },
  body: JSON.stringify({ ips: ["185.220.101.1"] })
})
requests.post("https://api.zerobot.info/v3/account/blacklist",
  headers={"Authorization": "Bearer YOUR_LICENSE_KEY"},
  json={"ips": ["185.220.101.1"]})
Try it
POST /v3/account/blacklist
— —

Remove from blacklist (IP / Range / ASN)

DELETE /v3/account/blacklist

Remove blacklist entries by their type. Pass ip/ips, range/ranges, or asn/asns.

curl -X DELETE https://api.zerobot.info/v3/account/blacklist \
     -H "Authorization: Bearer YOUR_LICENSE_KEY" \
     -H "Content-Type: application/json" \
     -d '{"ip":"185.220.101.1"}'
fetch("https://api.zerobot.info/v3/account/blacklist", {
  method: "DELETE",
  headers: { "Authorization": "Bearer YOUR_LICENSE_KEY", "Content-Type": "application/json" },
  body: JSON.stringify({ ip: "185.220.101.1" })
})
requests.delete("https://api.zerobot.info/v3/account/blacklist",
  headers={"Authorization": "Bearer YOUR_LICENSE_KEY"},
  json={"ip": "185.220.101.1"})
Try it
DELETE /v3/account/blacklist
— —

List private hostings

GET /v3/account/hosting

Returns your private hosting accounts with status, expiry, server, cPanel URL and user, nameservers, allowed countries, traffic counters and subdomain usage. meta.purchase tells you whether a new purchase would succeed right now: service switch, price, your balance and the free slots on the pool you would be placed on (placement.mode is private when your account was granted a private server, otherwise public).

Hosting endpoints are gated on the hosting product itself — not on your dashboard plan — and do not consume anti-bot quota. Every hosting is addressed by its primary domain (or hosting_id).

Parameters
page integer Page number, default 1.
per_page integer Items per page (1-100), default 25.
Errors
401 Invalid or missing license key.
403 Account suspended or unverified.
curl https://api.zerobot.info/v3/account/hosting \
     -H "Authorization: Bearer YOUR_LICENSE_KEY"
fetch("https://api.zerobot.info/v3/account/hosting", { headers: { "Authorization": "Bearer YOUR_LICENSE_KEY" } })
requests.get("https://api.zerobot.info/v3/account/hosting", headers={"Authorization": "Bearer YOUR_LICENSE_KEY"})
Try it
GET /v3/account/hosting
— —
200 Example response
{
  "status": "ok",
  "data": [{
    "id": 239, "domain": "example.com", "status": "Active",
    "end_date": "2026-10-02 15:44:48", "days_left": 20,
    "server": { "id": 6, "name": "Private 4" },
    "cpanel": { "url": "https://private4.example-host.com:2083/", "user": "exampl6549" },
    "nameservers": ["lauryn.ns.cloudflare.com", "sullivan.ns.cloudflare.com"],
    "cloudflare_linked": true,
    "allowed_countries": "all",
    "block_residential_proxy": false,
    "traffic": { "humans": 56, "bots": 1917 },
    "subdomains": { "count": 1, "max": 5 }
  }],
  "meta": {
    "page": 1, "per_page": 25, "total": 1, "total_pages": 1,
    "purchase": {
      "service_enabled": true, "price": 100, "currency": "USD",
      "balance": 250, "can_buy": true, "reason": null,
      "placement": { "mode": "public", "free_slots": 1 }
    }
  }
}

Buy private hosting

POST /v3/account/hosting

Creates a new private hosting account for domain and charges the hosting price from your account balance.

Placement rule. The API first checks whether your account is authorized on a private server. If it is, the hosting is created on your private server only — public servers are never used, not even when your private server is full (you get 409 instead). If your account has no private authorization, the hosting goes to the public pool. GET /v3/account/hosting → meta.purchase.placement shows which pool applies to you and its free slots.

Placement, balance and domain checks all run before anything is charged. The deduction is atomic; if provisioning fails afterwards you are refunded in full. The response carries the cPanel credentials and the nameservers to point the domain to.

⚠ Real purchase. The tester below charges your balance and provisions a real account. Check meta.purchase.can_buy first.

Parameters
domainREQUIRED string Domain to host, e.g. example.com. Brand-impersonation and fraud keywords are rejected.
server_id integer Optional. Pick one of your private servers (ids from meta.purchase.placement.servers). A public server id is refused for accounts authorized on a private server; accounts without private authorization cannot pick a server at all. Leave empty for automatic placement.
Errors
400 Invalid domain format.
402 Insufficient balance — nothing was charged.
403 Server not allowed for your account (private server you are not authorized on, or a public server while you are assigned to a private one).
409 No free slot: your private server is full, the public pool is full, or the requested server is full / unavailable.
422 Domain blocked by the brand / fraud check.
502 Provisioning failed on the server — the charge was refunded.
503 Hosting service is currently disabled.
curl -X POST https://api.zerobot.info/v3/account/hosting \
     -H "Authorization: Bearer YOUR_LICENSE_KEY" \
     -H "Content-Type: application/json" \
     -d '{"domain":"example.com"}'
fetch("https://api.zerobot.info/v3/account/hosting", {
  method: "POST",
  headers: { "Authorization": "Bearer YOUR_LICENSE_KEY", "Content-Type": "application/json" },
  body: JSON.stringify({ domain: "example.com" })
})
requests.post("https://api.zerobot.info/v3/account/hosting",
  headers={"Authorization": "Bearer YOUR_LICENSE_KEY"},
  json={"domain": "example.com"})
Try it
POST /v3/account/hosting
— —
200 Example response
{
  "status": "ok",
  "data": {
    "hosting": { "id": 240, "domain": "example.com", "status": "Active", "days_left": 30, … },
    "charged": 100, "currency": "USD",
    "cpanel": { "url": "https://private4.example-host.com:2083/", "user": "exampl1234", "password": "••••••••" },
    "nameservers": ["lauryn.ns.cloudflare.com", "sullivan.ns.cloudflare.com"],
    "next_step": "Point the domain to the nameservers above. The site goes live once DNS propagates."
  }
}

cPanel login for a domain

GET /v3/account/hosting/cpanel

Returns the cPanel URL, username and password of the hosting behind domain, plus login_url: a one-time link that opens cPanel already logged in. It is valid for a few minutes and works once; it is null when the server could not issue a session (fall back to the credentials). Treat the whole response as a secret.

Parameters
domainREQUIRED string Primary domain of the hosting. hosting_id is accepted instead.
Errors
404 No hosting with that domain on your account.
curl https://api.zerobot.info/v3/account/hosting/cpanel -G --data-urlencode "domain=example.com" \
     -H "Authorization: Bearer YOUR_LICENSE_KEY"
fetch("https://api.zerobot.info/v3/account/hosting/cpanel?domain=example.com", { headers: { "Authorization": "Bearer YOUR_LICENSE_KEY" } })
requests.get("https://api.zerobot.info/v3/account/hosting/cpanel", params={"domain": "example.com"}, headers={"Authorization": "Bearer YOUR_LICENSE_KEY"})
Try it
GET /v3/account/hosting/cpanel
— —
200 Example response
{
  "status": "ok",
  "data": {
    "hosting_id": 239, "domain": "example.com", "status": "Active",
    "cpanel": { "url": "https://private4.example-host.com:2083/", "user": "exampl6549", "password": "••••••••" },
    "login_url": "https://private4.example-host.com:2083/cpsess0123456789/login/?session=exampl6549%3a…",
    "login_url_note": "One-time auto-login link: open it right away, it expires within minutes and works once."
  }
}

Change hosting domain

PUT /v3/account/hosting/domain

Moves the hosting from domain to new_domain. Subdomains of the old domain are removed, the cPanel primary domain is changed, the old Cloudflare zone is replaced by a new one (A records + protection route) and the new nameservers are returned. The new name passes the same brand / fraud check as a purchase. A suspended hosting cannot be changed. POST is accepted as an alias of PUT.

Parameters
domainREQUIRED string Current primary domain (or hosting_id).
new_domainREQUIRED string New primary domain.
Errors
400 Invalid domain format.
404 No hosting with that domain on your account.
409 Hosting is suspended.
422 New domain blocked by the brand / fraud check.
502 cPanel or Cloudflare rejected part of the change — the new domain is recorded; contact support.
curl -X PUT https://api.zerobot.info/v3/account/hosting/domain \
     -H "Authorization: Bearer YOUR_LICENSE_KEY" \
     -H "Content-Type: application/json" \
     -d '{"domain":"old.com","new_domain":"new.com"}'
fetch("https://api.zerobot.info/v3/account/hosting/domain", {
  method: "PUT",
  headers: { "Authorization": "Bearer YOUR_LICENSE_KEY", "Content-Type": "application/json" },
  body: JSON.stringify({ domain: "old.com", new_domain: "new.com" })
})
requests.put("https://api.zerobot.info/v3/account/hosting/domain",
  headers={"Authorization": "Bearer YOUR_LICENSE_KEY"},
  json={"domain": "old.com", "new_domain": "new.com"})
Try it
PUT /v3/account/hosting/domain
— —
200 Example response
{
  "status": "ok",
  "data": {
    "hosting": { "id": 239, "domain": "new.com", "status": "Active", … },
    "old_domain": "old.com", "new_domain": "new.com", "changed": true,
    "nameservers": ["lauryn.ns.cloudflare.com", "sullivan.ns.cloudflare.com"],
    "message": "Domain changed to new.com",
    "note": "Subdomains of the old domain were removed. Point the new domain to the nameservers above."
  }
}

Allowed countries

PUT /v3/account/hosting/countries

Restricts who can reach the hosting by country. Visitors from any other country get the country denied decision and show as denied in the visitor log. Applies to the primary domain and every subdomain. Send "all" (or an empty value) to lift the restriction. Visitors whose country cannot be resolved are never denied by this rule. POST is accepted as an alias of PUT.

Parameters
domainREQUIRED string Primary domain (or hosting_id).
countriesREQUIRED string | array "all", a comma-separated list ("FR,DE,US") or a JSON array of ISO 3166-1 alpha-2 codes.
Errors
400 Unknown country code.
404 No hosting with that domain on your account.
curl -X PUT https://api.zerobot.info/v3/account/hosting/countries \
     -H "Authorization: Bearer YOUR_LICENSE_KEY" \
     -H "Content-Type: application/json" \
     -d '{"domain":"example.com","countries":"FR,DE,US"}'
fetch("https://api.zerobot.info/v3/account/hosting/countries", {
  method: "PUT",
  headers: { "Authorization": "Bearer YOUR_LICENSE_KEY", "Content-Type": "application/json" },
  body: JSON.stringify({ domain: "example.com", countries: ["FR", "DE", "US"] })
})
requests.put("https://api.zerobot.info/v3/account/hosting/countries",
  headers={"Authorization": "Bearer YOUR_LICENSE_KEY"},
  json={"domain": "example.com", "countries": "FR,DE,US"})
Try it
PUT /v3/account/hosting/countries
— —
200 Example response
{
  "status": "ok",
  "data": {
    "hosting_id": 239, "domain": "example.com",
    "allowed_countries": ["FR", "DE", "US"],
    "applies_to": "the primary domain and all of its subdomains"
  }
}

Under Attack status

GET /v3/account/hosting/under-attack

Current Cloudflare security level of the hosting's zone. under_attack is true while "I'm Under Attack" mode is on (every visitor gets a JavaScript challenge before reaching the site).

Parameters
domainREQUIRED string Primary domain (or hosting_id).
Errors
404 No hosting with that domain on your account.
409 No Cloudflare zone or token linked to this hosting.
502 Cloudflare did not answer.
curl https://api.zerobot.info/v3/account/hosting/under-attack -G --data-urlencode "domain=example.com" \
     -H "Authorization: Bearer YOUR_LICENSE_KEY"
fetch("https://api.zerobot.info/v3/account/hosting/under-attack?domain=example.com", { headers: { "Authorization": "Bearer YOUR_LICENSE_KEY" } })
requests.get("https://api.zerobot.info/v3/account/hosting/under-attack", params={"domain": "example.com"}, headers={"Authorization": "Bearer YOUR_LICENSE_KEY"})
Try it
GET /v3/account/hosting/under-attack
— —
200 Example response
{
  "status": "ok",
  "data": { "hosting_id": 239, "domain": "example.com", "under_attack": false, "security_level": "medium" }
}

Under Attack on / off

PUT /v3/account/hosting/under-attack

Switches Cloudflare "I'm Under Attack" mode for the hosting's zone. enabled: true sets the security level to under_attack; false returns it to medium (the same two levels the dashboard switch uses). Idempotent — changed is false when the zone was already in the requested state. POST is accepted as an alias of PUT.

Parameters
domainREQUIRED string Primary domain (or hosting_id).
enabledREQUIRED boolean true to turn Under Attack mode on, false to turn it off.
Errors
400 enabled must be true or false.
404 No hosting with that domain on your account.
409 No Cloudflare zone or token linked to this hosting.
502 Cloudflare rejected the change.
curl -X PUT https://api.zerobot.info/v3/account/hosting/under-attack \
     -H "Authorization: Bearer YOUR_LICENSE_KEY" \
     -H "Content-Type: application/json" \
     -d '{"domain":"example.com","enabled":true}'
fetch("https://api.zerobot.info/v3/account/hosting/under-attack", {
  method: "PUT",
  headers: { "Authorization": "Bearer YOUR_LICENSE_KEY", "Content-Type": "application/json" },
  body: JSON.stringify({ domain: "example.com", enabled: true })
})
requests.put("https://api.zerobot.info/v3/account/hosting/under-attack",
  headers={"Authorization": "Bearer YOUR_LICENSE_KEY"},
  json={"domain": "example.com", "enabled": True})
Try it
PUT /v3/account/hosting/under-attack
— —
200 Example response
{
  "status": "ok",
  "data": { "hosting_id": 239, "domain": "example.com", "under_attack": true, "security_level": "under_attack", "changed": true }
}

Residential proxy status

GET /v3/account/hosting/residential-proxy

Whether residential-proxy blocking is enabled for this hosting domain. Applies to the primary domain and all its subdomains. Allowed by default.

Parameters
domainREQUIRED string Primary domain (or hosting_id).
Errors
404 No hosting with that domain on your account.
curl https://api.zerobot.info/v3/account/hosting/residential-proxy -G --data-urlencode "domain=example.com" \
     -H "Authorization: Bearer YOUR_LICENSE_KEY"
fetch("https://api.zerobot.info/v3/account/hosting/residential-proxy?domain=example.com", { headers: { "Authorization": "Bearer YOUR_LICENSE_KEY" } })
requests.get("https://api.zerobot.info/v3/account/hosting/residential-proxy", params={"domain": "example.com"}, headers={"Authorization": "Bearer YOUR_LICENSE_KEY"})
Try it
GET /v3/account/hosting/residential-proxy
— —
200 Example response
{
  "status": "ok",
  "data": { "hosting_id": 239, "domain": "example.com", "block_residential_proxy": false }
}

Residential proxy block / allow

PUT /v3/account/hosting/residential-proxy

Block or allow residential-proxy exit IPs for this hosting domain (and its subdomains).
block: true blocks; false allows (default).
POST is accepted as an alias of PUT.
⚠ Warning: residential-proxy IPs are mostly real consumer/mobile addresses (home ISPs, mobile carriers) that rotate daily and are only temporarily used as proxies — enabling blocking can affect some legitimate visitors.
Leave off unless you accept that trade-off.

Parameters
domainREQUIRED string Primary domain (or hosting_id).
blockREQUIRED boolean true to block residential-proxy traffic, false to allow it.
Errors
400 block must be true or false.
404 No hosting with that domain on your account.
curl -X PUT https://api.zerobot.info/v3/account/hosting/residential-proxy \
     -H "Authorization: Bearer YOUR_LICENSE_KEY" \
     -H "Content-Type: application/json" \
     -d '{"domain":"example.com","block":true}'
fetch("https://api.zerobot.info/v3/account/hosting/residential-proxy", {
  method: "PUT",
  headers: { "Authorization": "Bearer YOUR_LICENSE_KEY", "Content-Type": "application/json" },
  body: JSON.stringify({ domain: "example.com", block: true })
})
requests.put("https://api.zerobot.info/v3/account/hosting/residential-proxy",
  headers={"Authorization": "Bearer YOUR_LICENSE_KEY"},
  json={"domain": "example.com", "block": True})
Try it
PUT /v3/account/hosting/residential-proxy
— —
200 Example response
{
  "status": "ok",
  "data": { "hosting_id": 239, "domain": "example.com", "block_residential_proxy": true }
}

List subdomains

GET /v3/account/hosting/subdomains

Subdomains created under the hosting, with the per-hosting limit in meta.max.

Parameters
domainREQUIRED string Primary domain (or hosting_id).
Errors
404 No hosting with that domain on your account.
curl https://api.zerobot.info/v3/account/hosting/subdomains -G --data-urlencode "domain=example.com" \
     -H "Authorization: Bearer YOUR_LICENSE_KEY"
fetch("https://api.zerobot.info/v3/account/hosting/subdomains?domain=example.com", { headers: { "Authorization": "Bearer YOUR_LICENSE_KEY" } })
requests.get("https://api.zerobot.info/v3/account/hosting/subdomains", params={"domain": "example.com"}, headers={"Authorization": "Bearer YOUR_LICENSE_KEY"})
Try it
GET /v3/account/hosting/subdomains
— —
200 Example response
{
  "status": "ok",
  "data": [
    { "id": 838, "subdomain": "shop", "full_domain": "shop.example.com", "root_domain": "example.com", "created_at": "2026-09-02 15:45:28" }
  ],
  "meta": { "hosting_id": 239, "domain": "example.com", "count": 1, "max": 5 }
}

Create subdomain

POST /v3/account/hosting/subdomains

Creates a subdomain on the hosting: cPanel subdomain, proxied Cloudflare DNS record, protection route and a placeholder index page. The name passes the same brand / fraud check as a domain. The web server needs 20-30 seconds to load the new virtual host, during which it may answer 403.

Parameters
domainREQUIRED string Primary domain (or hosting_id).
subdomainREQUIRED string Prefix (shop) or full name (shop.example.com). Letters, digits and hyphens.
Errors
400 Invalid prefix.
404 No hosting with that domain on your account.
409 Subdomain already exists, limit reached, or hosting not active.
422 Name blocked by the brand / fraud check.
502 cPanel or Cloudflare error (nothing left behind).
curl -X POST https://api.zerobot.info/v3/account/hosting/subdomains \
     -H "Authorization: Bearer YOUR_LICENSE_KEY" \
     -H "Content-Type: application/json" \
     -d '{"domain":"example.com","subdomain":"shop"}'
fetch("https://api.zerobot.info/v3/account/hosting/subdomains", {
  method: "POST",
  headers: { "Authorization": "Bearer YOUR_LICENSE_KEY", "Content-Type": "application/json" },
  body: JSON.stringify({ domain: "example.com", subdomain: "shop" })
})
requests.post("https://api.zerobot.info/v3/account/hosting/subdomains",
  headers={"Authorization": "Bearer YOUR_LICENSE_KEY"},
  json={"domain": "example.com", "subdomain": "shop"})
Try it
POST /v3/account/hosting/subdomains
— —
200 Example response
{
  "status": "ok",
  "data": {
    "subdomain": { "id": 889, "subdomain": "shop", "full_domain": "shop.example.com", "root_domain": "example.com", "created_at": "2026-09-12 13:43:32" },
    "note": "DNS and protection are active. The web server needs ~20-30 seconds to load the new virtual host, during which it may answer 403."
  }
}

Delete subdomain

DELETE /v3/account/hosting/subdomains

Removes the subdomain from cPanel and Cloudflare (DNS record + protection route) together with its visitor log.

Parameters
domainREQUIRED string Primary domain (or hosting_id).
subdomainREQUIRED string Prefix or full name. subdomain_id is accepted instead.
Errors
404 Hosting or subdomain not found.
502 cPanel or Cloudflare error.
curl -X DELETE https://api.zerobot.info/v3/account/hosting/subdomains \
     -H "Authorization: Bearer YOUR_LICENSE_KEY" \
     -H "Content-Type: application/json" \
     -d '{"domain":"example.com","subdomain":"shop"}'
fetch("https://api.zerobot.info/v3/account/hosting/subdomains", {
  method: "DELETE",
  headers: { "Authorization": "Bearer YOUR_LICENSE_KEY", "Content-Type": "application/json" },
  body: JSON.stringify({ domain: "example.com", subdomain: "shop" })
})
requests.delete("https://api.zerobot.info/v3/account/hosting/subdomains",
  headers={"Authorization": "Bearer YOUR_LICENSE_KEY"},
  json={"domain": "example.com", "subdomain": "shop"})
Try it
DELETE /v3/account/hosting/subdomains
— —
200 Example response
{
  "status": "ok",
  "data": { "full_domain": "shop.example.com", "deleted": true }
}

Hosting visitors

GET /v3/account/hosting/visitors

Visitor log of a hosting domain or one of its subdomains, newest first, with the anti-bot decision for each visit (human, bot, country_denied). By default one row per IP (its latest visit) — pass unique=0 for every visit. meta.summary carries the all-time counters for the domain.

Parameters
domainREQUIRED string Primary domain or subdomain (shop.example.com).
status string Filter: human, bot, denied (country) or blocked (bot + denied).
country string Filter by ISO country code, e.g. US.
unique boolean 1 (default) one row per IP; 0 every visit.
from date Start date YYYY-MM-DD.
to date End date YYYY-MM-DD.
page integer Page number.
per_page integer Items per page (max 100).
Errors
400 Invalid status or country.
404 Domain not found on your account.
curl https://api.zerobot.info/v3/account/hosting/visitors -G --data-urlencode "domain=example.com" --data-urlencode "status=bot" --data-urlencode "per_page=50" \
     -H "Authorization: Bearer YOUR_LICENSE_KEY"
const params = new URLSearchParams({ domain: "example.com", status: "bot", per_page: 50 });
fetch("https://api.zerobot.info/v3/account/hosting/visitors?" + params, { headers: { "Authorization": "Bearer YOUR_LICENSE_KEY" } })
requests.get("https://api.zerobot.info/v3/account/hosting/visitors",
  params={"domain": "example.com", "status": "bot", "per_page": 50},
  headers={"Authorization": "Bearer YOUR_LICENSE_KEY"})
Try it
GET /v3/account/hosting/visitors
— —
200 Example response
{
  "status": "ok",
  "data": [{
    "ip": "66.29.156.92", "decision": "bot", "allowed": false,
    "country": { "code": "US", "name": "United States" },
    "isp": "Namecheap Inc.", "asn": "AS22612", "hostname": "nc-ph-4031.web-hosting.com",
    "device": "Unknown", "browser": "Unknown",
    "user_agent": "Mozilla/5.0 (compatible; ResearchScanBot/0.2)",
    "path": "/", "visits": 1, "last_seen": "2026-09-12 13:29:28"
  }],
  "meta": {
    "page": 1, "per_page": 25, "total": 290, "total_pages": 12,
    "domain": "example.com", "unique": true,
    "summary": { "total_visits": 1945, "unique_visitors": 290, "humans": 35, "bots": 1910, "country_denied": 0 }
  }
}