One of our nodes recently went silent for 15 hours. We diagnosed it as a Termux app-data reset on the phone hosting it. Easy fix: re-install the runtime. Standard curl ... | sh from inside Termux, except the curl downloaded an HTML page titled "Suspicious Site Blocked" instead of our shell script. The network was Spectrum residential WiFi, and Spectrum's "Security Shield" service had classified parlay-api.com as suspicious.
This is a real product problem we're seeing more of, not just an internal one. Customers hit it too. This post is the symptom-recognition guide + workaround menu.
Major US residential ISPs run optional or default-on DNS filtering services under various brand names:
| ISP | Service name | Default state |
|---|---|---|
| Spectrum / Charter | Security Shield | Often on for new installs |
| AT&T Fiber | ActiveArmor / Smart Home Manager | Opt-in but heavily promoted |
| Cox | Cox Security Suite | Bundled with router |
| Xfinity | xFi Advanced Security | Default on for xFi customers |
| Verizon Fios | Verizon Home Network Protection | Opt-in |
| T-Mobile Home Internet | Web Guard | Default off; can be enabled in the app |
All of them work the same way: the ISP's DNS resolver returns a special IP (their warning-page server) for any domain classified as suspicious, adult, gambling, or untrusted. Customer's device makes a TLS connection to that IP. The HTTPS handshake completes with the ISP's certificate. The body returned is an HTML page that says "this site was blocked because it may contain unsafe content."
For an API endpoint, this is fatal. Your client expects JSON; it gets HTML. Your parser blows up. Your application logs "weird response shape" and doesn't know that the network mangled it.
Three smoking-gun symptoms:
<!doctype html> or <html> with a title like "Suspicious Site Blocked," "Web Guard," "Site Restricted," or similar.Confirm in 5 seconds at /diag. If you can load that page but the test suite shows non-JSON bodies, you've got a filter in the path.
URL classification vendors (Webroot BrightCloud, Cisco Talos Intelligence, Symantec, etc.) build their lists from a mix of crawled content, lexicon heuristics, and customer reports. Domains containing "parlay," "bet," "odds," "sportsbook," or related betting vocabulary often get auto-flagged as gambling, which the ISP then groups with adult content under "filter unsafe categories." We've been classified by some vendors and not others; the ones that flag us are the ones whose lists feed into the residential ISP filters.
Set your device's DNS to a public resolver that doesn't filter:
| Provider | IPv4 | DoT hostname |
|---|---|---|
| Cloudflare | 1.1.1.1 | one.one.one.one |
8.8.8.8 | dns.google | |
| Quad9 | 9.9.9.9 | dns.quad9.net |
On Android: Settings → Network → Private DNS → hostname → one.one.one.one. On macOS: Network preferences → Advanced → DNS → add 1.1.1.1. On Linux: /etc/resolv.conf or your network manager.
Fixes 80% of cases. If the ISP is doing pure DNS-based filtering, the alternative resolver returns our real IP and TLS connects through cleanly. If the ISP is also doing TLS-SNI inspection (less common in residential), this won't help.
If you have any other machine on the internet (cloud VM, home server, friend's box), set it up as a Tailscale node and configure it as an exit node:
# On the exit-node machine:
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up --advertise-exit-node
# In the Tailscale admin panel:
# approve the machine as exit node
# On your client device (Pixel, Mac, etc.):
# Tailscale app -> Exit Nodes -> pick the machine
All traffic from your client device now exits via that other machine. The ISP sees encrypted Tailscale traffic to a single IP and can't classify the destination domain. Completely transparent on the application side.
Any commercial VPN works. Mullvad, ProtonVPN, NordVPN, IVPN, etc. Toggle on, connect to any server, the ISP filter doesn't see your real traffic. Same logic as a Tailscale exit node but you don't have to operate the exit machine.
If you're integrating an unfiltered API into a customer-facing product where you can't control the customer's network, the standard fix is to proxy requests through a domain YOU control that the customer's network doesn't classify. api.your-company.com reverse-proxies to parlay-api.com, and the customer's ISP sees a domain it doesn't have an opinion on. Operationally heavy (you're now in the network path) but works in the long tail.
On our side, we're working a few angles in parallel:
api2.parlay-api.com (planned) with independent DNS records that haven't been classified yet, as a fallback DNS.1.1.1.1 per the table above.The pattern of "JSON API returns HTML" is one of the more confusing failure modes in client integration because nothing about the error log obviously points at "your ISP is filtering domains." We hope this post makes it 30-second-recognizable instead of 4-hour-debug-session for the next person.