IP address lookup API
Bewary's IP API returns the network that owns an address (ASN and name), the registry country of the address block, what kind of network it is (datacenter, CDN, residential, mobile or business) and its reverse DNS name. Use it as facts for your own rules: an address alone rarely decides a scam. A call costs 1 credit, a free key gets 1,000 credits a month, no card, and it answers in median 260 ms.
- Endpoint
- GET
https://api.bewary.app/v1/ip/{ip} - Price
- 1 credit
- Free key
- 1,000 credits a month, no card
- Speed
- Median 260 ms, measured
- Returns
- verdict, p_scam, entities with typed facts, reason codes
- MCP tool
lookup_ip
Try it
Free from this page: 10 lookups a dayCall it
curl "https://api.bewary.app/v1/ip/1.1.1.1" import requests
r = requests.get("https://api.bewary.app/v1/ip/1.1.1.1", timeout=10)
result = r.json()
print(result["verdict"], result["p_scam"], result["reason_codes"]) const res = await fetch("https://api.bewary.app/v1/ip/1.1.1.1")
const result = await res.json()
console.log(result.verdict, result.p_scam, result.reason_codes) Act on the result
Copy-paste code that turns the answer into a decision. It runs with or without BEWARY_API_KEY. TypeScript uses bewary.d.ts.
import os, requests
def is_datacenter(ip):
key = os.environ["BEWARY_API_KEY"] # a free key: bewary.app/account
headers = {"Authorization": f"Bearer {key}"}
r = requests.get(f"https://api.bewary.app/v1/ip/{ip}", headers=headers, timeout=10)
r.raise_for_status()
return r.json()["entities"][0]["facts"]["hosting_type"] in ("datacenter", "cdn")
print(is_datacenter("8.8.8.8")) # True import type { Envelope } from "./bewary"
export async function isDatacenter(ip: string): Promise<boolean> {
const key = process.env.BEWARY_API_KEY! // a free key: bewary.app/account
const headers: Record<string, string> = { Authorization: `Bearer ${key}` }
const res = await fetch(`https://api.bewary.app/v1/ip/${ip}`, { headers, signal: AbortSignal.timeout(10_000) })
if (!res.ok) throw new Error(`Bewary ${res.status}`)
const e = (await res.json() as Envelope).entities[0]
return e.type === "ip" && (e.facts.hosting_type === "datacenter" || e.facts.hosting_type === "cdn")
} Response
The fields you will use
entities[0].facts.hosting_type- datacenter, cdn, residential, mobile, business or unknown.
entities[0].facts.as_org- The network owner.
entities[0].facts.asn- The autonomous system number.
entities[0].facts.country- Registry country of the address block.
{ "schema_version": "1.7.0", "request_id": "req_01M69WHH116VSWFG4NPD6WSKBG", "checked_at": "2026-10-31T21:06:10Z", "engine": "bewary-engine/3.0.0", "models": { "calibration": "interim-band-v0" }, "input": { "kind": "lookup", "channel": "unknown", "language": null, "region": null }, "verdict": "unknown", "p_scam": 0.2, "risk_score": 20, "confidence": 0, "classification": null, "reason_codes": [], "entities": [ { "id": "e1", "type": "ip", "value": "1.1.1.1", "normalized": "1.1.1.1", "role": "mentioned", "verdict": "unknown", "p_scam": 0.2, "risk_score": 20, "reason_codes": [], "facts": { "version": 4, "asn": 13335, "as_org": "CLOUDFLARENET - Cloudflare, Inc., US", "country": "AU", "hosting_type": "cdn", "reverse_dns": "one.one.one.one", "listed": null }, "related": [], "datasets": [ { "dataset": "ip_network", "coverage": "WW", "in_coverage": true }, { "dataset": "threat_feeds", "coverage": "WW", "in_coverage": true }, { "dataset": "community_reports", "coverage": "WW", "in_coverage": true } ] } ], "signals": [], "degraded": false, "degraded_reasons": [], "cached": false, "explanation": null} Real responses, captured from the live API on 2026-10-31. Every response has this shape: see Response format.
IP address facts
Every key is in every response, null when unknown. A key marked Coming stays null until its source ships, so you can code against it now.
| Key | Type | Meaning |
|---|---|---|
version | 4 | 6 or null | IP version. |
asn | integer or null | The autonomous system number. |
as_org | string or null | The network owner. |
country | ISO code or null | The registry country of the address block. |
hosting_type | datacenter | cdn | residential | mobile | business | unknown or null | What kind of network it is, estimated from the network owner. |
reverse_dns | string or null | The PTR name. |
listed | boolean or null | IPs are not put on the scam list, because many sites share one address. |
Parameters
| Name | Type | Description |
|---|---|---|
ippath, required | string | An IPv4 or IPv6 address. |
Questions
Can I tell if an IP is a datacenter or a home connection?
Yes. facts.hosting_type is datacenter, cdn, residential, mobile, business or unknown, estimated from the network owner's name.
Does it detect VPNs and proxies?
No. It reports the network and its type, not whether a VPN or proxy is in use.
Why does 1.1.1.1 show country AU?
country is the registry country of the address block, which is not always where the server stands. 1.1.1.1 was allocated through the Asia-Pacific registry.
Used for
- Telling datacenter traffic from home users.
- Checking where a link or sender is hosted.
- Enriching logs with the network owner.
Reason codes it can return
| Code | Meaning | Severity |
|---|---|---|
LISTED_BLOCKLIST | On scam blocklist | Critical |
IP_LISTED | An address on a confirmed abuse list | High |
IP_DATACENTER_FOR_SENDER | A sender hosted in a datacenter, not a home or business network | Low |
LISTED_CAUTION | On a broad threat list that also holds spam and malware hosts | Medium |
PLATFORM_TAKEDOWN | The hosting platform took it down | High |
PLATFORM_WARNING | The platform shows a phishing or malware warning in front of it | High |
Reserved codes are in the contract, not emitted yet. All reason codes
Updated · This page as Markdown · Changelog