2026-05-20 · distribution network workarounds

When your ISP blocks a sportsbook API: the URL-classifier problem and how to work around it

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.

What's actually happening

Major US residential ISPs run optional or default-on DNS filtering services under various brand names:

ISPService nameDefault state
Spectrum / CharterSecurity ShieldOften on for new installs
AT&T FiberActiveArmor / Smart Home ManagerOpt-in but heavily promoted
CoxCox Security SuiteBundled with router
XfinityxFi Advanced SecurityDefault on for xFi customers
Verizon FiosVerizon Home Network ProtectionOpt-in
T-Mobile Home InternetWeb GuardDefault 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.

How to recognize it

Three smoking-gun symptoms:

  1. Response body is HTML where you expected JSON. Specifically starts with <!doctype html> or <html> with a title like "Suspicious Site Blocked," "Web Guard," "Site Restricted," or similar.
  2. Works on cellular, fails on WiFi. Cellular traffic doesn't go through the home router. If your client works fine on a hotspot but breaks on the ISP's WiFi, it's almost certainly a DNS or proxy filter.
  3. Works on some networks, fails on others, with no pattern to time-of-day. Outages have temporal patterns; ISP filters are deterministic per network.

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.

Why "parlay" gets flagged

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.

Our position: we are a B2B API. We don't take wagers, don't accept money, don't serve content to consumers. We're closer to a market-data vendor than a sportsbook. The classification is wrong in our case and we're working through reclassification requests with the vendors. In the meantime: workarounds.

Client-side workarounds, ranked by ease

1. Change your device's DNS (30 seconds)

Set your device's DNS to a public resolver that doesn't filter:

ProviderIPv4DoT hostname
Cloudflare1.1.1.1one.one.one.one
Google8.8.8.8dns.google
Quad99.9.9.9dns.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.

2. Tailscale exit node (5 minutes)

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.

3. VPN service (10 seconds, $5-10/month)

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.

4. Operator-managed mirror domain (your last resort)

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.

Server-side things we're doing

On our side, we're working a few angles in parallel:

What we're NOT doing: bot-detection evasion, captive-portal injection workarounds, anything that looks like circumventing legitimate network security. We're working within the rules to get reclassified correctly. The workarounds above are end-user choices about how to route their own traffic; we don't try to evade their ISP for them.

Concrete steps if you're hitting this right now

  1. Visit parlay-api.com/diag. If the page loads but tests fail with HTML responses, you've confirmed it's a filter, not an outage.
  2. Change your device's DNS to 1.1.1.1 per the table above.
  3. Retry. If now passing, you're done.
  4. If still failing, switch to a VPN or Tailscale exit node.
  5. If you operate enough of a customer base that this is a real product problem, email support and we'll discuss a tenant-specific subdomain.

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.

All posts · /diag connectivity test · /status live status