VPN API
You need to reliably tell whether an IP is coming from a VPN before you allow a login, start a checkout, or open a high-risk workflow. By the end of this guide you will integrate a single HTTP call that reads security.is_vpn from an IP geolocation endpoint, learn when to cache the result, and ship production checks that fail closed when the API is unreachable.
What you will build: a single-call VPN check with IP context
This guide centers on one field—security.is_vpn—returned by an IP lookup at the path /api/ip. The same response gives you geolocation and network context (country, region, city, ISP, ASN string) that you can log for audit or use in additional rules. You will get a copy-pasteable cURL, the official JSON fixture, and one language sample that parses the fields you actually need.
Authentication uses an Authorization: Bearer token. For quick starts, there is a trial (7 days or 50 requests), and the Basic plan is $29.99/mo. You can create your key with the Register link when you are ready.
Endpoint and authentication
All lookups use a GET request to the same path and pass the IP address as a query parameter:
- Path: /api/ip
- Query parameter: ip
- Auth: Authorization: Bearer YOUR_API_KEY
Hostnames:
- Public API host: ipxapi.com
- MCP: MCP (ipxapi.com/mcp 404s)
Live responses can change, but the sample below is the official product fixture for a documented IP and is safe to use for wiring up your parser.
Official cURL and response
Run this call exactly as shown to inspect the fixture and verify your auth and parsing logic. Replace only the token with your own when you go live.
curl "https://ipxapi.com/api/ip?ip=148.105.12.120" -H "Accept: application/json" -H "Authorization: Bearer YOUR_KEY"
Official JSON for the same IP (copy/paste the structure when writing your parser):
{
"status": "success",
"country": "United States",
"countryCode": "US",
"region": "US-CA",
"regionName": "California",
"city": "Mountain View",
"zip": "94043",
"lat": 37.40599,
"lon": -122.0786,
"timezone": "America/Los_Angeles",
"isp": "MailChimp",
"org": "MailChimp",
"as": "AS14782 MailChimp",
"query": "148.105.12.120",
"inEU": false,
"continentCode": "NA",
"security": {
"is_proxy": false,
"is_vpn": false,
"is_cloud_provider": true
}
}
What you will actually use in code:
- security.is_vpn (boolean): true if the IP is detected as a VPN exit; false otherwise.
- query (string): the IP you asked about; helpful for logging and cache keys.
- timezone (string, Olson/IANA): relevant for risk scoring when comparing user-declared time zones.
- country, region, regionName, city: human-readable geolocation context for audits and rules.
- as, isp, org: network owner strings you may want to allowlist or flag.
Minimal integration: parse security.is_vpn and fail safely
In most applications you only need a yes/no on VPN usage and some context to store alongside the session. Use a short timeout, treat timeouts and non-200s as high risk, and cache by IP to limit duplicate lookups per request burst.
JavaScript example (Node.js with fetch)
import fetch from "node-fetch";
async function checkVpn(ip) {
const url = `https://ipxapi.com/api/ip?ip=${encodeURIComponent(ip)}`;
const res = await fetch(url, {
headers: {
"Accept": "application/json",
"Authorization": "Bearer YOUR_API_KEY"
},
// keep the end-to-end latency tight for auth flows
timeout: 3000
});
if (!res.ok) {
// Fail safe: treat unknowns as high risk or trigger step-up auth
return { allowed: false, reason: `lookup_failed_${res.status}` };
}
const data = await res.json();
// Defensive access: if security is missing, treat as unknown/high risk
const isVpn = data && data.security && data.security.is_vpn === true;
// Persist context you may need later (do not block on logging)
const context = {
ip: data.query,
country: data.country,
region: data.region,
city: data.city,
timezone: data.timezone,
isp: data.isp,
asn: data.as,
proxy: data.security ? data.security.is_proxy : undefined,
vpn: isVpn,
cloud_provider: data.security ? data.security.is_cloud_provider : undefined
};
if (isVpn) {
return { allowed: false, reason: "vpn_detected", context };
}
return { allowed: true, reason: "no_vpn", context };
}
// Example usage:
checkVpn("148.105.12.120").then(result => {
console.log(result);
}).catch(err => {
// Network error / timeout handling
console.error("Lookup error", err);
});
Decision design: where the VPN check runs
Place the VPN check in the narrowest path that needs it (e.g., login, password reset, gift card redemption). For web apps, call the API server-side to avoid exposing your token. For mobile clients, proxy requests through your backend and enforce server-side rules.
- Block vs. step-up: If security.is_vpn is true, either block outright or require a second factor. Tie this to business risk.
- Cloud provider nuance: security.is_cloud_provider may be true for traffic from known clouds. Decide whether to treat those as VPN-like for your use case.
- Proxy signals: If security.is_proxy is true, you may apply the same policy as VPN.
Caching strategy and TTLs
Responses are stable enough for short-term caching. Consider these patterns:
- Edge cache key: ip.
- TTL: 5 to 15 minutes for typical auth flows; shorter if you need near-real-time updates.
- Background refresh: On cache hit close to expiry, refresh asynchronously so live requests don’t wait.
Do not cache errors indefinitely. Use a small negative-cache (e.g., 30–60 seconds) for transient failures to avoid thundering herds.
Error handling, timeouts, and fallbacks
Plan for temporary network failures. Keep your client timeout under your overall SLO for the guarded action (for example, 2–3 seconds in login). If the call fails:
- Treat as unknown and require step-up authentication, or
- Block high-risk flows if your policy is strict.
Always log the failure mode (HTTP status, timeout, parse error) along with the ip you attempted to check, and include the query field from the response when available for correlation.
Observability: what to log
Log only what you need for security decisions and audits. At minimum:
- ip (from response.query)
- security.is_vpn, security.is_proxy, security.is_cloud_provider
- country, region, city, timezone
- as, isp, org (text strings can help investigations)
- status and any error detail if the lookup failed
Avoid logging raw request headers or tokens. Redact Authorization values in all traces.
Testing and fixtures
Use the documented fixture to write deterministic tests for your parser and decision logic. The live service can change flags over time; the fixture lets you confirm your wiring stays correct:
- Fixture IP: 148.105.12.120
- Path: /api/ip
- Key target: security.is_vpn
In unit tests, emulate three scenarios: the official response, a version with security omitted, and a hard network timeout. Your code should correctly parse the field, treat missing fields as unknown/high risk if that’s your policy, and avoid crashing on unexpected shapes.
Rollout checklist
- Confirm Authorization: Bearer YOUR_API_KEY is present on every request.
- Set an explicit Accept: application/json header.
- Enforce a short client timeout consistent with your auth SLO.
- Implement a cache keyed by IP with a modest TTL.
- Define what happens on lookup failure and on security.is_vpn === true.
- Track allow/deny reason codes for analytics (e.g., vpn_detected, lookup_failed_408, no_vpn).
Pricing and account setup
You can start with a trial (7 days or 50 requests). The Basic plan is $29.99/mo when you are ready to move to production. Create your account and get an API key here: Register. For parameters and field definitions, refer to the Documentation. If your platform consumes managed control-plane endpoints, see MCP.
Security and privacy considerations
Store only what you need for risk evaluation and compliance. If you persist IP-derived data, document the retention period and purge schedule. Because timezone is an Olson/IANA string, you can compare it to user-declared timezones without additional conversion. Latitude and longitude are numeric values (degrees) and may be approximate; use them for coarse checks only, not precise geofencing.
If your organization requires allowlists, you can match by as or isp strings or by countryCode. Keep in mind that enterprise VPNs can egress from consumer ISPs and vice versa; rely on security.is_vpn for the primary decision and use other fields as secondary signals.
Common pitfalls and how to avoid them
- Parsing without null checks: Always guard accesses to security.* and other nested fields.
- Blocking on every page view: Only call the endpoint where a decision is needed; cache the rest.
- Leaking tokens: Do not embed your API key in client-side code; proxy via your backend.
- Assuming fixtures are “live”: The official fixture is for testing; do not treat it as the user’s IP.
Quick reference: fields you will check
- security.is_vpn: boolean. The primary VPN detection signal.
- security.is_proxy: boolean. Additional anonymity indication.
- security.is_cloud_provider: boolean. Traffic from cloud infrastructure.
- query: string. The IP address you looked up, used for logs and cache keys.
- country, region, regionName, city, timezone: Context for rules and audits.
- isp, org, as: Network ownership strings for allow/deny rules.
FAQ
How do I authenticate requests?
Send Authorization: Bearer YOUR_API_KEY and Accept: application/json on every request to /api/ip.
Which field indicates a VPN?
Use security.is_vpn. Treat true as VPN detected. Combine with security.is_proxy or security.is_cloud_provider if your policy needs broader coverage.
Can I cache results?
Yes. Cache by IP for 5–15 minutes for most auth flows. Refresh asynchronously near expiry. Never cache errors for long.
What happens if the API is down or times out?
Use a short timeout and fail safely: either step up authentication or temporarily block the risky action. Log the failure reason and the IP you attempted to check.
Where can I find parameters and field definitions?
See the Documentation for full details. The MCP endpoint is available at MCP.
Ready to wire up security.is_vpn and ship your VPN checks? Create your key and start the 7-day or 50-request trial, then move to Basic at $29.99/mo when you’re set. Register here: Register.
