PureGuard API Documentation

PureGuard is a server-side bot detection engine built for performance media buyers. It analyzes every click through 18 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/v7/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": "high",
  "reason": "",
  "ms": 2.3
}

Authentication

All API requests (except GET /api/v7/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/v7/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/v7. All request and response bodies use JSON. Times are in UTC.

Bot Check

POST /api/v7/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
headers object Optional Full HTTP headers map. Pass the visitor's original headers for maximum detection accuracy (Sec-Fetch, Accept-Language, etc.)
zone string Optional Traffic source zone/site ID for zone reputation scoring
source string Optional Traffic source name (e.g. "POPADS", "ROLLERADS"). Used for source-level analytics.
country string Optional 2-letter ISO country code. If not provided, resolved from IP via geo intelligence.

Full Example

Request
POST /api/v7/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",
  "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",
  "source": "POPADS"
}
Response -- 200 OK
{
  "api_version": "v7",
  "verdict": "ACCEPT",
  "action": "pass",
  "trust": 7.3,
  "trust_threshold": 7.0,
  "confidence": "high",
  "reason": "",
  "scores": {
    "device": 1.2,
    "browser": 1.5,
    "headers": 1.0,
    "network": 1.3,
    "behavior": 0.8,
    "reputation": 0.5
  },
  "signals": { /* detection flags — see Signal Reference */ },
  "detection_layers": {
    "total": 18,
    "triggered": [
      { "layer": "device", "impact": 1.2 },
      { "layer": "browser", "impact": 1.5 }
    ]
  },
  "geo": {
    "country": "TH",
    "asn": "45758",
    "isp": "Triple T Broadband"
  },
  "zone": {
    "id": "zone_1234",
    "allowed": true,
    "status": "known",
    "accept_rate": 87.3,
    "hits": 4521
  },
  "redirect": {
    "action": "pass_to_l3",
    "l3_required": true
  },
  "ms": 2.3,
  "request_id": "a1b2c3d4e5f6g7h8"
}

Response Fields

FieldTypeDescription
verdict string "ACCEPT" or "BLOCK" -- the final decision
action string "pass" or "block" -- simplified verdict for integrations
trust float Trust score from 0.0 (definite bot) to 10.0 (definite human)
trust_threshold float The threshold used for this check. Default: 7.0. Configurable per request.
confidence string Engine confidence level: "high", "medium", or "low"
reason string Primary reason for a BLOCK verdict. Empty string for ACCEPT.
scores object Category breakdown: device, browser, headers, network, behavior, reputation
signals object Detection flags from the engine. Key-value pairs indicating which checks fired and their results.
geo object Enriched geo data: country (2-letter ISO), asn, isp
detection_layers object total: number of active layers. triggered: array of layers that fired, each with layer name and impact score.
zone object Zone reputation: id, allowed (bool), status (blocked/known/unknown), accept_rate, hits
redirect object action: "pass_to_l3" (accepted) or "dump" (blocked). l3_required: whether L3 client-side verification is needed.
ms float Processing time in milliseconds
quality_tier string One of: "gold", "silver", "filtered", "unknown". Traffic quality classification based on trust score and detection signals.
quality_tier_reason string Human-readable explanation of the tier assignment.
request_id string Unique identifier for this request. Use for debugging and support.
Blocked response example: When a bot is detected, the response contains the kill reason: {"verdict":"BLOCK","trust":0.0,"confidence":"high","reason":"threat_intel","ms":0.8}

Traffic Quality Tiers

Every check returns a quality tier classification:

  • Gold — Trust score 7.0+. High confidence human visitor with clean signals across all detection layers.
  • Silver — Trust score 5.0-6.9. Passed detection but with borderline or missing signals.
  • Filtered — Bot signals detected or zone blocked. Non-human or fraudulent traffic.
  • Unknown — Insufficient data to classify (rare).

Traffic Statistics

GET /api/v7/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/v7/zones

Returns zone reputation data for all zones seen in your traffic. Use this to identify high-quality and toxic traffic sources programmatically. Supports pagination, filtering by status and source, and sorting.

Query Parameters

ParameterTypeDefaultDescription
limit int 100 Results per page (max 500)
offset int 0 Skip N results for pagination
status string all Filter by zone status: block, neutral, trusted
source string all Filter by traffic source (e.g. POPADS, CLICKADILLA)
sort string hits Sort field: hits, accept_rate, trust
dir string desc Sort direction: asc or desc
Response -- 200 OK
{
  "api_version": "v7",
  "total": 9501,
  "returned": 3,
  "offset": 0,
  "limit": 100,
  "summary": { "block": 9501, "neutral": 52046, "trusted": 25 },
  "zones": [
    {
      "id": "5149124",
      "status": "block",
      "rule": "BOTH_CONFIRM",
      "source": "POPADS",
      "hits": 31058,
      "accepts": 30492,
      "blocks": 566,
      "accept_rate": 98.2,
      "avg_trust": 0
    },
    {
      "id": "3212677",
      "status": "neutral",
      "rule": "CONVERSION_PROTECTED",
      "source": "POPADS",
      "hits": 26399,
      "accepts": 16091,
      "blocks": 10095,
      "accept_rate": 61.0,
      "avg_trust": 3.2
    }
  ]
}

Zone Statuses

StatusMeaning
trusted Zone has high accept rate and confirmed human evidence. Elite traffic. Whitelisted by UQC v4.
neutral New zone or insufficient data to classify. Traffic is evaluated normally by Guard.
block Zone consistently sends bot traffic. Blocked by one of 14 UQC v4 rules. All traffic from this zone is rejected at the RTB bid level.

Zone Response Fields

FieldTypeDescription
id string Zone/site ID from your traffic source
status string block, neutral, or trusted -- determined by UQC v4 engine
rule string|null UQC v4 rule that triggered the verdict (e.g. BOTH_CONFIRM, CHROME_MASQUERADE, DEAD_ZONE). Null for neutral zones.
source string Traffic source name (e.g. POPADS, CLICKADILLA)
hits integer Total number of visitors seen from this zone
accepts integer Number of visitors that passed Guard (human)
blocks integer Number of visitors blocked by Guard (bot)
accept_rate float Percentage of traffic accepted (0-100). Below 30% with 10+ hits triggers zone blocking.
avg_trust float Average trust score across all hits. 0-10 scale.
tier string Quality tier: "gold", "silver", "filtered", or "unknown". Assignment logic: gold = accept_rate >= 80% AND avg_trust >= 7.0 AND hits >= 10; silver = accept_rate >= 50% AND avg_trust >= 5.0; filtered = blocked zones; unknown = insufficient data.

The summary object in the zones response includes a tier_counts field with aggregate counts:

tier_counts in summary
"tier_counts": {
  "gold": 142,
  "silver": 3891,
  "filtered": 9501,
  "unknown": 48038
}

UQC v4 Block Rules

The Zone Quality Engine (UQC v4) evaluates zones against 14 blocking rules every 10 minutes. When a zone is blocked, the rule field tells you exactly why.

RuleDescription
BOTH_CONFIRMGuard score and pattern evidence both confirm bot traffic
GUARD_EXTREMEGuard block rate above 60%
PATTERN_EXTREMEBehavioral pattern evidence above 55%
DEAD_ZONEZone has hits but zero accepts
CHROME_MASQUERADEHigh rate of headless Chrome impersonation detected
ZERO_MONEY_BOTSignificant traffic with zero revenue and bot evidence
IP_FARMTraffic from concentrated IP addresses (bot farm)
ENTROPY_BOTUnnatural timing patterns (too uniform)
GEO_MISMATCH_BOTHigh rate of geo mismatches between declared and actual location

System Status

GET /api/v7/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.25",
  "layers": 18,
  "uptime": 99.7
}

Beacon

POST /api/v7/beacon

Client-side engagement and browser integrity data from the PureGuard WordPress plugin or custom integration. Beacons are sent via navigator.sendBeacon and are non-blocking.

Request Body

FieldTypeDescription
zstringZone ID
srcstringTraffic source name
tintegerTime on page in seconds
sbooleanWhether user scrolled more than 5%
spintegerMaximum scroll percentage (0-100)
cbooleanWhether user clicked on the page
bvstringBot verdict: "pass" or "fail"
bfarrayBot check flags (e.g. ["webdriver", "no_chrome_obj"])
tierstringQuality tier from server-side check (gold/silver/filtered)
labelstringBeacon trigger: "check", "10s", "30s", "60s", or "leave"
urlstringPage URL where beacon fired

Authentication

API key passed as query parameter: ?key=YOUR_API_KEY

Response

Returns 204 No Content on success. Beacons are fire-and-forget.

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.

IP Threat Intelligence Kill
Checks the visitor's IP against the multiple curated threat intelligence feeds covering hundreds of millions of known malicious IPs. Updated continuously from global sensor networks.
Signal: threat_intel:KILL
Community Threat Network Kill
Community-driven threat intelligence network. Blocks IPs flagged by a global network of sensor machines for scanning, brute-force, and automated abuse.
Signal: community_threat:KILL
Bot UA Detection Kill
Pattern-matches the User-Agent against known bot signatures: 200+ known automation frameworks, headless browsers, scraping tools, and bot crawlers.
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
Browser Build Verification 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: build_verify:KILL
Burst Rate Limiter Kill
Blocks IPs that exceed the burst threshold (configurable, default: 5 hits per minute). Uses in-memory 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: known ad verification crawlers, fraud detection spiders, and ad-tech measurement bots.
Signal: adfraud_ua:KILL

Trust Scoring Layers

These layers adjust the trust score (proprietary base) 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: bonus | Missing: penalty | Invalid: minor penalty
Datacenter ASN Detection Score
Checks the visitor's IP against 13,200+ known hosting, datacenter, and cloud ASNs from a proprietary database. Real users browse from residential or mobile networks, not datacenters.
Datacenter: penalty | Residential: minor bonus
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: penalty | Connection:close: penalty | No Sec-CH-UA: penalty
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): penalty
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: penalty
Hosting ASN Detection Score
Uses proprietary ASN intelligence to identify hosting, VPN, and proxy providers at the ASN level. Covers 13,200+ hosting networks including smaller VPS providers and commercial VPNs.
Hosting ASN: penalty

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 fingerprint of the HTTP header order. Real browsers send headers in a consistent, browser-specific order. Automated tools and proxies produce different orderings.
Logs: header_fingerprint
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

L3 Client-Side Detection

Layer 3 (L3) is a client-side JavaScript probe that runs in the visitor's browser after server-side Guard analysis. It collects hardware and software signals that cannot be obtained from HTTP headers alone. L3 detects headless browsers, emulators, and sophisticated bot frameworks.

How it works: When Guard accepts a visitor (trust above threshold), the redirect page includes a lightweight JavaScript probe. The probe collects 40+ hardware/software signals, computes a verdict, and sends a beacon back to PureGuard. If the L3 check fails, the zone and IP receive penalties that affect future traffic scoring.
SignalDetects
fn_lieFunction.toString() tampering -- headless browsers patch native functions
no_runtimeMissing browser runtime APIs (navigator, screen, performance)
no_fontsNo system fonts detected -- headless Chrome has zero fonts installed
vp_screenViewport/screen size mismatch -- emulators report inconsistent dimensions
bot_fpBot fingerprint patterns (webdriver flag, phantom properties, selenium artifacts)
bad_rtAbnormal rendering timing -- bots render pages in under 1ms
gpu_platGPU/platform mismatch (e.g. SwiftShader GPU on a claimed mobile device)
tz_geoTimezone vs geo-IP mismatch -- bot claims US timezone but IP is in Russia
uad_mobileUser-Agent Data mobile flag contradicts device properties
ntb_wiNetwork timing anomalies (no connection API, impossible RTT values)
rafrequestAnimationFrame timing anomalies -- bots skip frame rendering
dhidDevice hardware ID inconsistencies across multiple browser APIs

L3 results feed back into the zone quality engine and the Quality Tiers system. Zones with high L3 failure rates receive penalties that trigger zone blocking and tier downgrades. The combination of server-side Guard analysis and client-side L3 probing creates a detection system that is extremely difficult for bot operators to bypass.

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/v7/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/v7/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/v7/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 your offer URL  |  BLOCK: 302 to dump URL

Click URL Format

Campaign URL
https://pureguard.io/click.php?wk={workspace_key}&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 RTB pipeline with dozens of 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   (above threshold)  -> 200 with bid response
//   NOBID (below threshold)  -> 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 score and applies multiple 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).

Bid Request Format (OpenRTB 2.5)

Send a POST request with a JSON body conforming to OpenRTB 2.5:

Example Bid Request
{
  "id": "request-123",
  "imp": [{
    "id": "1",
    "instl": 1,
    "bidfloor": 0.50,
    "banner": { "topframe": 1 },
    "ext": { "type": "pop" }
  }],
  "site": {
    "domain": "example.com",
    "id": "site_456",
    "page": "https://example.com/page"
  },
  "device": {
    "ua": "Mozilla/5.0 (Linux; Android 14; ...) Chrome/131.0...",
    "ip": "203.0.113.42",
    "devicetype": 2,
    "os": "Android",
    "geo": { "country": "US" }
  },
  "at": 1,
  "cur": ["USD"]
}

Bid Response Format (200 OK)

When Guard accepts the traffic (above acceptance threshold), PureGuard returns a bid:

Example Bid Response
{
  "id": "request-123",
  "seatbid": [{
    "bid": [{
      "id": "bid-abc123",
      "impid": "1",
      "price": 0.501,
      "adm": "https://pureguard.io/click.php?s1={zone}&s3=SSP&...",
      "nurl": "https://pureguard.io/win.php?bid=bid-abc123&price=${AUCTION_PRICE}&req=request-123&zone={zone}&ssp=SSP",
      "adomain": ["pureguard.io"],
      "crid": "pop-1"
    }]
  }],
  "cur": "USD"
}

No-Bid Response (204 No Content)

A 204 response means PureGuard declined to bid. Common reasons:

ReasonDescription
Budget exhaustedDaily spend cap reached (instant 204, no processing)
Low trustTrust score below acceptance threshold
Bot UAKnown bot/crawler user agent detected
IP blocklistIP found in threat intelligence blocklists
Blocked siteSite/zone on the iframe or quality blocklist
Geo mismatchDeclared geo doesn’t match IP-based geo
instl=0Non-interstitial placement (pop requires instl=1)

Win Notice

The nurl field in the bid response contains the win notification URL. The SSP must call this URL when the auction is won, replacing ${AUCTION_PRICE} with the actual clearing price (CPM). PureGuard uses this to track daily spend and per-zone win caps.

Win Notice URL
GET https://pureguard.io/win.php?bid={bid_id}&price={clearing_price_cpm}&req={request_id}&zone={zone}&ssp={ssp_name}

Testing Your Integration

Test Bid Request (curl)
curl -X POST https://pureguard.io/bid.php \
  -H "Content-Type: application/json" \
  -d '{
    "id":"test-1",
    "imp":[{"id":"1","instl":1,"bidfloor":0.50,"ext":{"type":"pop"}}],
    "site":{"domain":"test.com"},
    "device":{"ua":"Mozilla/5.0 Chrome/131.0","geo":{"country":"US"},
              "ip":"1.2.3.4","devicetype":2},
    "at":1,"cur":["USD"]
  }'

# 200 = bid accepted (check response JSON)
# 204 = no bid (check RTB dashboard for reason)

Current SSP Status

SSPStatusNotes
ClickAdillaLivePop traffic, OpenRTB 2.5, win macro: ${AUCTION_PRICE}
EvaDavCode-readyEndpoint configured, awaiting buying-side credentials from partner

Conversion Tracking (Postback)

PureGuard tracks conversions via server-to-server postback URLs. When a user converts on your offer, the smartlink or offer network fires a postback to PureGuard with the conversion details. This lets PureGuard attribute revenue to specific zones and power the zone quality engine.

How It Works

1 User clicks your ad → PureGuard's click.php filters and redirects clean traffic to your offer
2 User converts (sign up, purchase, etc.) on your offer/smartlink
3 Offer network fires postback to https://pureguard.io/postback.php?...
4 PureGuard logs the SALE event, attributes revenue to the zone, and updates your dashboard

Postback URL Parameters

ParameterRequiredDescription
s2 Yes Click ID / token. Must match the click ID passed in your campaign URL. This is how PureGuard links conversions back to the original click.
value Optional Payout amount (e.g. 2.50). Used to calculate revenue per zone. Defaults to 0 if not provided.
s1 Optional Zone ID. Used for per-zone revenue attribution. Should match the zone macro from your campaign URL.
network Optional Traffic source name (e.g. HILLTOPADS, ROLLERADS). Used for source-level revenue tracking.
wk Recommended Your workspace key. Found in your Connectors page. Enables tenant attribution — without it, conversions are logged but not linked to your account.
type Optional Event type. Use ftd for first-time deposit / sale events. Defaults to conversion.

Example Postback URL

Generic Format
https://pureguard.io/postback.php?s2={click_id}&value={payout}&s1={zone}&network=HILLTOPADS&wk=YOUR_WK

Network-Specific Macros

Each ad network uses different macro names. Use the correct macros for your network:

NetworkClick ID (s2)Zone (s1)Payout (value)
HilltopAds{token}{zone}{payout}
RollerAds{clickId}{zoneId}{payout}
PopAds[IMPRESSIONID][WEBSITEID]{payout}
ExoClick{click_id}{zone_id}{payout}
PropellerAds{click_id}{zoneid}{payout}
Clickadu{clickid}{zoneid}{payout}
AdMaven{clickId}{subId}{payout}
ClickAdilla{clickid}{zoneid}{payout}
Adsterra{clickid}{zoneid}{payout}
EvaDav{click_id}{src_id}{payout}
PopCash[clickid][siteid]{payout}
Galaksion{clickid}{zoneid}{payout}
Tip: You don't need to memorize these macros. Go to Connectors, edit your source, and the postback URL with correct macros is auto-generated and ready to copy.

Duplicate Protection

PureGuard automatically deduplicates conversions. If the same click ID (s2) fires a postback within 5 minutes, the duplicate is rejected. This prevents double-counting from network retries.

Response Format

Success
OK: Fired to HILLTOPADS | HILLTOPADS: HTTP 200. Sentinel Updated.
Duplicate
DUPLICATE: Conversion for token already recorded within 5 minutes.

Shadow Mode

Shadow mode lets you see exactly what PureGuard would do with your traffic -- without blocking anything. Every click is analyzed, scored, and logged, but all traffic passes through to your destination. Use it to prove the value before committing.

Zero risk. Shadow mode never blocks a single visitor. Your campaigns run exactly as they do today. PureGuard just watches and builds intelligence in the background.

What Shadow Mode Does

1 Traffic flows through PureGuard's click handler as normal
2 The full detection engine runs on every click (all layers, all signals)
3 Guard records what it would have blocked -- but lets everything through
4 Your dashboard shows: bot rate, wasted spend, zone blocklists -- hard evidence

Setting Up Shadow Mode

  1. Go to Connectors and create (or edit) your traffic source connector
  2. Set the Mode dropdown to Shadow
  3. Copy your campaign URL and paste it into your traffic source
  4. Launch your campaign -- traffic flows through PureGuard immediately
3-day proof. With $20-50/day traffic, shadow mode builds enough data in 3 days to show your exact bot rate and generate actionable zone blocklists. Most users see results within 24 hours.

Reading Your Shadow Results

After running shadow mode, your dashboard shows:

PageWhat You See
Dashboard Total checks, block rate, spend saved estimate, trust distribution
Zone Quality Per-zone trust scores, bot rates, and exportable blocklists
Traffic Quality Trust histogram, bot signal breakdown by source
Live Traffic Real-time stream of every click with trust scores and verdicts

Shadow → Protect

When you are ready to start blocking bots, switch the connector mode from Shadow to Protect. That is it -- one toggle, instant protection. All your shadow data and zone intelligence carries over.

Rate Limits

Rate limits are enforced per API key using in-memory counters. Exceeding the limit returns 429 Too Many Requests. The response includes X-RateLimit-Remaining and X-RateLimit-Reset headers.

API Rate Limits (per API key)

PlanPer MinutePer Hour
Starter (Free) 60 1,000
Pro 200 5,000
Enterprise 1,000 50,000

Endpoint Rate Limits (per IP, Nginx level)

EndpointRate LimitBurstNotes
/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 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/v7/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