Response Schema
Canonical v1 Response Example
Section titled “Canonical v1 Response Example”The v1 API returns IPBot-owned intelligence fields. Public responses do not expose internal data source names, data package versions, raw rule identifiers, or runtime metadata.
{ "ip": "2001:4860:4860::8888", "stack": "ipv6", "location": { "country": "United States", "country_code": "US", "region": "California", "city": "Mountain View", "latitude": 37.3861, "longitude": -122.0838, "timezone": "-08:00" }, "network": { "asn": "AS15169", "org": "GOOGLE", "category": "internet_infrastructure", "operator": "Google", "operator_domain": "google.com", "operator_type": "cloud", "service_role": "public_dns_resolver", "service_name": "Google Public DNS", "routability": "global", "owner": "Google LLC", "allocation": { "cidr": "2001:4860:4860::/48", "range": "2001:4860:4860:: - 2001:4860:4860:ffff:ffff:ffff:ffff:ffff", "registry": "arin", "country": "US" } }, "routing": { "origin_asn": "AS15169", "prefix": "2001:4860:4860::/48", "rpki_status": "valid", "confidence": "high", "origin_conflict": false }, "score": { "ip_score": 70, "risk_score": 30, "band": "good", "verdict": "allow", "recommended_action": "allow" }, "classification": { "usage_type": "public_dns_resolver", "is_datacenter": false, "is_proxy": false, "service_role": "public_dns_resolver", "service_name": "Google Public DNS", "is_public_resolver": true, "is_anycast": true, "is_special_use": false, "is_bogon": false, "is_privacy_relay": false, "is_known_crawler": false, "is_verified_crawler": false, "is_mobile": false, "confidence": "high", "traits": ["public_dns_resolver", "anycast_network", "internet_infrastructure"], "threat_level": "Low", "country_alignment": { "status": "multi_region", "geo_country": "US", "allocation_country": "US", "confidence": "low", "basis": ["geoip", "allocation"], "reason_codes": ["anycast_service"], "score_effect": 0 } }, "evidence": { "summary": { "direct_threat_match": false, "prefix_context_only": false }, "signals": [ { "category": "network", "label": "Public DNS Resolver", "severity": "info", "confidence": "high", "description": "This IP is a well-known public DNS resolver endpoint." } ] }}Root Fields
Section titled “Root Fields”| Field | Type | Description |
|---|---|---|
ip | string | Queried IP address |
stack | string | ipv4, ipv6, or unknown |
location | object | Approximate geolocation |
network | object | ASN, organization, network category, and public operator |
routing | object or null | Optional route-origin confidence for the matched prefix |
rdap | object | Pro-only normalized RDAP registration contacts, omitted unless include=rdap_contacts is accepted |
score | object | Score, band, verdict, and recommended action |
classification | object | Proxy, crawler, privacy, datacenter, and threat flags |
evidence | object | Structured public signals |
decision | object (optional) | Decision Engine v1 recommendation (additive; does not change score) |
scores | object (optional) | Decision Engine v1 component sub-scores (0-100 each) |
scenarios | object (optional) | Decision Engine v1 per-scenario recommendations |
explanation | object (optional) | Decision Engine v1 human-readable explanation and drivers |
Field Projection
Section titled “Field Projection”GET /v1/ip/current and GET /v1/ip/{ip} support fields for top-level response projection:
curl -s "https://api.ipbot.com/v1/ip/8.8.8.8?fields=ip,network,score" | jqfields accepts ip, stack, location, network, routing, score, classification, evidence, decision, scores, scenarios, and explanation. The returned object contains only the requested top-level fields and preserves each field’s canonical nested structure. Empty fields= or unknown names return 400 INVALID_FIELDS with allowed_fields and invalid_fields in details.
When a Pro API key requests include=rdap_contacts, rdap becomes an additional top-level field and can be projected with fields=rdap. Without the include, fields=rdap is invalid because the public canonical response does not contain RDAP contacts.
Network Object
Section titled “Network Object”| Field | Type | Description |
|---|---|---|
asn | string | Autonomous System Number |
org | string | Organization name associated with the ASN |
category | string | Normalized category such as residential, datacenter, cloud, edge, internet_infrastructure, content, network, or unknown |
operator | string | Public network operator name when available |
operator_domain | string | Optional normalized official operator hostname from reviewed mappings or the loaded operator profile; display-only metadata that never changes scores or decisions |
operator_type | string | Operator class such as isp, cloud, cdn, content, network_service_provider, mobile_carrier, or enterprise |
service_role | string | Stable role for known public infrastructure endpoints, such as public_dns_resolver |
service_name | string | User-facing service name, such as Google Public DNS, Cloudflare 1.1.1.1, Telegram, or Stripe Webhooks |
routability | string | Public routability class: global, private, non_global, reserved, or bogon |
owner | string | Registry owner name when available |
allocation | object or null | Allocation CIDR/range/country context when available |
The country fields answer different questions and must not be conflated: location.country_code is the estimated GeoIP country (where the IP appears to be used), network.allocation.country is the registration/allocation country (who holds the block and where it is registered), classification.registry_country is RIR delegated country context, and routing.origin_asn describes route origin — none of them proves physical server location. classification.country_alignment compares these inputs and reports the result explicitly.
Pro RDAP Object
Section titled “Pro RDAP Object”rdap is omitted from Free and anonymous responses. It is only returned by GET /v1/ip/current and GET /v1/ip/{ip} when a Pro API key explicitly sends include=rdap_contacts.
| Field | Type | Description |
|---|---|---|
available | boolean | Whether normalized RDAP registration data was available for the lookup |
cache_status | string | Optional ownership cache state such as fresh or stale |
conformance | array | RDAP conformance labels from the RDAP response |
network | object or null | Registered network metadata, allocation, events, and RDAP links |
contacts | array | RDAP entity contacts with roles, names, organizations, emails, phones, addresses, events, and links |
notices | array | RDAP notices from the registry response |
remarks | array | RDAP remarks from the registry response |
redacted | array | RDAP redaction metadata when the registry reports redacted fields |
IPBot still does not expose raw RDAP JSON or raw WHOIS text. The Pro object is normalized, cacheable ownership data extracted from RDAP fields.
Routing Object
Section titled “Routing Object”| Field | Type | Description |
|---|---|---|
origin_asn | string | Origin ASN used for route-origin validation |
prefix | string | Matched prefix used for validation |
rpki_status | string | valid, invalid_asn, invalid_length, or unknown |
confidence | string | low, medium, or high |
origin_conflict | boolean | True when optional BGP origin evidence disagrees with lookup or range ASN evidence |
Score Object
Section titled “Score Object”| Field | Type | Description |
|---|---|---|
ip_score | integer | 0-100, higher is better |
risk_score | integer | 0-100, higher is riskier |
band | string | perfect, excellent, good, fair, poor, or danger |
verdict | string | allow, monitor, challenge, or block |
recommended_action | string | allow, rate_limit, captcha_challenge, manual_review, or block |
Classification Object
Section titled “Classification Object”| Field | Type | Description |
|---|---|---|
usage_type | string | Product-level usage type such as datacenter, vpn, residential_proxy, or unknown |
is_datacenter | boolean | True when hosting/cloud/infrastructure evidence is present |
is_proxy | boolean | True when proxy, VPN, Tor, or related proxy evidence is present |
is_cloud | boolean | True when the IP matched a public cloud provider range |
cloud_provider | string | Public cloud or network operator name when available |
cloud_service | string | Provider service label when available |
cloud_region | string | Provider region when available |
is_vpn | boolean | True when VPN evidence is present |
is_tor | boolean | True when Tor evidence is present |
is_known_abuser | boolean | True when abuse evidence is present |
is_residential_proxy | boolean | Present only when residential proxy evidence is known |
service_role | string | Stable role for known public infrastructure endpoints |
service_name | string | User-facing service name for known public infrastructure endpoints and official service ranges |
is_public_resolver | boolean | True for well-known public DNS resolver endpoints |
is_anycast | boolean | True when the endpoint is known to be anycasted |
is_edge_network | boolean | True when the IP matched a CDN or edge provider range |
is_privacy_relay | boolean | True for privacy relay networks |
privacy_relay_provider | string | Public relay operator name when available |
is_known_crawler | boolean | True when official crawler range evidence is present; known crawlers are not penalized only for a bot-like user agent |
is_verified_crawler | boolean | Present and true when caller-independent crawler DNS verification passes, or on the no-store verification endpoint when a supported range and explicitly supplied user-agent agree |
crawler_provider | string | Public crawler operator name when available |
crawler_type | string | Stable crawler family label such as googlebot, bingbot, or gptbot |
crawler_verified_by | string | Present only for verified crawler evidence; user_agent_ip_range is limited to a surface that explicitly accepts caller context, not cacheable third-party IP GETs |
crawler_hostname | string | Present only when DNS verification yields a stable crawler hostname |
is_special_use | boolean | True when the IP is in a special-use or non-global address range |
special_use_type | string | Stable label such as private, shared_address_space, loopback, documentation, reserved, or unallocated |
is_bogon | boolean | True when the IP is not expected as a normal public Internet source address, including dynamic full-bogon evidence when available |
is_mobile | boolean | True when the network is classified as a mobile carrier network |
registry | string | Public registry context when available |
registry_country | string | RIR delegated country code when available |
proxy_type | string | Normalized proxy type such as vpn, tor, web_proxy, or residential_proxy |
confidence | string | low, medium, or high |
traits | string[] | Stable traits such as datacenter, cloud_network, known_crawler, public_dns_resolver, anycast_network, proxy, or verified_crawler |
threat_level | string | Human-readable threat level |
country_alignment | object | Explainable “native IP” tendency: estimated GeoIP country versus registration countries. Always present on v1 IP responses — see below |
classification.country_alignment Object
Section titled “classification.country_alignment Object”The explainable replacement for a bare is_native boolean. It compares the estimated GeoIP country with the registration countries (allocation first, RIR delegation as fallback) and reports a tendency, never proof of physical server location. An RFC 8805 operator geofeed from a reviewed publisher can corroborate the estimated GeoIP country, but it never replaces missing registration context. The current authority review is publisher-level, not an independent per-prefix authorization proof. Descriptive only: the result never feeds risk_score, verdict, or recommended_action.
| Field | Type | Description |
|---|---|---|
status | string | aligned, mismatch, multi_region, unknown, or not_applicable |
geo_country | string | Estimated GeoIP two-letter country code input; invalid/unknown sentinels are omitted |
allocation_country | string | Registration/allocation two-letter country input; invalid/unknown sentinels are omitted |
delegation_country | string | RIR delegated two-letter country context input; invalid/unknown sentinels are omitted |
confidence | string | low, medium, or high; high requires a publisher-reviewed RFC 8805 geofeed to corroborate the GeoIP country |
basis | string[] | Inputs actually available: geoip, allocation, rir_delegation, and publisher-reviewed geofeed |
reason_codes | string[] | Machine-readable reasons such as geo_matches_allocation, geo_mismatch_allocation, geo_corroborated_by_geofeed, geofeed_geo_conflict, anycast_service, special_use_address, bogon_address, rdap_rir_country_conflict, origin_conflict_present |
score_effect | integer | Always 0 — country alignment never changes scores or verdicts |
Anycast and public-resolver services report multi_region instead of a single-country verdict; special-use and bogon addresses report not_applicable; conflicting or missing inputs (including an operator-geofeed conflict or contested BGP origin) report unknown. Geofeed rows from an unreviewed publisher—or a source claiming verification without a documented publisher-authority basis—are ignored. RFC 9092 plus RPKI/RDAP per-prefix scope validation remains Phase 2. mismatch describes a cross-region deployment tendency (often called “non-native”), not malice, and the assessment always describes the current egress IP—it cannot recover the real IP behind a VPN or proxy.
Evidence Object
Section titled “Evidence Object”evidence.signals is the public explanation layer. It is designed for product UI and customer automation.
{ "signals": [ { "category": "privacy", "label": "VPN", "severity": "medium", "confidence": "medium", "description": "This IP is associated with VPN traffic." } ]}| Field | Type | Description |
|---|---|---|
category | string | network, privacy, threat, automation, routing, or context |
label | string | User-facing signal label |
severity | string | info, low, medium, or high |
confidence | string | low, medium, or high |
description | string | Short explanation suitable for UI |
evidence.summary Object
Section titled “evidence.summary Object”summary separates “this exact IP has a direct threat record” from “only the surrounding prefix has threat context”. A risk_score of 0 means trust offsets, suppression, and rounding left no effective risk score; it does not prove that no direct record exists. Use direct_threat_match for that question.
| Field | Type | Description |
|---|---|---|
direct_threat_match | boolean | True when this exact IP appears on a loaded threat list |
prefix_context_only | boolean | True when the IP itself has no direct record but its surrounding /24 shows threat-list coverage |
evidence.prefix_threat_context Object
Section titled “evidence.prefix_threat_context Object”For IPv4 lookups, reports sampled threat-list coverage of the surrounding /24 (method sampled_hosts_v1: 8 host addresses probed against the loaded threat lists). Omitted for IPv6. matched_hosts / sampled_hosts is a sampling estimate of list coverage in the prefix — not a measured share of malicious hosts. Evidence-only: score_effect is always 0; this context never enters scoring and never flips a verdict on its own.
| Field | Type | Description |
|---|---|---|
scope | string | Sampled prefix, for example 137.175.101.0/24 |
method | string | sampled_hosts_v1 |
sampled_hosts | integer | Probed neighbor addresses |
matched_hosts | integer | Probed neighbors present on loaded threat lists |
confidence | string | low or medium — sampling confidence, capped because 8 probes cover a /24 sparsely |
source | string | loaded_threat_lists |
score_effect | integer | Always 0 |
evidence.threatfox Object
Section titled “evidence.threatfox Object”When the optional ThreatFox sidecar is present and the IP also matched the existing ThreatFox feed, evidence can include a structured threatfox object. This is metadata only: it does not add any extra score beyond the normal THREAT_LIST:threatfox risk path.
| Field | Type | Description |
|---|---|---|
malware | string | Malware family or printable label from the sidecar |
first_seen | string | First-seen timestamp from the sidecar |
last_seen | string | Last-seen timestamp from the sidecar |
confidence_level | integer | ThreatFox confidence level for the IOC |
Data Status Object
Section titled “Data Status Object”GET /v1/data/status returns public service capability readiness. Detailed source versions and coverage counters are reserved for internal operations.
{ "service_status": "ok", "capabilities": [ "geolocation", "asn_intelligence", "threat_intelligence", "proxy_detection", "provider_intelligence", "range_intelligence", "routability_intelligence", "ownership_intelligence", "routing_intelligence", "service_intelligence", "bgp_origin_intelligence", "crawler_verification" ], "coverage_summary": { "lookup_ready": true, "threat_intelligence_ready": true, "proxy_detection_ready": true, "provider_intelligence_ready": true, "range_intelligence_ready": true, "routability_ready": true, "ownership_ready": true, "routing_ready": true, "service_intelligence_ready": true, "bgp_origin_ready": false, "crawler_verification_ready": true }}TypeScript Interface
Section titled “TypeScript Interface”interface IPBotResponse { ip: string; stack: "ipv4" | "ipv6" | "unknown"; location: { country: string; country_code: string; region?: string; city: string; postal?: string; latitude: number; longitude: number; timezone: string; }; network: { asn: string; org: string; category: string; operator?: string; operator_domain?: string; operator_type?: string; service_role?: string; service_name?: string; routability?: "global" | "private" | "non_global" | "reserved" | "bogon"; owner?: string; allocation?: { cidr?: string; range?: string; registry?: string; country?: string; }; }; routing?: { origin_asn?: string; prefix?: string; rpki_status?: "valid" | "invalid_asn" | "invalid_length" | "unknown"; confidence?: "low" | "medium" | "high"; origin_conflict?: boolean; }; score: { ip_score: number; risk_score: number; band: "perfect" | "excellent" | "good" | "fair" | "poor" | "danger"; verdict: "allow" | "monitor" | "challenge" | "block"; recommended_action: "allow" | "rate_limit" | "captcha_challenge" | "manual_review" | "block"; }; classification: { usage_type: string; is_datacenter: boolean; is_proxy: boolean; is_cloud?: boolean; cloud_provider?: string; cloud_service?: string; cloud_region?: string; service_role?: string; service_name?: string; is_public_resolver?: boolean; is_anycast?: boolean; is_edge_network?: boolean; is_privacy_relay?: boolean; privacy_relay_provider?: string; is_known_crawler?: boolean; is_verified_crawler?: boolean; crawler_provider?: string; crawler_type?: string; crawler_verified_by?: string; crawler_hostname?: string; is_special_use?: boolean; special_use_type?: string; is_bogon?: boolean; registry?: string; registry_country?: string; is_mobile?: boolean; proxy_type?: string; confidence: "low" | "medium" | "high"; traits?: string[]; is_vpn?: boolean; is_tor?: boolean; is_known_abuser?: boolean; is_residential_proxy?: boolean; threat_level: string; country_alignment: { status: "aligned" | "mismatch" | "multi_region" | "unknown" | "not_applicable"; geo_country?: string; allocation_country?: string; delegation_country?: string; confidence: "low" | "medium" | "high"; basis?: string[]; reason_codes?: string[]; score_effect: 0; }; }; evidence: { summary: { direct_threat_match: boolean; prefix_context_only: boolean; }; signals: Array<{ category: string; label: string; severity: "info" | "low" | "medium" | "high"; confidence: "low" | "medium" | "high"; description: string; }>; prefix_threat_context?: { scope: string; method: "sampled_hosts_v1"; sampled_hosts: number; matched_hosts: number; confidence: "low" | "medium"; source: "loaded_threat_lists"; score_effect: 0; }; threatfox?: { malware?: string; first_seen?: string; last_seen?: string; confidence_level?: number; }; }; // Decision Engine v1 — additive, optional. Does not change score/verdict/recommended_action. decision?: { profile: string; role: string; action: "allow" | "monitor" | "challenge" | "rate_limit" | "manual_review" | "block"; risk_level: "low" | "medium" | "high"; confidence: "low" | "medium" | "high"; policy_version: string; allowed_actions: string[]; blocked_actions: string[] | null; guardrails_applied: string[] | null; }; scores?: { risk_score: number; base_risk_score: number; abuse_score: number; anonymity_score: number; trust_score: number; infrastructure_score: number; routing_risk_score: number; evidence_quality_score: number; }; scenarios?: Record< "content" | "seo_crawler" | "login" | "signup" | "payment" | "api", { action: string; risk_level: string; confidence: string; reason: string; } >; explanation?: { summary: string; key_reason: string; drivers: Array<{ type: string; label: string; impact: string; impact_score: number; direction: "raises_risk" | "lowers_risk"; reason: string; }>; guardrails_applied: string[]; reason_chain: string[]; };}Decision Engine
Section titled “Decision Engine”The Decision Engine adds four optional, additive top-level objects to the v1 lookup response: decision, scores, scenarios, and explanation. They are purely advisory and do not change score.risk_score, score.verdict, or score.recommended_action. Clients that do not consume these fields keep working unchanged. Any of these objects may be absent.
{ "decision": { "profile": "balanced", "role": "datacenter", "action": "monitor", "risk_level": "medium", "confidence": "high", "policy_version": "decision-v1-2026-07.1", "allowed_actions": ["allow", "monitor", "challenge"], "blocked_actions": null, "guardrails_applied": ["verified_crawler_protection"] }, "scores": { "risk_score": 30, "base_risk_score": 28, "abuse_score": 0, "anonymity_score": 10, "trust_score": 90, "infrastructure_score": 80, "routing_risk_score": 0, "evidence_quality_score": 85 }, "scenarios": { "content": { "action": "allow", "risk_level": "low", "confidence": "high", "reason": "Trusted infrastructure" }, "seo_crawler": { "action": "allow", "risk_level": "low", "confidence": "high", "reason": "Known good network" }, "login": { "action": "monitor", "risk_level": "medium", "confidence": "medium", "reason": "Datacenter origin" }, "signup": { "action": "challenge", "risk_level": "medium", "confidence": "medium", "reason": "Datacenter origin" }, "payment": { "action": "challenge", "risk_level": "high", "confidence": "medium", "reason": "Elevated payment risk" }, "api": { "action": "monitor", "risk_level": "low", "confidence": "high", "reason": "Automation expected" } }, "explanation": { "summary": "Datacenter network with strong infrastructure trust and no abuse history.", "key_reason": "datacenter_origin", "drivers": [ { "type": "infrastructure", "label": "Datacenter Origin", "impact": "+10", "impact_score": 10, "direction": "raises_risk", "reason": "Traffic originates from a hosting/datacenter network." } ], "guardrails_applied": ["verified_crawler_protection"], "reason_chain": ["base_risk=28", "datacenter_origin", "policy=balanced"] }}decision Object
Section titled “decision Object”| Field | Type | Description |
|---|---|---|
profile | string | Policy profile that produced this decision |
role | string | Inferred role of the IP (for example datacenter, residential, proxy, verified_crawler) |
action | string | Recommended action: allow, monitor, challenge, rate_limit, manual_review, or block |
risk_level | string | low, medium, or high |
confidence | string | low, medium, or high |
policy_version | string | Version of the decision policy applied |
allowed_actions | string[] | Actions permitted under the active policy |
blocked_actions | string[] or null | Actions explicitly blocked, or null when none |
guardrails_applied | string[] or null | Guardrails that constrained the decision, or null when none |
scores Object
Section titled “scores Object”Eight component sub-scores, each 0-100. These break down how the assessment is composed and are advisory only.
| Field | Type | Description |
|---|---|---|
risk_score | integer | Composite risk (0-100) |
base_risk_score | integer | Base risk before policy adjustments |
abuse_score | integer | Known-abuse contribution |
anonymity_score | integer | Proxy/VPN/Tor anonymity contribution |
trust_score | integer | Positive trust contribution (higher = more trusted) |
infrastructure_score | integer | Infrastructure/datacenter contribution |
routing_risk_score | integer | Route-origin/RPKI risk contribution |
evidence_quality_score | integer | Confidence in the supporting evidence |
scenarios Object
Section titled “scenarios Object”Per-scenario recommendation keyed by scenario name (content, seo_crawler, login, signup, payment, api). Each value has the same shape, and scenarios.*.confidence is computed for that scenario from its own expected-loss margin and supporting evidence rather than copied from decision.confidence.
| Field | Type | Description |
|---|---|---|
action | string | Recommended action for this scenario |
risk_level | string | low, medium, or high for this scenario |
confidence | string | low, medium, or high |
reason | string | Short explanation for the scenario recommendation |
explanation Object
Section titled “explanation Object”| Field | Type | Description |
|---|---|---|
summary | string | Human-readable summary of the decision |
key_reason | string | Primary driver identifier |
drivers | array | Individual factors influencing risk |
drivers[].type | string | Driver category |
drivers[].label | string | User-facing driver label |
drivers[].impact | string | Signed impact magnitude (for example +10) |
drivers[].impact_score | integer | Numeric impact contribution |
drivers[].direction | string | raises_risk or lowers_risk |
drivers[].reason | string | Short explanation for the driver |
guardrails_applied | string[] | Guardrails that constrained the decision |
reason_chain | string[] | Ordered reasoning steps |