Every IP address ProtonVPN exits and enters from, refreshed daily. Scraped from the Proton API by a GitHub Action, committed as plain files.
| File | Contents |
|---|---|
protonvpn_ips.txt, .json |
exit IPs, the address a site sees |
protonvpn_entry_ips.txt, .json |
entry IPs, the address a client connects to |
protonvpn_entry_ip_ranges.txt |
CIDRs of the ASNs behind the entry IPs |
protonvpn_subdomains.json |
server hostnames |
protonvpn_logicals.json |
the full API response, every field |
import json
with open("protonvpn_ips.json") as file:
exit_ips = set(json.load(file))
"84.247.50.181" in exit_ips # TrueRanges are CIDRs behind a # header:
import ipaddress
with open("protonvpn_entry_ip_ranges.txt") as file:
networks = [
ipaddress.ip_network(line.strip())
for line in file
if line.strip() and not line.startswith("#")
]
address = ipaddress.ip_address("146.70.120.210")
any(address in network for network in networks) # TrueThese are whole ASNs and overshoot, only shared ISP and CDN ones are trimmed to their entry IPs. Prefer the IP lists.
python main.py # logicals, exit IPs
python entry_ips.py # entry IPs, ASN ranges
The API needs a logged-in session. Take four cookies from account.protonvpn.com (F12, Web Storage, Cookies) into secrets:
| Secret | Cookie |
|---|---|
AUTH_PM_UID |
the {uid} in the AUTH-{uid} name |
AUTH_TOKEN |
value of AUTH-{uid} |
REFRESH_TOKEN |
value of REFRESH-{uid} |
SESSION_ID |
value of Session-Id |
A fifth, GH_TOKEN with Contents: Read/Write and Secrets: Read/Write, lets
the workflow write refreshed tokens back. Auth rotates every ~24 h by itself,
REFRESH_TOKEN dies after ~180 days. Free accounts see fewer servers than paid.
Fork it. Two files, stdlib only, nothing to install, point any daily cron at
main.py and entry_ips.py with your own four cookies. GitLab CI, Woodpecker,
sourcehut, a Raspberry Pi under a desk.
Running one? Open an issue and it gets linked here. Mirrors, rewrites, other sources, all welcome.