More in API

IP lookup

POST /ip-lookup: where up to 100 IP addresses are, who runs their networks, and whether each is a VPN, proxy, Tor exit or on a block list. The request, an example answer and every field.

POST https://service.cerberius.com/api/ip-lookup

Where each IP address is, who runs its network, and how risky it looks. Use it to spot risky sign-ups and logins, show local prices or times, or block abuse.

Request#

Headers

  • X-API-Key: your key. A key made with Sign every call also needs X-Timestamp and X-Signature (see Authentication and signed calls).
  • Content-Type: application/json

Body

{ "data": ["8.8.8.8", "192.168.1.20"] }
FieldRules
data
list of text
1 to 100 IP addresses, IPv4 or IPv6. Spaces around each address are removed.
  • Every item must be a valid IP address. If one isn't, the whole call fails, so check them before you send them.
  • Private and reserved addresses are allowed, but not looked up. These are addresses used only inside a home or office network (such as 10.x.x.x and 192.168.x.x), loopback (127.0.0.1, ::1), shared carrier networks (100.64.x.x to 100.127.x.x) and the ranges kept for documentation (such as 203.0.113.x).
  • The same address twice gets two results.

Example#

curl https://service.cerberius.com/api/ip-lookup \
  -H "X-API-Key: $CERBERIUS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"data": ["8.8.8.8", "192.168.1.20"]}'

The answer. Values like these change over time, so yours may differ.

{
  "excess_charges_apply": false,
  "data": [
    {
      "ip_address": "192.168.1.20",
      "lookup_status": "private_not_queried",
      "asn": "",
      "isp": "",
      "city": "",
      "country": "",
      "country_code": "",
      "timezone": "",
      "timezone_offset": 0,
      "locale": "",
      "currency": "",
      "currencysymbol": "",
      "in_eu": false,
      "latitude": "",
      "longitude": "",
      "continet_code": "",
      "continet_name": "",
      "reverse_dns": "",
      "is_tor_exit_point": false,
      "is_anonimous": false,
      "on_block_list": false,
      "on_drop_list": false,
      "recent_spam_domain": false,
      "fraud_score": "",
      "org_name": "",
      "org_address": "",
      "org_phone": "",
      "org_email": "",
      "abuse_email": "",
      "remark": ""
    },
    {
      "ip_address": "8.8.8.8",
      "lookup_status": "Success",
      "asn": "15169",
      "isp": "Google LLC",
      "city": "Mountain View",
      "country": "United States",
      "country_code": "US",
      "timezone": "America/Los_Angeles",
      "timezone_offset": -8,
      "locale": "en-US",
      "currency": "USD",
      "currencysymbol": "$",
      "in_eu": false,
      "latitude": "37.405990",
      "longitude": "-122.078514",
      "continet_code": "NA",
      "continet_name": "North America",
      "reverse_dns": "dns.google.",
      "is_tor_exit_point": false,
      "is_anonimous": false,
      "on_block_list": false,
      "on_drop_list": false,
      "recent_spam_domain": false,
      "fraud_score": "low",
      "org_name": "Google LLC",
      "org_address": "1600 Amphitheatre Parkway",
      "org_phone": "+1-650-253-0000",
      "org_email": "[email protected]",
      "abuse_email": "[email protected]",
      "remark": ""
    }
  ]
}

Results can come back in a different order

Addresses are looked up at the same time, and each result is added when it's ready. In the example, the private address came back first. Match each result to your list by its ip_address.

Every result has every field. When something isn't known, a text field is empty (""), a true/false field is false, and timezone_offset is 0.

Fields#

The answer#

FieldWhat it means
excess_charges_apply
true/false
Always false. There are no extra charges. Kept so code written for the first version keeps working.
data
list
One result for each address you sent.

Status#

FieldWhat it means
ip_address
text
The address you sent, without spaces around it.
lookup_status
text
Success: we found where the address is, or who runs its network. No information found: we found neither. private_not_queried: a private or reserved address, which isn't looked up. All its other fields are empty.

Network and owner#

FieldWhat it means
asn
text
The number of the network the address belongs to (its autonomous system number), digits only, such as 15169. 0 or empty when unknown.
isp
text
Who runs that network, such as Google LLC.
org_name
text
Who the address is registered to, from its public registration record (WHOIS).
org_address
text
The street address in that record, when it has one.
org_phone
text
The phone number in that record.
org_email
text
The contact email in that record.
abuse_email
text
Where to report abuse coming from this address.
reverse_dns
text
The address's host name, as DNS gives it, with a dot at the end, such as dns.google.. No reverse DNS found when it has none.
remark
text
Why the address got its fraud_score, such as IP is likely a VPN or proxy, Missing or invalid abuse contact info. When there's no reason to give, the remarks from the registration record, if any.

Location#

The location is approximate: often the centre of a city, sometimes only the country. It's never a street address.

FieldWhat it means
city
text
The city, in English.
country
text
The country, in English, such as United States.
country_code
text
The country's two-letter code, such as US.
continet_code
text
The continent's two-letter code: AF, AN, AS, EU, NA, OC or SA. The misspelt name is the first version's, kept so older code keeps working.
continet_name
text
The continent, in English, such as North America. Spelling kept, as above.
in_eu
true/false
The country is in the European Union.
latitude
text
Degrees north (negative: south), as text with six decimals, such as "37.405990". Turn it into a number before you use it. 0.000000 or empty when unknown.
longitude
text
Degrees east (negative: west), as text, like latitude.
timezone
text
The time zone's name, such as America/Los_Angeles. Empty when the city isn't known.
timezone_offset
number
The time zone's difference from UTC at the moment of the lookup, in whole hours, such as -8. It follows daylight saving. Half hours are dropped: India (UTC+5:30) shows 5. 0 when the time zone isn't known.
locale
text
A likely language and country, such as en-US.
currency
text
The country's currency code, such as USD.
currencysymbol
text
The currency's symbol, such as $. Note: no underscore in this name.

Risk#

FieldWhat it means
is_tor_exit_point
true/false
A Tor exit: traffic from the Tor network comes out here, so the real sender is hidden.
is_anonimous
true/false
Looks like a VPN or proxy: the address is in a list of VPN networks, or its registration record mentions words such as VPN, proxy or tunnel. The misspelt name is kept so older code keeps working.
on_block_list
true/false
On a public block list of addresses recently reported for attacks or spam.
on_drop_list
true/false
In a network on Spamhaus's DROP lists: networks taken over by, or run by, spammers and criminals. Drop all traffic from these.
recent_spam_domain
true/false
Seen sending spam in the last 30 days. Despite its name, it's about the address, not a domain.
fraud_score
text
The overall risk: low, medium or high. Empty for private addresses. A Tor exit, a VPN or proxy, a block list, or a network known for abuse raises it a lot. A cloud or hosting network, and a registration record without an abuse contact, raise it a little. On a drop list, it's always high.

Match results to your list#

Python:

import os

import requests

addresses = ["8.8.8.8", "1.1.1.1", "192.168.1.20"]
response = requests.post(
    "https://service.cerberius.com/api/ip-lookup",
    headers={"X-API-Key": os.environ["CERBERIUS_API_KEY"]},
    json={"data": addresses},
    timeout=100,
)
answer = response.json()
if response.status_code != 200:
    raise RuntimeError(f"{response.status_code}: {answer['error']['message']}")

by_address = {result["ip_address"]: result for result in answer["data"]}
for address in addresses:
    result = by_address[address]
    print(address, result["lookup_status"], result.get("country"), result.get("fraud_score"))

Node.js (18 or later, in a .mjs file):

const addresses = ["8.8.8.8", "1.1.1.1", "192.168.1.20"];
const response = await fetch("https://service.cerberius.com/api/ip-lookup", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-API-Key": process.env.CERBERIUS_API_KEY,
  },
  body: JSON.stringify({ data: addresses }),
  signal: AbortSignal.timeout(100_000),
});
const answer = await response.json();
if (!response.ok) throw new Error(`${response.status}: ${answer.error.message}`);

const byAddress = new Map(answer.data.map((result) => [result.ip_address, result]));
for (const address of addresses) {
  const result = byAddress.get(address);
  console.log(address, result.lookup_status, result.country, result.fraud_score);
}

Credit for the location data#

The location and network data come from DB-IP, under the CC BY 4.0 licence. If you show it to people, such as a country or city on a page, add a credit nearby: "IP data by DB-IP".