# Rotating residentiële proxy's in Python: requests, httpx en aiohttp

> Rotating residentiële proxy's in Python: requests met retries, httpx sync en async, aiohttp, sticky sessies en aantal bytes. Syntax-gecontroleerde code.

- URL: https://crawlrelay.com/nl/docs/python/
- Laatst bijgewerkt: 2026-10-13
- Publisher: CrawlRelay (https://crawlrelay.com)

Productiepatronen, geen one-liners: retries die Retry-After respecteren, een nieuw IP per verzoek of één IP per flow, per-host concurrency in asyncio, en de bytes die elke pagina werkelijk kost.

**Kort antwoord — Hoe gebruik ik rotating residentiële proxy's in Python?**

Geef `http://USERNAME:PASSWORD@gw.crawlrelay.com:7777` door als proxy voor zowel `http` als `https` in requests, als `proxy=` in httpx 0.26+, of als `proxy=` per verzoek in aiohttp. **CrawlRelay** residentiële proxy's roteren bij elke nieuwe verbinding en blijven sticky met `-session-<id>` in de gebruikersnaam. Ze kosten US$ 1,50 voor 1 GB en US$ 0,50/GB vanaf 50 GB. Betaald in crypto, zonder KYC.

## Python setup in één oogopslag

| Attribuut | Waarde |
| --- | --- |
| requests | `proxies={"http": url, "https": url}`; retries via urllib3 `Retry` |
| httpx | `httpx.Client(proxy=url)`; het oude `proxies=` argument is verwijderd in httpx 0.28 |
| aiohttp | `session.get(url, proxy="http://USER:PASS@host:port")`, inloggegevens in de proxy URL |
| SOCKS5 | `pip install "requests[socks]"` of `httpx[socks]`, daarna `socks5h://` op poort 7778 |
| Nieuw IP per verzoek | Schakel keep-alive uit: `Connection: close`, `max_keepalive_connections=0` of `force_close=True` |
| Zelfde IP voor een flow | `-session-<id>-ttl-<minutes>` in de gebruikersnaam, tot 30 minuten |
| Proxy URL schema | Altijd `http://` voor de HTTP-poort, ook voor HTTPS-websites |

## Hoe gebruik ik een proxy met requests, met retries die Retry-After respecteren?

Monteer een `HTTPAdapter` met urllib3's `Retry` op een `Session`. Het herhaalt verbindingsfouten en de statussen die je lijst, wacht met exponentiële backoff, en respecteert de `Retry-After`-header die sites sturen bij 429 en 503. Stel `trust_env = False` in zodat een overgebleven `HTTPS_PROXY` variabele je proxy niet kan overschrijven.

*requests + urllib3 Retry*

```python
import os

import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

USER = os.environ.get("CRAWLRELAY_USER", "USERNAME-country-us")
PASSWORD = os.environ.get("CRAWLRELAY_PASSWORD", "PASSWORD")
PROXY = "http://%s:%s@gw.crawlrelay.com:7777" % (USER, PASSWORD)

retry = Retry(
    total=4,
    connect=2,
    backoff_factor=1.0,  # waits of 0 s, 2 s, 4 s, 8 s (Retry-After wins on 429/503)
    status_forcelist=(429, 502, 503, 504),
    allowed_methods=frozenset({"GET", "HEAD"}),
    respect_retry_after_header=True,
)

session = requests.Session()
session.trust_env = False  # ignore HTTP(S)_PROXY from the environment
session.mount("http://", HTTPAdapter(max_retries=retry))
session.mount("https://", HTTPAdapter(max_retries=retry))
session.proxies = {"http": PROXY, "https": PROXY}
session.headers["User-Agent"] = "ExampleCrawler/1.0 (+https://example.com/crawler-info)"
session.headers["Connection"] = "close"  # new connection per request, so usually a new exit IP

r = session.get("https://api.ipify.org", timeout=(10, 30))
print(r.status_code, r.text)
```

## Hoe bouw ik veilige gerichte proxy-URL's?

Genereer de gebruikersnaam in één functie en valideer deze. Een typfout in een landcode of een niet-geëncodeerd teken in het wachtwoord veroorzaakt een [407](https://crawlrelay.com/nl/docs/errors/407-proxy-authentication-required/) die eruitziet als een inlogprobleem. De helper hieronder houdt de gedocumenteerde volgorde aan (`USERNAME-country-{cc}-state-{state}-city-{city}-zip-{zip}-asn-{asn}-session-{id}-ttl-{minutes}`), weigert onbekende sleutels en URL-encodeert beide inloggegevens.

*Gebruikersnaam bouwer en proxy URL*

```python
import re
from urllib.parse import quote

ORDER = ("country", "state", "city", "zip", "asn", "session", "ttl")
VALUE = re.compile(r"^[a-z0-9_]+$")


def build_username(base, **params):
    """build_username("USERNAME", country="us", city="new_york", session="job1", ttl=30)"""
    parts = [base]
    for key in ORDER:
        value = params.pop(key, None)
        if value is None:
            continue
        value = str(value).lower()
        if not VALUE.match(value):
            raise ValueError("bad value for %s: %r" % (key, value))
        if key == "ttl" and not 1 <= int(value) <= 30:
            raise ValueError("ttl must be 1 to 30 minutes")
        parts.append("%s-%s" % (key, value))
    if params:
        raise ValueError("unknown parameters: %s" % ", ".join(sorted(params)))
    return "-".join(parts)


def proxy_url(username, password, socks=False):
    scheme, port = ("socks5h", 7778) if socks else ("http", 7777)
    return "%s://%s:%s@gw.crawlrelay.com:%d" % (
        scheme, quote(username, safe=""), quote(password, safe=""), port)


print(proxy_url(build_username("USERNAME", country="us", state="ny", city="new_york", session="job1", ttl=20), "p@ss:word"))
```

## Hoe krijg ik een nieuw IP bij elk verzoek met httpx?

Schakel keep-alive uit. httpx poolt standaard verbindingen, en een hergebruikte verbinding behoudt zijn exit IP. Met `max_keepalive_connections=0` opent elk verzoek een nieuwe verbinding naar de gateway en verlaat die meestal via een andere residentiële IP. `num_bytes_downloaded` geeft de ontvangen bytes op de lijn, wat het dichtst bij de verkeersafrekening ligt.

*httpx: roterende client*

```python
import httpx

PROXY = "http://USERNAME-country-gb:PASSWORD@gw.crawlrelay.com:7777"

# No keep-alive: every request opens a new connection, so it usually gets a new exit IP
with httpx.Client(
    proxy=PROXY,
    timeout=httpx.Timeout(30.0, connect=10.0),
    limits=httpx.Limits(max_keepalive_connections=0),
    follow_redirects=True,
) as client:
    for _ in range(3):
        r = client.get("https://api.ipify.org")
        print(r.text, r.num_bytes_downloaded, "bytes on the wire")
```

## Hoe scrape ik gelijktijdig met asyncio zonder één site te overladen?

Beperk concurrency **per host**, niet alleen globaal. Een globale limiet van 50 laat alle 50 verzoeken dezelfde site raken als je URL-lijst op domein is gesorteerd. Een semaphore per hostname houdt elke site op een beleefd niveau terwijl de crawl als geheel snel blijft. Begin met 2 tot 4 per host en lees de [concurrency gids](https://crawlrelay.com/nl/docs/concurrency/) voordat je verhoogt.

*httpx.AsyncClient met een semaphore per host*

```python
import asyncio
from collections import defaultdict
from urllib.parse import urlsplit

import httpx

PROXY = "http://USERNAME-country-us:PASSWORD@gw.crawlrelay.com:7777"
PER_HOST = 4  # parallel requests per target host
host_slots = defaultdict(lambda: asyncio.Semaphore(PER_HOST))


async def fetch(client, url):
    async with host_slots[urlsplit(url).hostname]:
        try:
            r = await client.get(url)
            return url, r.status_code, r.num_bytes_downloaded
        except httpx.HTTPError as exc:
            return url, type(exc).__name__, 0


async def main(urls):
    limits = httpx.Limits(max_connections=50, max_keepalive_connections=0)
    timeout = httpx.Timeout(30.0, connect=10.0)
    async with httpx.AsyncClient(proxy=PROXY, limits=limits, timeout=timeout) as client:
        for result in await asyncio.gather(*(fetch(client, u) for u in urls)):
            print(*result)


asyncio.run(main(["https://example.com/", "https://example.org/", "https://example.net/"]))
```

## Hoe gebruik ik een proxy met aiohttp?

Geef de gateway door als `proxy=` bij elk verzoek, met de inloggegevens in de proxy URL. Dat werkt zonder waarschuwingen bij elke aiohttp 3.x release; `BasicAuth` en de `proxy_auth=` parameter zijn verouderd sinds aiohttp 3.14 en worden verwijderd in 4.0. Een `TCPConnector(force_close=True)` sluit elke verbinding na de respons, zodat elk verzoek meestal een nieuwe exit krijgt. Zonder dit hergebruikt aiohttp verbindingen en behoud je tijdelijk hetzelfde IP.

*aiohttp met inloggegevens in de proxy URL*

```python
import asyncio

import aiohttp

# Credentials go in the proxy URL; percent-encode a password that contains @ or :
PROXY = "http://USERNAME-country-fr:PASSWORD@gw.crawlrelay.com:7777"


async def main():
    # force_close=True: one connection per request, so each request usually gets a new exit IP
    connector = aiohttp.TCPConnector(limit=20, force_close=True)
    timeout = aiohttp.ClientTimeout(total=60, sock_connect=10)
    async with aiohttp.ClientSession(connector=connector, timeout=timeout) as session:
        for _ in range(3):
            async with session.get("https://api.ipify.org", proxy=PROXY) as resp:
                print(resp.status, await resp.text())


asyncio.run(main())
```

## Hoe houd ik één IP voor een multi-step flow?

Genereer één sessie-ID per flow en zet die in de gebruikersnaam met een TTL langer dan de flow. Keep-alive is hier prima: je wilt dezelfde exit. Als het residentiële apparaat achter de sessie offline gaat, kan het IP veranderen voordat de TTL afloopt, dus maak de flow herstartbaar vanaf het laatste checkpoint.

*Sticky sessie met requests*

```python
import uuid

import requests

session_id = uuid.uuid4().hex[:10]  # one ID per flow, lowercase letters and digits
user = "USERNAME-country-us-state-ca-session-%s-ttl-20" % session_id
proxy = "http://%s:PASSWORD@gw.crawlrelay.com:7777" % user

with requests.Session() as s:
    s.proxies = {"http": proxy, "https": proxy}
    first = s.get("https://api.ipify.org", timeout=(10, 30)).text
    second = s.get("https://api.ipify.org", timeout=(10, 30)).text
    print(first, second, "same exit" if first == second else "exit changed")
```

## Hoeveel verkeer gebruikt een Python scraper per pagina?

Meet het op 100 echte pagina's voordat je volume koopt. Log `num_bytes_downloaded` in httpx, of de lengte van de gecomprimeerde body in je eigen middleware, en vermenigvuldig met je pagina-aantal. Alleen HTML-verzoeken kosten meestal tientallen kilobytes per pagina; de [bandbreedte gids](https://crawlrelay.com/nl/guides/bandwidth-calculator/) geeft de cijfers en de kosten per 1.000 pagina's tegen CrawlRelay prijzen.

Drie gewoontes verminderen verkeer zonder je resultaten te veranderen:

- Verstuur `Accept-Encoding: gzip, br` (httpx en requests versturen standaard gzip) zodat pagina's gecomprimeerd reizen.
- Haal de JSON-endpoint op die een pagina laadt, wanneer de site dit documenteert of toestaat, in plaats van de volledige HTML.
- Sla ongewijzigde pagina's over met `If-None-Match` of `If-Modified-Since`: een `304 Not Modified` kost enkele honderden bytes.

## Python en proxy's: vragen

### requests, httpx of aiohttp: welke moet ik gebruiken voor scraping?

Gebruik requests voor eenvoudige scripts en kleine crawls. Gebruik httpx als je dezelfde API wilt in synchrone en asynchrone code, en HTTP/2 via `httpx[http2]`. Gebruik aiohttp voor zeer hoge gelijktijdigheid in pure asyncio. Voor een crawl met planning, deduplicatie en pipelines gebruik je [Scrapy](https://crawlrelay.com/nl/docs/scrapy/) in plaats van die onderdelen zelf te schrijven.

### Waarom krijg ik SSL: WRONG_VERSION_NUMBER bij mijn proxy in Python?

Je hebt waarschijnlijk `https://` voor het proxyadres gezet. De gatewaypoort 7777 spreekt gewoon HTTP en opent een CONNECT-tunnel voor HTTPS-sites, dus de proxy-URL moet in zowel de `http` als `https` sleutels met `http://` beginnen. Zie de [foutpagina](https://crawlrelay.com/nl/docs/errors/ssl-wrong-version-number/).

### Hoe gebruik ik SOCKS5 proxy's in Python?

Installeer de extra (`pip install "requests[socks]"` of `pip install "httpx[socks]"`), gebruik dan `socks5h://USERNAME:PASSWORD@gw.crawlrelay.com:7778`. De `h` stuurt hostnamen naar de proxy, zodat DNS-resolutie bij de uitgang gebeurt en je dezelfde regionale antwoorden krijgt als lokale bezoekers.

### Waarom geeft mijn roterende proxy twee keer hetzelfde IP terug?

Je client hergebruikte een keep-alive verbinding, terwijl rotatie per nieuwe verbinding gebeurt. Verstuur `Connection: close` in requests, stel `max_keepalive_connections=0` in httpx in, of gebruik `force_close=True` in aiohttp. Controleer ook dat de gebruikersnaam geen `session-` onderdeel heeft, dat opzettelijk één IP behoudt.

### Hoe stel ik time-outs in voor proxied requests?

Stel ze expliciet in: requests heeft standaard geen timeout. Een connect-timeout van ongeveer 10 seconden en een read-timeout van 30 seconden is geschikt voor HTML-pagina's; mobiele exits en trage doelen hebben 45 tot 60 seconden nodig. Probeer een timeout eenmaal opnieuw met een nieuwe verbinding in plaats van langer te wachten. Zie [read timeouts](https://crawlrelay.com/nl/docs/errors/read-timeout/).

### Kan ik deze proxy's gebruiken in Jupyter of met pandas?

Ja. Alles wat requests of httpx gebruikt, neemt dezelfde proxy-instellingen over. Voor `pandas.read_html` of `read_csv` op een URL, download eerst de inhoud met een proxied requests-aanroep en geef dan de tekst of bytes aan pandas door.

**Laat je Python scraper lopen via residentiële proxy's** US$ 1,50 voor 1 GB, US$ 0,50/GB vanaf 50 GB. [Zie prijslijst](https://crawlrelay.com/nl/pricing/) · [Scrapy gids](https://crawlrelay.com/nl/docs/scrapy/)

---

CrawlRelay: CrawlRelay is een developer-first dienst met roterende residentiële proxy's en mobiele 4G-proxy's voor scrapers en AI-datastromen: niet-vervalverkeer op basis van pay-as-you-go vanaf US$ 0,50/GB, betaald in crypto, zonder ID-upload. Zonder KYC — geen ID, geen selfie, geen documenten. Betaal met crypto.
