
Summarize this page with
A Python proxy detection integration should validate the address, keep its credential on the server, make a bounded API request and preserve errors as unavailable results. Parse the provider’s documented response version afterward. A failed lookup must not silently become is_proxy = false.
The transport is usually the easy part. The difficult failures happen when a private address is sent by mistake, a rate-limit response is parsed as a valid verdict, or a missing JSON field is converted into a negative classification.
- 1Validate the address
- 2Call the API
- 3Preserve unknown outcomes
Start with the address your infrastructure trusts
Obtain the client address through your framework’s documented trusted-proxy configuration. A request header supplied directly by an internet client is not a trustworthy source just because its name includes “Forwarded.” Your load balancer and origin topology determine which hop can supply the address.
Use Python’s ipaddress module to parse IPv4 and IPv6. It can reject malformed input and distinguish globally reachable addresses from local or special-purpose ranges. This validates address syntax and scope; it does not establish that an address belongs to the current visitor.
An IPv6 string can contain colons and equivalent textual representations. Normalize it before using it as a cache key. Do not shorten IPv6 handling to a regular expression written for dotted IPv4 addresses.
A small transport adapter
The example below uses the documented proxycheck.io v3 API as a concrete transport destination. It returns the payload without inventing a vendor-neutral fraud score. The function accepts only a globally reachable address and reads the credential from a server environment variable.
import ipaddress
import json
import os
from urllib.error import HTTPError, URLError
from urllib.parse import urlencode
from urllib.request import Request, urlopen
def lookup_proxy(address):
try:
ip = ipaddress.ip_address(address)
except ValueError:
return {"state": "invalid_input"}
if not ip.is_global:
return {"state": "unsupported_address"}
key = os.environ.get("PROXYCHECK_API_KEY")
if not key:
return {"state": "configuration_error"}
params = urlencode({"key": key, "ver": "24-June-2026"})
request = Request(
f"https://proxycheck.io/v3/{ip.compressed}?{params}",
headers={"Accept": "application/json"},
)
try:
with urlopen(request, timeout=3) as response:
payload = json.load(response)
except HTTPError as error:
return {"state": "unavailable", "http_status": error.code}
except (URLError, TimeoutError, ValueError):
return {"state": "unavailable"}
if not isinstance(payload, dict) or payload.get("status") != "ok":
return {"state": "unavailable"}
return {"state": "received", "ip": ip.compressed, "payload": payload}
received means the transport returned a successful payload. Validate the address-specific object, expected field types and required detections before treating it as an actionable result. Keep your schema adapter versioned separately from this transport function.
The three-second setting is a socket-operation timeout, not a hard deadline for every DNS, connection and read operation combined. A production request path needs an overall deadline in its execution environment. The latency guide covers that distinction.
Keep unknown values explicit
Build a small internal result model that separates receipt, classification and freshness. For example, an application-owned object might contain state, provider, checked_at, response_version, proxy_flag and confidence. Those are your adapter fields, not a promise that every vendor uses those names.
| Condition | Application treatment |
|---|---|
| Valid response and explicit negative flag | A completed negative classification for that field |
| Missing or null required field | Unknown or incomplete result |
| HTTP authentication error | Configuration or access failure |
| HTTP rate limit | Unavailable until the documented retry condition |
| Invalid JSON or unexpected structure | Contract failure requiring investigation |
Avoid bool(value) for provider fields. A nonempty string containing "false" is truthy in Python. Stable proxycheck.io v3 uses boolean detection values, while older integrations may use other representations. Validate the version and types deliberately.
Protect credentials and operational logs
The API key in this transport is part of the request URL. That makes URL redaction in application logs, error tracking and outbound observability a requirement. Do not log the complete Request object or exception string if it may contain a credential.
Use a test account or approved allowance and addresses you are authorized to assess. A loop over a large CSV can consume many lookups even when sent as one batch. Check the documented credit unit and rate limits before running it.
For production, preserve a minimal request reference and timing record. Keep raw payloads only as long as your operational and privacy requirements justify. Sensitive customer data should not appear in a debugging fixture or public issue.
Know when an IP lookup is only one part
A backend IP check evaluates an address. If your use case needs current browser evidence, device identity and account-abuse detection, follow the product’s supported collection model. ShieldLabs provides that device/network/account context through SDKs, API and webhooks.
Do not represent a Python IP-only adapter as a full implementation of a browser-assisted product. Use the API overview to choose the interface, then compare current access and evaluate on permitted traffic.
FAQ
Should I read X-Forwarded-For directly in Python?
Use the framework and infrastructure’s trusted-proxy configuration. A client can supply untrusted forwarding headers. The correct address depends on the proxy chain and whether direct access to the origin is prevented.
Should a timeout return false?
No. Return an unavailable state and apply the application’s explicit failure behavior. A missing result does not establish whether a connection is a proxy.
Can I cache by IP address?
An IP-only result can be cached within its permitted scope and freshness policy. Include provider, response version and relevant request options in the key. Do not share a visitor or account assessment across unrelated users behind that address.
Sources
- Python ipaddress, address parsing and properties.
- Python urllib.request, request and timeout behavior.
- proxycheck.io API documentation, v3 interface and error handling.
- MDN X-Forwarded-For, address trust boundaries.
Continue reading
- Proxy detection in JavaScript: browser and backend
- How to test residential proxy detection APIs fairly
Compare all ten tools or read the complete deployment guide.