← Back to NoBotsPls

Getting Started

Everything you need to integrate NoBotsPls bot detection into your stack.

1. Authentication

An API key is optional. Without one you are on the free plan: 10 requests/min and 500/day, counted per client IP. Send your key to get your Starter or Pro limits.

HeaderRequiredDescription
X-API-Key No (free plan without) Your API key (format afk_…). Get one with Starter (£10/mo) or Pro (£100/mo).
Content-Type For POST endpoints application/json

2. Probe API — quickest path

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.

Basic call

# free plan, no key needed
curl -s https://nobotspls.com/probe

# with an API key (Starter / Pro limits)
curl -s -H "X-API-Key: afk_xxx..." https://nobotspls.com/probe

Override auto-detected values

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..."

Available override fields

ParameterDescription
useragentUser-Agent string
sec_ch_uaClient Hints UA string
sec_ch_ua_mobileClient Hints mobile flag
acceptlanguageAccept-Language header
acceptencodingAccept-Encoding header
acceptAccept header
ipClient IP address
xforwardedforX-Forwarded-For chain
xrealipX-Real-IP header
viaVia header
dntDo Not Track header
pragmaPragma header
cache_controlCache-Control header
upgrade_insecure_requestsUpgrade-Insecure-Requests header
extra_headersJSON object of additional headers

3. Score API — full control

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" }
}

Response shape

{
  "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 }
}

4. Batch scoring

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.

5. Code examples

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 });
  }
};

6. Errors & rate limits

StatusMeaningNotes
200OKScore computed.
400Bad RequestInvalid or missing fields.
401UnauthorizedInvalid or inactive X-API-Key. Omit the header to use the free plan.
429Rate limitedFree: 10 req/min, 500/day. Starter: 3,000 req/min, 25k/day. Pro: unlimited.
5xxServer errorRetry with backoff.

Probabilities are calibrated to [0,1]. Use the provided decision field or apply your own threshold.

7. OpenAPI spec

Full interactive API documentation is available at /docs (Swagger UI, auto-generated by FastAPI).

Privacy-first: we don't store your request payloads or IP addresses. Only aggregate counters for rate-limits/billing are kept — your data stays your data.