Diagram of proxy detection api in python
Original illustration for Proxy API Review. Guide and sources checked October 23, 2026.

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.

  1. 1Validate the address
  2. 2Call the API
  3. 3Preserve unknown outcomes
A practical sequence for the question covered in this guide.

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.

Comparison of the methods and conditions discussed in this section.
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

Continue reading

Compare all ten tools or read the complete deployment guide.