PureGuard API Documentation

PureGuard is a server-side bot detection engine built for performance media buyers. It analyzes every click through 13+ configurable detection layers and returns a verdict in under 15ms. This documentation covers the REST API, detection engine internals, and integration guides for every major traffic source.

Quick start: Sign up, grab your API key from the Dashboard, and send your first bot check request in under 60 seconds.

1. Create an account

Register for free to get instant access. Your starter plan includes 100,000 bot checks per month at no cost. No credit card required.

2. Get your API key

After logging in, navigate to Settings to find your API key. The key is a unique string prefixed with pg_ that authenticates all your requests.

3. Make your first request

cURL
curl -X POST https://pureguard.io/api/v1/check \
  -H "X-API-Key: pg_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "ip": "1.2.3.4",
    "ua": "Mozilla/5.0 Chrome/131.0",
    "url": "https://example.com/offer"
  }'

The response tells you whether to accept or block the visitor:

Response
{
  "verdict": "ACCEPT",
  "trust": 7.3,
  "confidence": 92,
  "signals": ["sec_fetch:+1.0", "residential:+0.3", "real_device:+0.5"],
  "ms": 2.3
}

Authentication

All API requests (except GET /api/v1/status) require authentication via the X-API-Key header. Your API key is available on the Settings page of your dashboard.

Keep your key secret. Do not expose it in client-side JavaScript. PureGuard is designed for server-to-server calls only. If your key is compromised, regenerate it immediately from Settings.
HeaderValueRequired
X-API-Key pg_xxxxxxxxxxxxxxxx Required
Content-Type application/json Required (for POST)

Example authenticated request:

cURL
curl -H "X-API-Key: pg_abc123def456" \
     https://pureguard.io/api/v1/stats

Requests without a valid key receive a 401 Unauthorized response. Requests that exceed your plan's monthly quota receive 429 Too Many Requests.

API Reference

Base URL: https://pureguard.io/api/v1. All request and response bodies use JSON. Times are in UTC.

Bot Check

POST /api/v1/check

Evaluate a single visitor. Send the visitor's IP address, user-agent string, and optionally their HTTP headers for maximum detection accuracy. The engine runs all enabled layers and returns a verdict within milliseconds.

Request Body

FieldTypeRequiredDescription
ip string Required Visitor's IPv4 or IPv6 address
ua string Required Raw User-Agent header string
url string Required Destination URL (the page they would see)
headers object Optional Full HTTP headers map. Enables Sec-Fetch, Accept-Language, and header fingerprint layers
zone string Optional Traffic source zone/site ID for zone reputation scoring

Full Example

Request
POST /api/v1/check HTTP/1.1
Host: pureguard.io
X-API-Key: pg_abc123def456
Content-Type: application/json

{
  "ip": "203.0.113.42",
  "ua": "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Mobile Safari/537.36",
  "url": "https://example.com/offer?s1=zone_1234",
  "headers": {
    "Sec-Fetch-Site": "cross-site",
    "Sec-Fetch-Mode": "navigate",
    "Sec-Fetch-Dest": "document",
    "Accept-Language": "th-TH,th;q=0.9,en;q=0.8",
    "Accept-Encoding": "gzip, deflate, br"
  },
  "zone": "zone_1234"
}
Response -- 200 OK
{
  "verdict": "ACCEPT",
  "trust": 7.3,
  "confidence": 92,
  "signals": [
    "sec_fetch:+1.0",
    "residential:+0.3",
    "real_device:+0.5",
    "chrome_build:+0.5",
    "lang_match:+0.3"
  ],
  "ms": 2.3
}

Response Fields

FieldTypeDescription
verdict string "ACCEPT" or "BLOCK" -- the final decision
trust float Trust score from 0.0 (definite bot) to 10.0 (definite human). Base: 5.0
confidence integer Engine confidence in the verdict, 0-100. Higher means more signals evaluated
signals array List of detection signals that fired, with their score impact (e.g. "firehol:KILL")
ms float Processing time in milliseconds
Blocked response example: When a bot is detected, the response contains the kill reason: {"verdict":"BLOCK","trust":0.0,"confidence":99,"signals":["firehol:KILL"],"ms":0.8}

Traffic Statistics

GET /api/v1/stats

Returns aggregated traffic statistics for your account. Includes today's counts and a 7-day historical breakdown for charting.

Response -- 200 OK
{
  "today": {
    "checks": 14832,
    "accepts": 8291,
    "blocks": 6541,
    "rate": 55.9
  },
  "week": [
    { "date": "2026-03-02", "checks": 12480, "accepts": 7120, "blocks": 5360 },
    { "date": "2026-03-03", "checks": 13105, "accepts": 7402, "blocks": 5703 },
    { "date": "2026-03-04", "checks": 11893, "accepts": 6814, "blocks": 5079 },
    { "date": "2026-03-05", "checks": 14220, "accepts": 7988, "blocks": 6232 },
    { "date": "2026-03-06", "checks": 15010, "accepts": 8201, "blocks": 6809 },
    { "date": "2026-03-07", "checks": 13744, "accepts": 7512, "blocks": 6232 },
    { "date": "2026-03-08", "checks": 14832, "accepts": 8291, "blocks": 6541 }
  ]
}

Zone Data

GET /api/v1/zones

Returns zone reputation data for all zones seen in your traffic. Use this to identify high-quality and toxic traffic sources programmatically.

Response -- 200 OK
{
  "zones": [
    {
      "id": "zone_1234",
      "status": "trusted",
      "score": 8.2,
      "hits": 4521,
      "accept_rate": 87.3
    },
    {
      "id": "zone_5678",
      "status": "blocked",
      "score": 1.4,
      "hits": 892,
      "accept_rate": 12.1
    },
    {
      "id": "zone_9012",
      "status": "neutral",
      "score": 5.0,
      "hits": 38,
      "accept_rate": 63.2
    }
  ]
}

Zone Statuses

StatusMeaning
trusted Zone has high accept rate and human evidence. Elite traffic.
neutral New zone or insufficient data to classify. In probation.
blocked Zone consistently sends bot traffic. All hits from this zone are rejected.

System Status

GET /api/v1/status

Public endpoint (no authentication required). Returns current engine status, version, and uptime. Use this for health checks and monitoring.

Response -- 200 OK
{
  "status": "operational",
  "engine": "v17.1",
  "layers": 13,
  "uptime": 99.7
}

Detection Layers

PureGuard's engine evaluates traffic through multiple independent detection layers. Each layer operates in one of three modes:

All layers are fully configurable through the dashboard. You can enable/disable individual layers and change their mode via the Guard Settings panel.

Hard Kill Layers

These layers terminate evaluation immediately when triggered. A single match means definite bot.

FireHOL L1 Kill
Checks the visitor's IP against the FireHOL Level 1 blocklist -- a curated feed of 400M+ known malicious IPs updated every 6 hours from 25+ threat intelligence sources.
Signal: firehol:KILL
CrowdSec Kill
Community-driven threat intelligence. Blocks IPs flagged by the CrowdSec network of 70,000+ machines worldwide for scanning, brute-force, and automated abuse.
Signal: crowdsec:KILL
Bot UA Detection Kill
Pattern-matches the User-Agent against known bot signatures: curl, wget, Selenium, Puppeteer, PhantomJS, WebDriver, Headless Chrome, MauiBot, DataForSeoBot, and 50+ others.
Signal: bot_ua:KILL
Chrome Version Check Kill
Blocks UAs claiming Chrome versions beyond 160 -- no such version exists. Bots often generate random high version numbers to appear "modern."
Signal: fake_chrome:KILL
Chrome86 v4 Analysis Kill
Detects Chrome UA Reduction abuse. Chrome 110+ legitimately sends .0.0.0 build numbers, but older Chrome versions with .0.0.0 are spoofed bots impersonating the UA Reduction format.
Signal: chrome86:KILL
Burst Rate Limiter Kill
Blocks IPs that exceed the burst threshold (configurable, default: 5 hits per minute). Uses in-memory APCu counters for sub-millisecond lookups with no database overhead.
Signal: burst_rate:KILL
Ad-Fraud UA Signatures Kill
Identifies ad verification and fraud detection crawlers: Snobi, Criteo Bot, DoubleVerify, Pixalate, Integral Ad Science (IAS), MOAT, Oracle Data Cloud, and similar ad-tech spiders.
Signal: adfraud_ua:KILL

Trust Scoring Layers

These layers adjust the trust score (base: 5.0) up or down. Multiple signals compound. The final trust score determines the verdict: below threshold = BLOCK, above = ACCEPT.

Sec-Fetch Headers Score
Analyzes browser-enforced Sec-Fetch-Site, Sec-Fetch-Mode, and Sec-Fetch-Dest headers. These headers cannot be spoofed by JavaScript and are only sent by real browsers.
Valid: +1.0 | Missing: -2.0 | Invalid: -0.5
Datacenter ASN Detection Score
Checks the visitor's IP against 50+ known datacenter/cloud ASNs including AWS, GCP, Azure, DigitalOcean, OVH, Hetzner, Vultr, and Linode. Real users browse from residential or mobile networks, not datacenters.
Datacenter: -1.5 | Residential: +0.3
Header Consistency Score
Validates expected HTTP header patterns: missing Accept-Encoding (bots skip it), Connection:close (browsers use keep-alive), and Chrome 89+ without Sec-CH-UA (Client Hints are mandatory in modern Chrome).
No Accept-Encoding: -2.0 | Connection:close: -1.0 | No Sec-CH-UA: -2.0
Referer Analysis Score
Penalizes empty referer headers from RTB traffic sources. Real pop/push traffic always includes a referer from the publisher site.
Empty referer (RTB): -1.5
HTTP Version Check Score
Chrome 80+ should use HTTP/2 or HTTP/1.1. If the request arrives as HTTP/1.0 with a modern Chrome UA, it is likely a proxy or script, not a real browser.
Chrome 80+ on HTTP/1.0: -3.0
Hosting ASN (MaxMind) Score
Uses MaxMind GeoIP2 ASN database to identify hosting/VPN/proxy providers at the ASN level. More granular than the datacenter ASN list -- catches smaller VPS providers and commercial VPNs.
Hosting ASN: -2.0

Observation Layers

These layers collect signals for analysis and reporting but do not affect the verdict by default. Enable scoring mode on any of them to start using them in decisions.

Language Check Observe
Compares the Accept-Language header against the visitor's geo-IP country. Includes an exception list of 26 countries where English is commonly used alongside local languages (SEA, India, parts of Africa).
Logs: lang_primary, lang_match
Header Fingerprint Observe
Computes a CRC32 hash of the HTTP header order. Real browsers send headers in a consistent, browser-specific order. Automated tools and proxies produce different orderings.
Logs: header_order_hash
TLS Fingerprint Observe
Records the TLS version from Cloudflare's CF-TLS-Version header. Modern browsers use TLS 1.3. Older TLS versions correlate with outdated or automated clients.
Logs: cf_tls_version

Integration Guide

PureGuard integrates into any stack. Below are production-ready examples for the most common languages, plus traffic source and RTB setup instructions.

PHP Integration

Direct server-side integration. Call the API before redirecting the visitor.

guard_check.php
<?php
function checkBot($ip, $ua, $url, $headers = []) {
    $ch = curl_init('https://pureguard.io/api/v1/check');
    curl_setopt_array($ch, [
        CURLOPT_POST           => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT        => 5,
        CURLOPT_HTTPHEADER     => [
            'X-API-Key: pg_your_api_key',
            'Content-Type: application/json',
        ],
        CURLOPT_POSTFIELDS => json_encode([
            'ip'      => $ip,
            'ua'      => $ua,
            'url'     => $url,
            'headers' => $headers,
        ]),
    ]);

    $response = json_decode(curl_exec($ch), true);
    curl_close($ch);

    return $response;
}

// Usage in your click handler:
$result = checkBot(
    $_SERVER['REMOTE_ADDR'],
    $_SERVER['HTTP_USER_AGENT'],
    'https://example.com/offer'
);

if ($result['verdict'] === 'BLOCK') {
    http_response_code(204);  // Bot killed silently
    exit;
}

// Clean visitor -- redirect to offer
header('Location: https://offer.example.com/lp1');
exit;

Node.js Integration

guard.js
const checkBot = async (ip, ua, url) => {
  const res = await fetch('https://pureguard.io/api/v1/check', {
    method: 'POST',
    headers: {
      'X-API-Key': process.env.PUREGUARD_KEY,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ ip, ua, url }),
  });

  return res.json();
};

// Express middleware example
app.get('/click', async (req, res) => {
  const guard = await checkBot(
    req.ip,
    req.get('User-Agent'),
    req.originalUrl
  );

  if (guard.verdict === 'BLOCK') {
    return res.status(204).end();
  }

  res.redirect('https://offer.example.com/lp1');
});

Python Integration

guard.py
import requests
import os

API_KEY = os.getenv("PUREGUARD_KEY")
API_URL = "https://pureguard.io/api/v1/check"

def check_bot(ip: str, ua: str, url: str) -> dict:
    resp = requests.post(
        API_URL,
        json={"ip": ip, "ua": ua, "url": url},
        headers={
            "X-API-Key": API_KEY,
            "Content-Type": "application/json",
        },
        timeout=5,
    )
    resp.raise_for_status()
    return resp.json()

# Usage with Flask
from flask import Flask, request, redirect, abort

app = Flask(__name__)

@app.route("/click")
def click_handler():
    result = check_bot(
        request.remote_addr,
        request.user_agent.string,
        request.url,
    )
    if result["verdict"] == "BLOCK":
        abort(204)
    return redirect("https://offer.example.com/lp1")

Traffic Source Setup

For pop/push/native traffic sources, point your campaign URL through PureGuard's click handler. The engine evaluates the visitor inline and redirects clean traffic to your offer.

Redirect flow: Traffic Source → click.php (Guard evaluates) → ACCEPT: 302 to offer  |  BLOCK: 204 No Content

Click URL Format

Campaign URL
https://pureguard.io/click.php?s1={zone_id}&s2={click_id}&s3={source}

Source Macro Mapping

Sources1 (Zone)s2 (Click ID)s3 (Source)
PopAds [WEBSITEID] [IMPRESSIONID] POPADS
RollerAds ${SITE_ID} ${CLICK_ID} ROLLERADS
HilltopAds {{zoneid}} {{clickid}} HILLTOPADS
ZeroPark {source} {cid} ZEROPARK
ExoClick {zone_id} {conversions_tracking} EXOCLICK

RTB / OpenRTB 2.5

PureGuard includes a built-in OpenRTB 2.5 bidder for programmatic traffic. The RTB endpoint evaluates bid requests through a dedicated Guard::runRTB() pipeline with 36 signals optimized for bid-time decisions (no browser headers available).

RTB Bid Endpoint
// SSP sends bid request to:
POST https://pureguard.io/bid.php

// PureGuard responds with:
//   BID   (trust >= 5.5)  -> 200 with bid response
//   NOBID (trust <  5.5)  -> 204 No Content

// Win notice URL (in bid response):
https://pureguard.io/win.php?price=${AUCTION_PRICE}&id={bid_id}

RTB Trust Engine

The RTB pipeline starts at a base trust of 10.0 and applies 36 penalty/bonus signals. Traffic scoring at bid time does not have access to browser headers (Sec-Fetch, etc.) since the RTB request comes from the SSP server, not the end user. Instead it relies on:

Daily budget: The RTB bidder enforces a configurable daily spend cap. When the budget is exhausted, all bid requests receive an instant 204 (no processing overhead).

Rate Limits

Rate limits are enforced per API key at the Nginx level. Exceeding the limit returns 429 Too Many Requests. The response includes a Retry-After header indicating when to retry.

EndpointRate LimitBurstNotes
/api/v1/* 10 req/s 20 Per API key. Sufficient for most traffic volumes.
/click.php 30 req/s 60 Per IP. Direct click handler for redirect-based integration.
/bid.php 100 req/s 200 Per IP. OpenRTB endpoint handles high QPS from SSPs.
Enterprise customers can request custom rate limits. Contact support to increase limits for high-volume campaigns processing over 1M+ checks per day.

Error Codes

All error responses include a JSON body with an error field describing the issue.

CodeStatusDescriptionResolution
200 OK Request processed successfully -
400 Bad Request Missing or malformed request body. Required fields: ip, ua, url Check your JSON payload for missing fields or syntax errors
401 Unauthorized Missing or invalid API key in X-API-Key header Verify your key in Settings. Regenerate if compromised.
403 Forbidden Account suspended or plan does not include this endpoint Contact support or upgrade your plan
429 Too Many Requests Rate limit or monthly quota exceeded Wait for Retry-After seconds, or upgrade for higher limits
500 Internal Error Unexpected server error during processing Retry the request. If persistent, check /api/v1/status

Error Response Format

401 Unauthorized
{
  "error": "Invalid or missing API key",
  "code": 401
}
429 Too Many Requests
{
  "error": "Rate limit exceeded",
  "code": 429,
  "retry_after": 2
}
Need help integrating?

Our team can help you set up PureGuard for your specific traffic stack.

Start Free Dashboard