Everything you need to integrate NoBotsPls bot detection into your stack.
Send your API key with every request.
| Header | Required | Description |
|---|---|---|
X-API-Key |
Yes | Your API key (format afk_…). Get one free at /billing. |
Content-Type |
For POST endpoints | application/json |
GET /probe auto-detects headers and IP from the incoming request and returns a score + decision.
This is the easiest way to integrate — just proxy the request through your backend.
curl -s -H "X-API-Key: afk_xxx..." https://nobotspls.com/probe
Pass query parameters to override any field:
curl -sG "https://nobotspls.com/probe" \
--data-urlencode "useragent=Mozilla/5.0 (Macintosh) Safari/605.1.15" \
--data-urlencode "ip=203.0.xxx.xx" \
-H "X-API-Key: afk_xxx..."
| Parameter | Description |
|---|---|
useragent | User-Agent string |
sec_ch_ua | Client Hints UA string |
sec_ch_ua_mobile | Client Hints mobile flag |
acceptlanguage | Accept-Language header |
acceptencoding | Accept-Encoding header |
accept | Accept header |
ip | Client IP address |
xforwardedfor | X-Forwarded-For chain |
xrealip | X-Real-IP header |
via | Via header |
dnt | Do Not Track header |
pragma | Pragma header |
cache_control | Cache-Control header |
upgrade_insecure_requests | Upgrade-Insecure-Requests header |
extra_headers | JSON object of additional headers |
POST /score lets you send an explicit payload with all fields. Use this when you want full control over what gets scored.
{
"useragent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) Safari/605.1.15",
"sec_ch_ua": "\"Chromium\";v=\"123\", \"Not A;Brand\";v=\"99\"",
"ip": "127.0.0.1",
"xforwardedfor": "104.21.32.1",
"extra_headers": { "cf-connecting-ip": "104.21.32.1" }
}
{
"p_is_invalid": 0.94,
"p_is_converted": 0.02,
"decision": "block",
"rule_hits": ["ua_mismatch", "datacenter_ip"],
"request_id": "a1b2c3d4-...",
"ip_enrichment": { "is_datacenter": true, "is_vpn": false, "is_residential_proxy": false },
"token_attribution_invalid": [ { "token": "sec-ch-ua:missing", "importance": 0.31 }, ... ],
"neighbors_total_counts": { "inv": 8, "ok": 1 }
}
POST /batch_score accepts an array of score requests (max 500). Same schema as /score, returned as an array.
[
{ "useragent": "Mozilla/5.0 …", "ip": "198.51.100.120" },
{ "useragent": "curl/8.4.0", "ip": "203.0.113.42" }
]
Also available via the batch UI.
Using the /probe endpoint. For more integrations (WordPress, Nginx, Cloudflare) see the
GitHub repos.
# pip install requests
import requests
BASE = "https://nobotspls.com"
API_KEY = "afk_xxx..." # your API key
params = {
# "useragent": "Mozilla/5.0 ...",
# "ip": "x.x.x.x",
}
r = requests.get(f"{BASE}/probe",
headers={"X-API-Key": API_KEY},
params=params,
timeout=10)
print(r.status_code, r.json())
# FastAPI middleware example
import os
from fastapi import FastAPI, Request, HTTPException, Depends
import httpx
API_BASE = os.getenv("NOBOTSPLS_API_BASE", "https://nobotspls.com")
API_KEY = os.getenv("NOBOTSPLS_API_KEY", "afk_xxx...")
app = FastAPI()
async def bot_guard(request: Request):
h = request.headers
ip = ((request.client.host if request.client else "") or "").split(",")[0].strip()
params = {
"useragent": h.get("user-agent", ""),
"sec_ch_ua": h.get("sec-ch-ua", ""),
"acceptlanguage": h.get("accept-language", ""),
"acceptencoding": h.get("accept-encoding", ""),
"accept": h.get("accept", ""),
"ip": ip,
}
try:
async with httpx.AsyncClient(timeout=5) as client:
res = await client.get(
f"{API_BASE}/probe",
headers={"X-API-Key": API_KEY},
params=params,
)
except httpx.RequestError as e:
raise HTTPException(status_code=502, detail=f"Bot check upstream error: {e!s}")
if res.status_code >= 400:
raise HTTPException(status_code=502, detail=res.text)
verdict = (res.json() or {}).get("decision", "allow")
if verdict == "block":
raise HTTPException(status_code=403, detail="Blocked by bot detection")
@app.post("/signup", dependencies=[Depends(bot_guard)])
async def signup():
return {"ok": True}
@app.get("/pricing")
async def pricing(_: None = Depends(bot_guard)):
return {"plan": "free"}
// Node 18+ (native fetch)
const BASE = "https://nobotspls.com";
const API_KEY = "afk_xxx...";
const params = new URLSearchParams({
// useragent: "Mozilla/5.0 ...",
// ip: "x.x.x.x",
});
fetch(`${BASE}/probe?${params.toString()}`, {
method: "GET",
headers: { "X-API-Key": API_KEY }
})
.then(async (res) => console.log(res.status, await res.json()))
.catch(console.error);
export default {
async fetch(request, env) {
const h = request.headers;
const ip = (h.get("x-forwarded-for") || h.get("cf-connecting-ip") || "").split(",")[0].trim();
const params = new URLSearchParams({
useragent: h.get("user-agent") || "",
ip
});
const resp = await fetch("https://nobotspls.com/probe?" + params.toString(), {
method: "GET",
headers: { "X-API-Key": env.NOBOTSPLS_API_KEY }
});
return new Response(await resp.text(), { status: resp.status });
}
};
| Status | Meaning | Notes |
|---|---|---|
200 | OK | Score computed. |
400 | Bad Request | Invalid or missing fields. |
401 | Unauthorized | Missing/invalid X-API-Key. |
429 | Rate limited | Free: 10 req/min, 500/day. Pro: 3,000 req/min, 250k/day. |
5xx | Server error | Retry with backoff. |
Probabilities are calibrated to [0,1]. Use the provided decision field or apply your own threshold.
Full interactive API documentation is available at /docs (Swagger UI, auto-generated by FastAPI).