Integrate enterprise-grade IP intelligence into your applications with our powerful REST API.
Generate your API key from the dashboard
Send HTTP requests to our endpoints
Receive JSON response with threat data
All API requests require authentication using your API key. Include it in the request header:
X-API-KEY: YOUR_API_KEY
https://affguard.ai/api/v1
Analyze an IP address for VPN, proxy, Tor, and datacenter detection.
Cache HIT (<20ms) applies to previously-seen IPs served from Redis. DB lookup (50–120ms) runs a range query against the intelligence database. AI Engine adds ~800ms–1.5s only when enabled on your key and the IP is brand new to our system.
GET /api/v1/ip/lookup?ip={ip}
| Parameter | Type | Required | Description |
|---|---|---|---|
ip |
string | Yes | IPv4 or IPv6 address to analyze |
curl -X GET "https://affguard.ai/api/v1/ip/lookup?ip=8.8.8.8" \
-H "X-API-KEY: YOUR_API_KEY"
{
"ip": "8.8.8.8",
"country": "US",
"is_vpn": "no",
"is_proxy": "no",
"is_tor": "no",
"is_bot": "no",
"is_hosting": "yes",
"is_blacklist": "no",
"is_residential": "no",
"risk_score": 40,
"fraud_score": 30,
"confidence": 80,
"threat_categories": ["datacenter_hosting"],
"subnet_risk": 12.50,
"is_abusive": "no",
"abuse_reports": 0,
"is_market_research_flagged": "no",
"market_research_reports": 0
}
| Field | Format | Description |
|---|---|---|
ip |
string | The IP address queried in the request. |
country |
string | Two-letter ISO 3166-1 country code (e.g., "US", "GB"). |
is_vpn |
yes/no | Returns "yes" if identified as a Virtual Private Network. |
is_proxy |
yes/no | Returns "yes" if identified as a non-anonymous or commercial proxy. |
is_tor |
yes/no | Returns "yes" if identified as an active Tor Exit Node. |
is_bot |
yes/no | Returns "yes" if the IP belongs to a known crawler or bot. |
is_hosting |
yes/no | Returns "yes" if the IP is from a cloud provider or data center. |
is_blacklist |
yes/no | Returns "yes" if the IP is found on global threat/spam blacklists. |
risk_score |
int (0–100) | Technical risk score based on the connection nature. |
fraud_score |
int (0–100) | Behavioral risk score based on historical abuse. |
confidence |
int (0–100) | null | Data confidence score. Higher values indicate more trusted provider sources. Returns null when no provider data is available. |
threat_categories |
array | Classified threat labels for this IP, ordered by severity. Possible values: tor_exit_node, vpn, proxy, datacenter_hosting, bot, blacklisted, cpa_fraud, survey_fraud, geo_asn_mismatch, dns_inconsistency, contaminated_subnet, residential. Returns an empty array when no threats are detected. |
subnet_risk |
float (0–100) | null | Percentage of flagged IPs within the same /24 subnet. Higher values indicate a contaminated network neighbourhood. Returns null for IPv6 addresses. |
is_residential |
yes/no | Returns "yes" if identified as a residential proxy or ISP connection. Residential IPs receive a lower risk score. |
is_abusive |
yes/no | Returns "yes" if flagged for advertising/CPA fraud behavior. |
abuse_reports |
int | Number of unique CPA fraud reports associated with this IP. |
is_market_research_flagged |
yes/no | Returns "yes" if flagged for survey/panel market research fraud (fake traffic, incentivized clicks). |
market_research_reports |
int | Number of unique market research fraud reports associated with this IP. |
Pass optional user/session/device parameters on the same IP Lookup call to detect multi-IP rotation, multi-accounting, device farms, and coordinated fraud that pure IP intelligence cannot catch on its own. All parameters are optional — when omitted, the response is identical to a standard IP lookup (fully backwards compatible).
| Parameter | Type | Required | Description |
|---|---|---|---|
user_id |
string | No | Your internal user identifier (any opaque string, max 128 chars). Hashed server-side — never stored in plaintext. |
session_id |
string | No | Your internal session identifier (any opaque string, max 128 chars). Hashed server-side. |
device_fingerprint |
string | No — Recommended | Any opaque string that uniquely identifies a device on your platform (max 256 chars). You choose the logic — it can be any hash you compute that stays consistent for a given device across sessions (e.g., from FingerprintJS, ThumbmarkJS, or your own client-side logic). The more unique-per-device it is, the better the detection. Hashed server-side. |
ua |
string | No | The end-user's browser User-Agent string. Used as a fallback when device_fingerprint is not provided. Low specificity on its own — see warning below. |
device_fingerprint is strongly recommended over ua alone:ua is provided, thresholds are raised significantly (15+ users vs 5) and signals only fire when corroborated by another behavioural signal. Each signal returns a confidence level so you know how much weight to put on it.
curl -G "https://affguard.ai/api/v1/ip/lookup" \
-H "X-API-KEY: YOUR_API_KEY" \
--data-urlencode "ip=203.0.113.45" \
--data-urlencode "user_id=user_12345" \
--data-urlencode "session_id=sess_abc" \
--data-urlencode "device_fingerprint=YOUR_DEVICE_HASH" \
--data-urlencode "ua=Mozilla/5.0 (iPhone; CPU iPhone OS 17_0)"
behavior block){
"ip": "203.0.113.45",
"country": "US",
"is_vpn": "no",
"is_residential": "yes",
"risk_score": 18,
"fraud_score": 62,
...
"behavior": {
"session_risk": 86,
"fingerprint_quality": "strong",
"ip_rotation": {
"detected": true,
"rotation_count": 4,
"unique_ips": 4,
"rotation_interval_seconds": 902,
"rotation_interval_label": "~15 min",
"rotation_consistency": "high",
"first_rotation_at": "2026-05-11T13:00:14Z",
"last_rotation_at": "2026-05-11T13:45:20Z"
},
"shared_ip": {
"detected": false,
"unique_users_60min": 1,
"unique_sessions_60min": 1,
"window_seconds": 3600
},
"unique_users_on_device": 1,
"unique_devices_on_user": 1,
"first_seen_session": "2026-05-11T13:00:14Z",
"signals": [
{ "type": "ip_rotation_detected", "confidence": "high" }
]
}
}
| Field | Format | Description |
|---|---|---|
behavior.session_risk |
int (0–100) | Composite behavioural risk score. Also added (weighted) into the top-level fraud_score automatically. |
behavior.fingerprint_quality |
string | "strong" — customer provided device_fingerprint. "weak" — only ua available; higher thresholds apply. "none" — no device data sent. |
behavior.ip_rotation.detected |
boolean | True when consistent IP rotation is detected (3+ IPs, 1 min – 2 hr cadence, with false-positive guards for DHCP/mobile). |
behavior.ip_rotation.rotation_interval_label |
string | Human-readable median rotation cadence: "~1 min", "~5 min", "~15 min", "~30 min", "~1 hour", "~2 hours". |
behavior.ip_rotation.rotation_consistency |
string | "high" — suspiciously regular cadence (likely automated). "medium" — semi-regular. "low" — irregular (likely organic). |
behavior.shared_ip.detected |
boolean | True when the same IP served 5+ distinct users or 8+ distinct sessions within the last 60 minutes — indicates a shared proxy or click farm exit. |
behavior.signals[] |
array | Fired behavioural signals. Each entry has type and confidence ("high", "medium", "low"). Empty array when no signals fire. |
| Signal | Triggers When | Recommended Action |
|---|---|---|
ip_rotation_detected |
3+ distinct IPs for the same session/user with consistent cadence (1 min–2 hr), passes DHCP/mobile false-positive guards | Flag or block when confidence is "high"; review when "medium" |
shared_ip_burst |
Same IP used by 5+ distinct users or 8+ distinct sessions in 60 min | Block conversion or require additional verification |
device_shared_across_users |
Same device fingerprint linked to 5+ distinct users in 24h (strong fp) or 15+ users (weak fp + corroboration) | Block when confidence is "high"; rely only on corroborated weak-fp signals |
velocity_attack |
Same user_id triggers 10+ API lookups in 60 seconds | Rate-limit or block the user_id immediately |
stale_fingerprint_on_new_ip |
Strong device fingerprint seen on 5+ different IPs in 24h | High-confidence proxy reuse — flag or block |
cross_customer_flagged_device |
Device fingerprint was flagged by another AffGuard customer in the last 7 days (strong fingerprint only) | Treat as high-risk; block conversion |
geo_jump |
Same session_id appears in a different country within 2 minutes | Impossible travel — block or invalidate session |
<?php
$apiKey = 'YOUR_API_KEY';
$userId = $loggedInUserId; // your internal user identifier
$sessionId = session_id(); // your session token
$deviceFingerprint = $myDeviceHash; // any string unique to this device
$userAgent = $_SERVER['HTTP_USER_AGENT'];
$query = http_build_query([
'ip' => $_SERVER['REMOTE_ADDR'],
'user_id' => $userId,
'session_id' => $sessionId,
'device_fingerprint' => $deviceFingerprint,
'ua' => $userAgent,
]);
$response = file_get_contents("https://affguard.ai/api/v1/ip/lookup?{$query}", false, stream_context_create([
'http' => ['header' => "X-API-KEY: {$apiKey}"]
]));
$data = json_decode($response, true);
$behavior = $data['behavior'] ?? null;
if ($behavior) {
foreach ($behavior['signals'] as $signal) {
if ($signal['type'] === 'ip_rotation_detected' && $signal['confidence'] === 'high') {
http_response_code(403);
exit('Suspicious activity detected. Conversion blocked.');
}
}
if ($behavior['session_risk'] >= 70) {
http_response_code(403);
exit('High session risk. Conversion blocked.');
}
}
user_id, session_id, device_fingerprint, ua) are HMAC-hashed on receipt and never stored in plaintext. AffGuard cannot reverse the hash to identify your users. Hashed signals are retained for 30 days for behavioural analysis and then permanently deleted.
Validate an email address for deliverability, disposable detection, DNS/MX verification, and SMTP probing. This endpoint is free — it uses your existing API key but does not count against your plan quota.
X-Email-RateLimit-Remaining-Minute, X-Email-RateLimit-Remaining-Hour, X-Email-RateLimit-Remaining-Daily.
GET /api/v1/email/{email}
| Parameter | Type | Location | Description |
|---|---|---|---|
email |
string | URL path | The email address to validate. URL-encode if needed (e.g., user%40example.com). |
curl -X GET "https://affguard.ai/api/v1/email/user%40gmail.com" \
-H "X-API-KEY: YOUR_API_KEY"
{
"email": "[email protected]",
"status": "valid",
"is_disposable": false,
"confidence_score": 95,
"smtp": "unverifiable",
"is_catch_all": null,
"has_typo_suggestion": false,
"did_you_mean": null,
"is_role_address": false,
"mx_host": "gmail-smtp-in.l.google.com",
"domain_age_days": null,
"has_web_presence": true,
"heuristic_score": 0
}
| Field | Type | Description |
|---|---|---|
email | string | The email address that was validated |
status | string | valid | invalid | unknown |
is_disposable | bool | True if confirmed disposable/temporary email provider |
confidence_score | int (0–100) | Overall deliverability confidence. Higher is better. |
smtp |
string |
valid — mailbox confirmed via SMTPinvalid — mailbox rejected by servercatch_all — server accepts all addressesgreylisted — temporarily deferredunverifiable — provider blocks SMTP probing (Gmail, Outlook, Yahoo, iCloud, etc.)unavailable — no MX records foundrate_limited — probing throttled for this domainnot_applicable — email already confirmed disposableerror — connection or protocol failure
|
is_catch_all | bool | True if the domain accepts mail for any address |
has_typo_suggestion | bool | True if the domain looks like a common provider typo |
did_you_mean | string|null | Suggested corrected domain (e.g., gmail.com), or null |
is_role_address | bool | True if the local part is a role address (e.g., info@, support@) |
mx_host | string|null | Primary MX hostname for the domain |
domain_age_days | int|null | Domain age in days from RDAP registration data. Returns null for major known providers (Gmail, Outlook, Yahoo, etc.) where age is irrelevant. |
has_web_presence | bool | True if the domain resolves to a website |
heuristic_score | int (0–100) | Internal heuristic risk score. Higher values indicate more disposable-like signals. |
| Header | Description |
|---|---|
X-Email-RateLimit-Minute | Max requests per minute (60) |
X-Email-RateLimit-Remaining-Minute | Requests remaining in current minute |
X-Email-RateLimit-Hour | Max requests per hour (1,000) |
X-Email-RateLimit-Remaining-Hour | Requests remaining in current hour |
X-Email-RateLimit-Day | Max requests per day (10,000) |
X-Email-RateLimit-Remaining-Daily | Requests remaining today |
When a user attempts to open an offer from your Ad Network or Offerwall partners, check their IP first. If the risk_score is above 40, we recommend blocking the redirect to protect your advertiser reputation.
// Example Logic
if ($data['risk_score'] > 40) {
die("VPN/Proxy detected. Please disable it to continue to offers.");
}
The threat_categories field returns a structured array of detected threat types, ordered by severity. Use these labels to build routing rules, auto-block policies, or dashboard visualizations.
| Category | Trigger | Description |
|---|---|---|
tor_exit_node |
is_tor = yes | Active Tor exit node, anonymized traffic |
vpn |
is_vpn = yes | Commercial or personal VPN service |
proxy |
is_proxy = yes | HTTP/SOCKS proxy detected |
datacenter_hosting |
is_hosting = yes | Cloud provider or data center IP |
bot |
is_bot = yes | Known crawler or automated bot |
blacklisted |
is_blacklist = yes | Found on global threat or spam blacklists |
cpa_fraud |
is_abusive = yes | Flagged for CPA or advertising fraud |
survey_fraud |
is_market_research_flagged = yes | Flagged for survey or panel fraud |
geo_asn_mismatch |
Network anomaly detected | IP geolocation does not match ASN registration country |
dns_inconsistency |
Network anomaly detected | PTR hostname does not resolve back to the original IP |
contaminated_subnet |
subnet_risk ≥ 20% | High concentration of flagged IPs in the /24 network block |
residential |
is_residential = yes, no threats | Clean residential ISP connection (informational only) |
["vpn", "blacklisted", "cpa_fraud"]). An empty array [] means no threat signals were detected.
Our IP Intelligence system provides multiple scoring dimensions to help you make informed security decisions.
Technical threat level based on VPN, Proxy, Tor, and hosting indicators. 0-100
Behavioral risk combining historical abuse, provider data, and recency. 0-100
How much we trust the data, based on provider quality. 0-100 or null
Percentage of flagged IPs in the same /24 network block. 0-100
| Range | Level | Recommended Action |
|---|---|---|
0–25 |
Low | Safe for most transactions. High confidence in residential origin. |
26–55 |
Medium | Moderate risk. Recommended: Extra verification (e.g., OTP or Email confirmation). |
56–85 |
High | Significant risk. High probability of VPN/Proxy abuse. Recommended: Manual review. |
86–100 |
Critical | Extreme fraud risk. Blacklisted or multi-flagged IP. Recommended: Immediate rejection. |
| Code | Status | Description |
|---|---|---|
200 |
OK | Request successful |
422 |
Unprocessable | Invalid IP address, private/reserved range, or malformed email |
401 |
Unauthorized | Invalid or missing API key |
402 |
Payment Required | Insufficient balance |
429 |
Too Many Requests | Rate limit exceeded |
500 |
Server Error | Internal server error |
Rate limits vary by plan tier:
<?php
$apiKey = 'YOUR_API_KEY';
$ip = '8.8.8.8';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://affguard.ai/api/v1/ip/lookup?ip=" . urlencode($ip));
curl_setopt($ch, CURLOPT_HTTPHEADER, ["X-API-KEY: $apiKey"]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$data = json_decode($response, true);
echo "Risk Score: " . $data['risk_score'];
?>
import requests
api_key = 'YOUR_API_KEY'
ip = '8.8.8.8'
headers = {'X-API-KEY': api_key}
response = requests.get('https://affguard.ai/api/v1/ip/lookup', params={'ip': ip}, headers=headers)
data = response.json()
print(f"Risk Score: {data['risk_score']}")
const apiKey = 'YOUR_API_KEY';
const ip = '8.8.8.8';
fetch(`https://affguard.ai/api/v1/ip/lookup?ip=${encodeURIComponent(ip)}`, {
headers: { 'X-API-KEY': apiKey }
})
.then(r => r.json())
.then(data => console.log('Risk Score:', data.risk_score));
curl -X GET "https://affguard.ai/api/v1/ip/lookup?ip=8.8.8.8" \
-H "X-API-KEY: YOUR_API_KEY"
Our support team is here to assist you with integration and technical questions.
mail Contact Support