Show each catalog only where it's licensed
Rights are sold by territory, and a VPN lets a viewer pick theirs. Look up the address when playback starts, and give VPN viewers the catalog of the country they pay in, or ask them to turn it off.
- VPNNordVPNHome catalogLive match203.0.113.30
- ISPHome broadbandPlayingSeries, S2 E4198.51.100.61
- HostingHetznerBlockedFilm192.0.2.19
- RelayiCloud Private RelayPlayingLive match203.0.113.77
What it stops
Territory hopping
Viewers on a VPN reaching a catalog licensed for another country.
Caught byis_vpnlocation.countryBlackouts
Live events watched through a VPN from inside a blackout area.
Caught byis_vpnis_resproxyPrice arbitrage
Subscriptions bought through a VPN in a country with a lower price.
Caught byis_vpnlocation.countryRestreams and scrapers
Streams pulled from hosting servers to rebroadcast or scrape.
Caught byis_hostingis_dcproxy
The fields that decide it
The four a rule here reads most, each marked with the plan that answers it.
location.countryThe country the address is in, to pick the catalog.Unlimitedis_vpnA commercial VPN, whose country isn't the viewer's.Unlimitedis_relayA privacy relay such as iCloud Private Relay, which keeps the viewer's country.Unlimitedis_hostingA hosting server, where restreams and scrapers run.Paid plans
Where to check
- 01
When playback starts
Look up the address before the stream is served. A VPN viewer gets the catalog of the country they pay in, or a note to turn the VPN off, and an ordinary address plays as normal.
- 02
At checkout
Check where a subscription is bought, since that's where a cheaper country's price is taken through a VPN. Charge the price of the card's country instead.
- 03
On a new device
Check when an account signs in somewhere new. A sign-in from a hosting server that isn't a relay is a script, not a viewer, and can be refused outright.
Start free, then add every field
The same rule runs with no key and with one. Without a key the paid fields are left out of the answer, so the second check waits for your upgrade.
- 1
Call it with no key
UnlimitedThe snippet runs as it is: the VPN, Tor and relay flags, the location and the network, with no daily or monthly cap.
- 2
Sign up for a key
No cardYour account's first 100 lookups each day answer every field, so you can try the paid half of the rule on real traffic.
- 3
Upgrade for every lookup
From $19 a monthEvery paid plan answers every field on every lookup, proxies, hosting and the operator behind each flag, and up to 1,000 addresses a batch.
import { VPNDetection } from "vpndetection";// No key needed. With one, every field answers.const client = new VPNDetection({ apiKey: process.env.VPNDETECTION_API_KEY });export async function catalogFor(viewer) {const r = await client.lookup(viewer.ip);// Proxies and hosting answer on a paid plan.if (r.isHosting && !r.isRelay) {return refuse(viewer);}if (r.isVpn) {return homeCatalog(viewer.billingCountry);}}
Common questions
Should a VPN viewer be blocked?
You can serve the catalog of the country they pay in instead, or ask them to turn the VPN off. A privacy relay such as iCloud Private Relay keeps the viewer's country, and comes back as is_relay, apart from VPNs.
Will a lookup delay playback?
A check is a single GET when playback starts, and the official clients keep each answer for an hour by default, so the next episode costs no extra lookup.
Can it tell which VPN a viewer uses?
On every paid plan, yes: vpn.provider names the service as a stable ID, with a confidence and the last day we saw the address on it.
Does it cover IPv6?
Yes, on the same endpoint as IPv4, and every dataset holds IPv6 addresses.
What does it cost?
The VPN flag and the country are free with no cap. Every paid plan answers every field, from Starter at $19 a month for 250,000 lookups to Max at $199 for 5,000,000, and Enterprise is priced to your volume.
Unlimited lookups, no sign-up.
Paste it into a terminal. Any address answers its VPN, Tor and relay flags, where it is and which network announces it, with no daily or monthly cap.
{"ip": "146.70.116.130","is_vpn": true,"is_relay": false,"is_tor": false,"location": {"country": "AT", ...},"asn": { "asn": 9009, ... }}