GUIDE · PROXY SETUP

How to use a proxy with Python requests

The requests library makes proxies simple: pass a dictionary and you are done. This guide shows the basic call, a reusable session, environment variables, timeouts and the errors you are most likely to meet, using placeholders so you never paste real credentials into code.

Last updated October 31, 2026

The short version

The requests documentation says you configure a proxy with the proxies argument to any request method. You give it one entry per scheme, and the value is the proxy URL. If the proxy needs a username and password, the documentation says to use the http://user:pass@host:port syntax, which is HTTP Basic authentication. That is all there is to it.

Any proxy provider works the same way: you need a host, a port and a login (or IP authorization). The placeholders here are PROXY_HOST, PROXY_PORT, PROXY_USER and PROXY_PASS. Only use proxies for lawful purposes and within the terms of the websites you visit. A proxy is not a cloak: the proxy provider and the sites you visit can still see activity, so do not treat a proxy as making your traffic anonymous.

What Webshare's documentation says about connecting

We use Webshare as the example because it is a common starting point and has a free plan. We have not tested it. The details below come from Webshare's help center article on the rotating proxy endpoint and its API documentation on proxy connections, as read on October 31, 2026. Your own dashboard shows your real values, so copy them from there and not from this page.

Rotating endpoint: the help center says the domain name is p.webshare.io, the default port is 80, and the alternative ports are 1080, 3128, or any port between 9999 and 29999. It describes this method as rotating proxies randomly within your list for each request. Direct connections are different: the API docs say they use the proxy address and port fields from the Proxy List API, so each proxy in your list has its own address and port.

Authentication: the API docs describe two ways. With username and password authentication you provide the username and password along with the proxy address and port. With IP authorization only the address and port are needed, with no credentials, because Webshare recognizes the IP address you connect from.

Username options: for backbone connections the API docs say you can add parameters to your username with hyphens, for example a two-letter country code, a numeric session ID for a sticky session, or the word rotate for a new IP on every request. We have not checked which of these your plan includes, so check your dashboard before relying on them.

A basic request through a proxy

This script asks a public service which IP address it sees, so you can confirm the proxy is in use. It sets a timeout, which you should always do: without one, requests can wait a very long time on a dead proxy.

python
import requests

PROXY_URL = "http://PROXY_USER:PROXY_PASS@PROXY_HOST:PROXY_PORT"

proxies = {
    "http": PROXY_URL,
    "https": PROXY_URL,
}

response = requests.get("https://api.ipify.org?format=json", proxies=proxies, timeout=15)
print(response.status_code, response.text)

Note that both entries use an http:// proxy URL, even for the https key. The scheme in the proxy URL describes how you talk to the proxy, not the website. The requests documentation example does the same. Replace the placeholders with your own values, and keep real credentials out of files you share.

Use a session and environment variables

If you make many requests, a Session saves repeating yourself. Reading the credentials from environment variables also keeps them out of your code. The requests documentation warns that storing a username and password in a version-controlled file is a security risk.

If you do not have a proxy yet, Webshare has a free plan you can try (Affiliate link) and shows your host, port and login in its dashboard. Check its current plan limits and terms on its own site first, as they can change.

python
import os
import requests

# Read credentials from environment variables, not from the source file.
user = os.environ["PROXY_USER"]
password = os.environ["PROXY_PASS"]
host = os.environ["PROXY_HOST"]
port = os.environ["PROXY_PORT"]

proxy_url = f"http://{user}:{password}@{host}:{port}"

session = requests.Session()
session.proxies.update({"http": proxy_url, "https": proxy_url})

for _ in range(3):
    r = session.get("https://api.ipify.org?format=json", timeout=15)
    print(r.text)

The documentation also notes a subtlety: values set on session.proxies may be overridden by environment proxies. To be sure your proxy is used when environment variables such as HTTPS_PROXY exist, pass the proxies argument on each request as in the first example.

Rotating or sticky addresses

With a rotating proxy endpoint, each request can leave through a different address. Webshare's help center describes its rotating endpoint as p.webshare.io, default port 80, rotating randomly within your list for each request. Its API docs describe adding hyphen-separated parameters to the username, such as a country code, a numeric session ID for a sticky session, or rotate for a new IP on every request. Check your dashboard for the exact username format for your plan, and whether those options are included in it.

A session ID keeps the same exit address for a while, which helps when a site expects you to stay on one IP through a login. Rotation helps when you are making many independent requests.

Handle errors and timeouts

Proxies fail in ordinary ways: a typo, a wrong password, an overloaded server. Catch the errors so your script reports what happened and does not crash halfway through.

python
import requests
from requests.exceptions import ProxyError, ConnectTimeout, ReadTimeout

try:
    r = requests.get("https://api.ipify.org?format=json", proxies=proxies, timeout=(5, 15))
    r.raise_for_status()
    print(r.text)
except ProxyError as e:
    print("Proxy refused or login failed:", e)
except (ConnectTimeout, ReadTimeout):
    print("Timed out; try again or check the proxy")

A 407 status code means the proxy wants a login you did not give correctly. Be gentle with the sites you request: add delays, honor robots.txt, and stay within their terms.

Check that it worked

Compare your public IP address with and without the proxy. Open our what is my IP tool in a normal browser tab and note the address, then run the same check through the proxy. The two addresses should differ if traffic really goes through the proxy. You can also run your proxy through our proxy checker, or look up the address you see with the IP lookup tool to see its country and network.

If the address did not change, the proxy is probably not being used. If you get an error instead, see the troubleshooting section.

Where to go next

For larger scraping projects, see Scrapy with a proxy and the overview of proxies for web scraping. For browsers driven from code, see Puppeteer.

Once your proxy works, the next step is choosing a plan that fits your use. See Webshare's current plans (Affiliate link) or compare other providers; the setup above stays the same either way.

Frequently asked questions

How do I pass a proxy to Python requests?

Use the proxies argument, a dictionary with http and https keys whose values are proxy URLs like http://user:pass@host:port.

Should the https key use an https:// proxy URL?

Usually not. The scheme in the proxy URL describes how you connect to the proxy. The requests documentation uses http:// proxy URLs for both keys.

Is it safe to put my proxy password in the script?

The requests documentation says storing credentials in an environment variable or version-controlled file is a security risk. Reading them from environment variables or a local settings file is safer.

Why does my session ignore the proxy?

The documentation warns that session.proxies can be overridden by environment proxy variables. Pass proxies on each request to be certain.

What does a ProxyError mean?

It means requests could not connect through the proxy. Usually the host or port is wrong, the login failed, or the proxy is down.