Skip to content
VPNDetection
For AI agents

The whole contract, on one page.

If you're an autonomous client, this page is for you: the one GET, the exact response shape, what an absent field means next to an empty one, and the two things a 429 can mean. You won't need another page to act on it.

The same facts are at /llms.txt as plain text, and the full reference is at docs.vpndetection.io.

One call, one address

The path is the address. IPv4 and IPv6 both, on the same endpoint. The key is a bearer token; without one you still get an answer, just a narrower one.

Request
curl "https://api.vpndetection.io/146.70.116.130" \
-H "Authorization: Bearer $VPNDETECTION_API_KEY"
EndpointGET https://api.vpndetection.io/{ip}
AuthAuthorization: Bearer <key>
Content typeapplication/json, pretty-printed
Success200 with the body below
Bad address400
Key problem401
Source not allowed403
Limit or allowance429, read Retry-After

What comes back

Nine independent flags and, for each, a detail object carrying the evidence, then where the address is and which network announces it. Every paid plan answers this; without a key or on Free, the fields that answer leaves out are omitted rather than nulled.

200 application/json
{
"ip": "146.70.116.130",
"is_vpn": true,
"is_hosting": true,
"is_relay": false,
"is_tor": false,
"is_cdn": false,
"is_resproxy": false,
"is_dcproxy": false,
"is_mobproxy": false,
"is_pubproxy": false,
"vpn": {
"provider": "mullvad",
"last_seen": "2026-09-02",
"confidence": "high"
},
"hosting": { "provider": "m247", "confidence": "high", "last_seen": "2026-09-02" },
"relay": {},
"tor": {},
"cdn": {},
"resproxy": {},
"dcproxy": {},
"mobproxy": {},
"pubproxy": {},
"location": {
"continent": "EU",
"continent_name": "Europe",
"country": "AT",
"country_name": "Austria",
"timezone": "Europe/Vienna",
"currency_code": "EUR",
"currency_name": "Euro",
"currency_symbol": "€",
"flag_emoji": "🇦🇹",
"flag_unicode": "U+1F1E6 U+1F1F9"
},
"asn": { "asn": 9009, "name": "M247 Europe SRL", "domain": "m247global.com", "type": "hosting", "prefix": "146.70.116.0/24" }
}
is_vpn
A commercial VPN exit. The operator is named in the vpn object.
is_hosting
Leased hosting or cloud space. Infrastructure, not anonymity on its own.
is_tor
A Tor node, from the public Tor set, rebuilt daily.
is_relay
A privacy relay, such as iCloud Private Relay, or an enterprise web gateway, such as Zscaler.
is_cdn
A content-delivery edge. Grouped with hosting, not with anonymity.
is_resproxy
Seen in a residential proxy pool over a rolling 90-day window.
is_dcproxy
Seen in a datacenter proxy pool over the same window.
is_mobproxy
Seen in a mobile proxy pool riding carrier-assigned space.
is_pubproxy
A public proxy anyone can connect through listens on the address or sends its traffic out from it, over the same window.
location.continent, continent_name
The continent, as a two-letter code and a name.
location.country, country_name
The ISO 3166 country, as a code and a name.
location.timezone
The address's IANA timezone.
location.currency_code, currency_name, currency_symbol
The country's currency.
location.flag_emoji, flag_unicode
The country's flag, and its code points.
asn.asn
The AS number, 0 when no announcement covers the address.
asn.name
Who runs the AS.
asn.domain
The AS operator's own domain.
asn.type
The kind of network: isp, hosting, education, government or business.
asn.prefix
The announced prefix that covers the address, the most specific when several do.

Seven things that will bite you otherwise

Each of these is a place where the obvious reading of the response is the wrong one.

01Absent is unknown, never false

A field your plan doesn't include is omitted from the response. Don't read a missing key as a negative answer; read it as a question you didn't buy an answer to.

02An empty object is a checked answer

A flag that is present always answers. Its detail object comes back as {} when the dataset was queried and nothing matched: "we looked", not "we don't know".

03Answer bogons before you call

Private, loopback, link-local, documentation and multicast ranges, including their 6to4 and Teredo forms, answer 200 with every flag false. Every official SDK answers them without a request. Sent to the API directly, they count against a keyless or Free plan allowance and cost nothing on a paid plan. Every range is listed at /bogon.

04Retry-After separates the two 429s

Present means a transient rate limit and retrying works. Absent means a monthly allowance is spent and retrying won't help. Branch on the header, not on the status.

05A 5xx is ours

Retry with exponential backoff and jitter. The official clients already do.

06Provider ids are stable

A provider id doesn't change between builds, so a rule keyed on one keeps working as that operator's ranges rotate. Join on the id, not on a display name.

07Classifications are probabilistic

They are inferences from measurement, published operator data and public registration records. They can be wrong or go stale. Don't make them the sole basis for denying a person access to a service.

What a plan lets you see

What an answer carries depends on the key, and every paid plan answers the same: the plans differ in lookups alone. Full detail is on the pricing page.

No key
ip and is_vpn, under a small daily allowance.
Free
Adds Tor and relay, location and asn, at 50,000 requests a month.
Starter, Scale and Max
Every field, the same on each: they differ only in how many requests they include.

When you need more than one answer

Don't loop the API over a list you already hold. Use one of these instead.

Send them in one call

POST https://api.vpndetection.io/batch answers up to 1,000 addresses at once, each costing what its single lookup costs, keyed by the string you sent. See the batch call.

Take the dataset

Every classification ships as a downloadable build, CSVGZ and where published MMDB, with checksums. Poll its metadata and fetch again only when it reports a newer build date. See the database pages.

Read the address space

Aggregates per block are public and need no key: every /8 down to /16 and /24, with how much of each block carries which signal. Individual address pages exist but are noindex and cost a live look-up, so crawl the blocks, not the addresses.

00 · No key

Start without signing up.

Paste it into a terminal. No account needed. 1,000 lookups a day per address, answering ip and is_vpn.

curl "https://api.vpndetection.io/146.70.116.130"
{
"ip": "146.70.116.130",
"is_vpn": true
}
01 · Free key

Sign up and get 50,000 requests a month.

No card and no sales call: the key is issued on signup.
Every request flags VPN IPs.
Upgrade only when you outgrow the allowance.