- authentication
- Secret API key as a bearer token
- billing
- 1 credit per answered prefix, free for the next seven days for the same prefix; a prefix inside reserved space costs nothing.
One prefix at a time, without looking every address up: for each signal group — datacenter, VPN, relay, Tor, anycast, RIR allocation, threat blocklist, crawler and corporate proxy — whether any installed feed overlaps it, how many feed records intersect it (a Tor group counts exits inside the prefix, a datacenter group the provider ranges that touch it) and which named providers do; whose network and country the first and last addresses resolve to and whether the two ends agree; and the registry facts of the first address. overlaps is tri-state exactly like the address flags, and record counts are capped at 10,000 per feed with truncated saying so, so a count is a floor for a very wide prefix.
The prefix travels as a query parameter, because a slash in a path segment has to be encoded and clients get that wrong: GET /api/v1/ip/range?prefix=203.0.113.0/24. A bare address is a /32 or /128, and the prefix comes back canonical (8.8.8.9/24 as 8.8.8.0/24). A prefix inside private or special-use space answers reason: "reserved_range" with every other field null, free.
Parameters
| name | in | type | description |
|---|---|---|---|
| prefixrequired | query | string | An IPv4 or IPv6 CIDR prefix, or a bare address. At most 64 characters. |
Example request
curl https://spaw.co/api/v1/ip/range \ -H "Authorization: Bearer sk_live_…"
This endpoint has no console on its page. It bills a credit per prefix and has no free guest path: a prefix answer reads every feed that overlaps the whole range rather than one address. POST /api/v1/ip is runnable for a single address inside it.
Responses
200The prefix, its two ends and what each feed group says.
{
"success": true,
"data": {
"prefix": "8.8.8.0/24",
"version": 4,
"reason": null,
"first": "8.8.8.0",
"last": "8.8.8.255",
"addresses": 256,
"network": {
"asn": 15169,
"org": "Google LLC"
},
"network_agrees": true,
"country": "US",
"country_agrees": true,
"registry": "arin",
"registry_country": "US",
"allocated_at": "2023-12-28",
"signals": {
"datacenter": {
"overlaps": true,
"ranges": 1,
"providers": []
},
"vpn": {
"overlaps": false,
"ranges": 0,
"providers": []
},
"relay": {
"overlaps": false,
"ranges": 0,
"providers": []
},
"tor": {
"overlaps": false,
"ranges": 0,
"providers": []
},
"anycast": {
"overlaps": true,
"ranges": 1,
"providers": []
},
"allocated": {
"overlaps": true,
"ranges": 1,
"providers": []
},
"blocklist": {
"overlaps": false,
"ranges": 0,
"providers": []
},
"attack_source": {
"overlaps": false,
"ranges": 0,
"providers": []
},
"crawler": {
"overlaps": false,
"ranges": 0,
"providers": []
},
"corporate_proxy": {
"overlaps": false,
"ranges": 0,
"providers": []
}
},
"truncated": false,
"sources": [
{
"dataset": "dbip-city-lite",
"version": "2026-09"
},
{
"dataset": "dbip-asn-lite",
"version": "2026-09"
},
{
"dataset": "datacenter-ranges",
"version": "2026-09-12"
},
{
"dataset": "vpn-ranges",
"version": "2026-09-12"
},
{
"dataset": "relay-ranges",
"version": "2026-09-12"
},
{
"dataset": "tor-exit-list",
"version": "2026-09-12"
},
{
"dataset": "anycast-ranges",
"version": "2026-09-12"
},
{
"dataset": "rir-allocations",
"version": "2026-09-12"
},
{
"dataset": "threat-blocklists",
"version": "2026-09-12"
},
{
"dataset": "crawler-ranges",
"version": "2026-09-12"
},
{
"dataset": "corporate-proxy-ranges",
"version": "2026-09-12"
}
]
},
"meta": {
"credits_used": 1,
"credits_remaining": 8,
"cache_hit": false,
"request_id": "req_01m1kgdm4xngzmbmff68g94w0c"
}
}401The key is missing, malformed, or revoked.
{
"success": false,
"error": {
"code": "UNAUTHENTICATED",
"message": "Provide a valid API key as a bearer token.",
"request_id": "req_01m1kgdm4xngzmbmff68g94w0c"
}
}402The balance is empty. The lookup did not run.
{
"success": false,
"error": {
"code": "INSUFFICIENT_CREDITS",
"message": "Your credit balance is empty.",
"request_id": "req_01m1kgdm4xngzmbmff68g94w0c"
}
}422The request body could not be validated; `error.errors` lists the fields.
{
"success": false,
"error": {
"code": "VALIDATION_FAILED",
"message": "The email field is required.",
"errors": {
"email": [
"The email field is required."
]
},
"request_id": "req_01m1kgdm4xngzmbmff68g94w0c"
}
}429Over 5 requests per second for the key. Retry after the limit resets.
{
"success": false,
"error": {
"code": "RATE_LIMITED",
"message": "Too many requests. Retry after the limit resets.",
"request_id": "req_01m1kgdm4xngzmbmff68g94w0c"
}
}Error codes
Failures answer { success: false, error: { code, message, request_id } }. Each code has its own page.