proxyproviders 0.2.2__tar.gz → 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. proxyproviders-0.3.0/.gitignore +10 -0
  2. proxyproviders-0.3.0/PKG-INFO +504 -0
  3. proxyproviders-0.3.0/README.md +473 -0
  4. proxyproviders-0.3.0/proxyproviders/__init__.py +31 -0
  5. {proxyproviders-0.2.2 → proxyproviders-0.3.0}/proxyproviders/algorithms.py +2 -2
  6. {proxyproviders-0.2.2 → proxyproviders-0.3.0}/proxyproviders/exceptions.py +2 -4
  7. proxyproviders-0.3.0/proxyproviders/httpx_transport.py +248 -0
  8. proxyproviders-0.3.0/proxyproviders/models/formats.py +277 -0
  9. proxyproviders-0.3.0/proxyproviders/models/proxy.py +205 -0
  10. {proxyproviders-0.2.2 → proxyproviders-0.3.0}/proxyproviders/providers/brightdata.py +25 -17
  11. proxyproviders-0.3.0/proxyproviders/providers/proxy_list.py +72 -0
  12. {proxyproviders-0.2.2 → proxyproviders-0.3.0}/proxyproviders/providers/webshare.py +11 -8
  13. proxyproviders-0.3.0/proxyproviders/proxy_provider.py +252 -0
  14. proxyproviders-0.3.0/proxyproviders/py.typed +0 -0
  15. proxyproviders-0.3.0/proxyproviders/session.py +228 -0
  16. {proxyproviders-0.2.2 → proxyproviders-0.3.0}/proxyproviders/version.py +1 -1
  17. proxyproviders-0.3.0/pyproject.toml +108 -0
  18. proxyproviders-0.2.2/PKG-INFO +0 -299
  19. proxyproviders-0.2.2/README.md +0 -254
  20. proxyproviders-0.2.2/proxyproviders/__init__.py +0 -16
  21. proxyproviders-0.2.2/proxyproviders/models/proxy.py +0 -149
  22. proxyproviders-0.2.2/proxyproviders/proxy_provider.py +0 -130
  23. proxyproviders-0.2.2/pyproject.toml +0 -49
  24. {proxyproviders-0.2.2 → proxyproviders-0.3.0}/LICENSE +0 -0
  25. {proxyproviders-0.2.2 → proxyproviders-0.3.0}/proxyproviders/models/__init__.py +0 -0
@@ -0,0 +1,10 @@
1
+ __pycache__
2
+ test.py
3
+ poetry.lock
4
+ .coverage
5
+ .DS_Store
6
+ build
7
+ dist
8
+ *.egg-info
9
+ coverage.xml
10
+ uv.lock
@@ -0,0 +1,504 @@
1
+ Metadata-Version: 2.4
2
+ Name: proxyproviders
3
+ Version: 0.3.0
4
+ Summary: One interface for proxy vendors (Webshare, Bright Data, or your own list) that works with requests, httpx, aiohttp, Playwright, Scrapy and more
5
+ Project-URL: Homepage, https://github.com/davidteather/proxyproviders
6
+ Project-URL: Documentation, https://davidteather.github.io/proxyproviders/
7
+ Project-URL: Source Code, https://github.com/davidteather/proxyproviders
8
+ Project-URL: Issues, https://github.com/davidteather/proxyproviders/issues
9
+ Project-URL: Changelog, https://github.com/davidteather/proxyproviders/releases
10
+ Project-URL: Funding, https://github.com/sponsors/davidteather
11
+ Author-email: David Teather <contact.davidteather@gmail.com>
12
+ License-Expression: MIT
13
+ License-File: LICENSE
14
+ Keywords: aiohttp,brightdata,httpx,playwright,proxies,proxy,proxy-pool,proxy-provider,proxy-rotation,requests,rotating-proxies,scrapy,socks5,web-scraping,webshare
15
+ Classifier: Development Status :: 4 - Beta
16
+ Classifier: Intended Audience :: Developers
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Programming Language :: Python :: 3.14
23
+ Classifier: Typing :: Typed
24
+ Requires-Python: >=3.10
25
+ Requires-Dist: requests
26
+ Provides-Extra: httpx
27
+ Requires-Dist: httpx>=0.26; extra == 'httpx'
28
+ Provides-Extra: socks
29
+ Requires-Dist: requests[socks]; extra == 'socks'
30
+ Description-Content-Type: text/markdown
31
+
32
+ # ProxyProviders
33
+
34
+ One interface for proxy vendors, many Python clients.
35
+
36
+ Get proxies from Webshare, Bright Data or your own list, and use them with requests, httpx, aiohttp, Playwright, Scrapy, Crawlee or curl without writing vendor-specific code. Swap providers by changing one line.
37
+
38
+ [![codecov](https://codecov.io/gh/davidteather/proxyproviders/graph/badge.svg?token=MZF0KP7YMA)](https://codecov.io/gh/davidteather/proxyproviders) [![Sponsor Me](https://img.shields.io/static/v1?label=Sponsor&message=%E2%9D%A4&logo=GitHub)](https://go.dteather.com/github-sponsors?src=proxyproviders&placement=badge) [![GitHub release (latest by date)](https://img.shields.io/github/v/release/davidteather/proxyproviders)](https://github.com/davidteather/proxyproviders/releases) [![GitHub](https://img.shields.io/github/license/davidteather/proxyproviders)](https://github.com/davidteather/proxyproviders/blob/main/LICENSE) [![Downloads](https://pepy.tech/badge/proxyproviders)](https://pypi.org/project/proxyproviders/) ![](https://visitor-badge.laobi.icu/badge?page_id=davidteather.proxyproviders) [![Support Server](https://img.shields.io/discord/783108952111579166.svg?color=7289da&logo=discord&style=flat-square)](https://go.dteather.com/discord?src=proxyproviders&placement=badge) [![LinkedIn](https://img.shields.io/badge/LinkedIn-0077B5?style=for-the-badge&logo=linkedin&logoColor=white&style=flat-square)](https://www.linkedin.com/in/davidteather/)
39
+
40
+ ```py
41
+ import requests
42
+ from proxyproviders import Webshare, ProxyFormat
43
+
44
+ provider = Webshare(api_key="your-api-key") # or BrightData(...), or ProxyList([...])
45
+ proxy = provider.get_proxy()
46
+
47
+ response = requests.get(
48
+ "https://httpbin.org/ip", proxies=proxy.format(ProxyFormat.REQUESTS)
49
+ )
50
+ ```
51
+
52
+ ## Table of Contents
53
+
54
+ - [Installation](#installation)
55
+ - [What It Does](#what-it-does)
56
+ - [Works With](#works-with)
57
+ - [Supported Proxy Providers](#supported-proxy-providers)
58
+ - [Quick Start Guide](#quick-start-guide)
59
+ - [Using Proxies With Your HTTP Client](#using-proxies-with-your-http-client)
60
+ - [requests](#python-requests-with-rotating-proxies)
61
+ - [httpx](#httpx-proxy)
62
+ - [aiohttp](#aiohttp-proxy)
63
+ - [Playwright](#playwright-proxy-in-python)
64
+ - [Scrapy](#scrapy-proxy)
65
+ - [Crawlee](#crawlee-for-python-proxy)
66
+ - [curl and subprocesses](#curl-and-subprocesses)
67
+ - [Use Your Own Proxy List](#use-your-own-proxy-list)
68
+ - [Automatic Rotation and Retries](#automatic-rotation-and-retries)
69
+ - [Reporting Failed Proxies](#reporting-failed-proxies)
70
+ - [Choosing Which Proxy You Get](#choosing-which-proxy-you-get)
71
+ - [Configuration](#configuration)
72
+ - [SOCKS Proxies](#socks-proxies)
73
+ - [Making Your Own Proxy Provider](#making-your-own-proxy-provider)
74
+ - [How to Support The Project](#how-to-support-the-project)
75
+
76
+ ## Installation
77
+
78
+ ```sh
79
+ python -m pip install proxyproviders
80
+ ```
81
+
82
+ Python 3.10 or newer. The full documentation is [here](https://davidteather.github.io/proxyproviders), and there are runnable [examples in the repository](https://github.com/davidteather/proxyproviders/tree/main/examples).
83
+
84
+ Using an AI coding assistant? Point it at [llms.txt](https://davidteather.github.io/proxyproviders/llms.txt), a short summary of the API written for assistants.
85
+
86
+ **📺 Video Tutorial:** [Watch the quick start guide on YouTube](https://youtu.be/h-JouO_orYo) for a walkthrough of getting started with ProxyProviders.
87
+
88
+ ## What It Does
89
+
90
+ Every proxy vendor has its own API, and every HTTP client wants its proxy in a different shape. ProxyProviders sits between the two:
91
+
92
+ ```text
93
+ Webshare | Bright Data | your own list
94
+ ↓
95
+ ProxyProvider → Proxy
96
+ ↓
97
+ requests | httpx | aiohttp | Playwright | Scrapy | Crawlee | curl
98
+ ```
99
+
100
+ - **Provider API handled for you.** Proxies are fetched from the vendor, cached, and refreshed on an interval.
101
+ - **One `Proxy` model.** Host, port, credentials, country and protocols look the same whichever vendor they came from.
102
+ - **One line to hand it to your client.** `proxy.format(...)` produces what each client expects, with credentials percent-encoded correctly.
103
+ - **Vendor-neutral code.** Anything written against `ProxyProvider` works with every provider, so switching vendor does not touch the rest of your application.
104
+
105
+ Rotation and retries are available if you want them ([`RotatingSession`](#automatic-rotation-and-retries)), and stay out of the way if your framework already does that.
106
+
107
+ ## Works With
108
+
109
+ | Client | How to pass the proxy | Format |
110
+ |---|---|---|
111
+ | [requests](#python-requests-with-rotating-proxies) | `requests.get(url, proxies=...)` | `ProxyFormat.REQUESTS` |
112
+ | [httpx](#httpx-proxy) | `httpx.Client(proxy=...)` | `ProxyFormat.HTTPX` |
113
+ | [aiohttp](#aiohttp-proxy) | `session.get(url, proxy=...)` | `ProxyFormat.AIOHTTP` |
114
+ | [Playwright](#playwright-proxy-in-python) | `browser.new_context(proxy=...)` | `ProxyFormat.PLAYWRIGHT` |
115
+ | [Scrapy](#scrapy-proxy) | `Request(meta={"proxy": ...})` | `ProxyFormat.SCRAPY` |
116
+ | [Crawlee](#crawlee-for-python-proxy) | `ProxyConfiguration(proxy_urls=[...])` | `proxy.to_url()` |
117
+ | urllib | `urllib.request.ProxyHandler(...)` | `ProxyFormat.URLLIB` |
118
+ | [curl](#curl-and-subprocesses) | `curl -x ...` | `ProxyFormat.CURL` |
119
+ | [Any subprocess](#curl-and-subprocesses) | `HTTP_PROXY` / `HTTPS_PROXY` variables | `ProxyFormat.ENV` |
120
+ | Other tools | `host:port:username:password` | `ProxyFormat.HOST_PORT_USER_PASS` |
121
+ | Anything else | `scheme://username:password@host:port` | `ProxyFormat.URL` |
122
+
123
+ requests, httpx, aiohttp, urllib and curl are tested in CI by sending a real request through a local proxy with each client's current release.
124
+
125
+ ## Supported Proxy Providers
126
+
127
+ | Provider | Class | Notes |
128
+ |---|---|---|
129
+ | Webshare | `Webshare(api_key=...)` | 10 free datacenter proxies to try it out |
130
+ | Bright Data | `BrightData(api_key=..., zone=...)` | Super proxy or one proxy per IP in the zone |
131
+ | Any other vendor | `ProxyList([...])` | [Your own list](#use-your-own-proxy-list) of proxy URLs or `host:port:user:pass` lines |
132
+
133
+ Getting credentials:
134
+
135
+ - **Webshare:** create an account [here](https://go.dteather.com/webshare?src=proxyproviders&placement=readme) (affiliate link), then generate a token in the [API tab](https://dashboard.webshare.io/userapi/keys).
136
+ - **Bright Data:** create an account [here](https://go.dteather.com/brightdata?src=proxyproviders&placement=readme) (affiliate link), then create an API token in the [account settings](https://brightdata.com/cp/setting/users).
137
+
138
+ Keep API tokens out of your code and repository; the examples below read them from environment variables. Each provider's options are in the [provider documentation](https://davidteather.github.io/proxyproviders/#proxyproviders-supported-providers). Don't see your vendor? [Add a provider](#making-your-own-proxy-provider) or open an issue.
139
+
140
+ ## Quick Start Guide
141
+
142
+ List the proxies from a provider:
143
+
144
+ ```py
145
+ import os
146
+ from proxyproviders import Webshare
147
+
148
+ provider = Webshare(api_key=os.environ["WEBSHARE_API_KEY"])
149
+
150
+ proxies = provider.list_proxies()
151
+ print(proxies) # passwords are left out of the printed output
152
+ ```
153
+
154
+ Or take one at a time. `get_proxy()` cycles through the list in order by default:
155
+
156
+ ```py
157
+ proxy = provider.get_proxy()
158
+ print(proxy.proxy_address, proxy.port, proxy.country_code)
159
+ ```
160
+
161
+ Providers take their own options. `Webshare` passes `search_params` to the [Webshare API](https://apidocs.webshare.io/proxy-list/list#parameters), so this returns only proxies based in the US:
162
+
163
+ ```py
164
+ provider = Webshare(
165
+ api_key=os.environ["WEBSHARE_API_KEY"], search_params={"country_code_in": "US"}
166
+ )
167
+ ```
168
+
169
+ Because every provider has the same interface, code written against `ProxyProvider` does not care which vendor is behind it:
170
+
171
+ ```py
172
+ import os
173
+ import requests
174
+ from proxyproviders import BrightData, ProxyFormat, ProxyProvider, Webshare
175
+
176
+
177
+ def fetch_ip(provider: ProxyProvider) -> str:
178
+ proxy = provider.get_proxy()
179
+ response = requests.get(
180
+ "https://httpbin.org/ip", proxies=proxy.format(ProxyFormat.REQUESTS)
181
+ )
182
+ return response.json()["origin"]
183
+
184
+
185
+ fetch_ip(Webshare(api_key=os.environ["WEBSHARE_API_KEY"]))
186
+ fetch_ip(BrightData(api_key=os.environ["BRIGHTDATA_API_KEY"], zone="my_zone"))
187
+ ```
188
+
189
+ That is how other libraries, such as my [TikTokAPI](https://go.dteather.com/tiktokapi?src=proxyproviders&placement=readme) package, accept any proxy vendor without knowing about it.
190
+
191
+ ## Using Proxies With Your HTTP Client
192
+
193
+ Every example below starts with a `provider`, which can be any provider from the table above.
194
+
195
+ ### Python requests With Rotating Proxies
196
+
197
+ ```py
198
+ import requests
199
+ from proxyproviders import ProxyFormat
200
+
201
+ proxy = provider.get_proxy()
202
+ response = requests.get(
203
+ "https://httpbin.org/ip", proxies=proxy.format(ProxyFormat.REQUESTS)
204
+ )
205
+ ```
206
+
207
+ Each `get_proxy()` call returns the next proxy, so calling it per request rotates through your proxies. To have rotation and retries done for you, use [`RotatingSession`](#automatic-rotation-and-retries).
208
+
209
+ ### httpx Proxy
210
+
211
+ httpx (0.26 or newer) takes one proxy URL in its `proxy=` argument, for both the sync and async clients:
212
+
213
+ ```py
214
+ import httpx
215
+ from proxyproviders import ProxyFormat
216
+
217
+ with httpx.Client(proxy=provider.get_proxy().format(ProxyFormat.HTTPX)) as client:
218
+ response = client.get("https://httpbin.org/ip")
219
+
220
+ async with httpx.AsyncClient(
221
+ proxy=provider.get_proxy().format(ProxyFormat.HTTPX)
222
+ ) as client:
223
+ response = await client.get("https://httpbin.org/ip")
224
+ ```
225
+
226
+ ### aiohttp Proxy
227
+
228
+ ```py
229
+ import aiohttp
230
+ from proxyproviders import ProxyFormat
231
+
232
+ async with aiohttp.ClientSession() as session:
233
+ async with session.get(
234
+ "https://httpbin.org/ip", proxy=provider.get_proxy().format(ProxyFormat.AIOHTTP)
235
+ ) as response:
236
+ print(await response.json())
237
+ ```
238
+
239
+ ### Playwright Proxy in Python
240
+
241
+ ```py
242
+ from playwright.sync_api import sync_playwright
243
+ from proxyproviders import ProxyFormat
244
+
245
+ with sync_playwright() as p:
246
+ browser = p.chromium.launch()
247
+ context = browser.new_context(
248
+ proxy=provider.get_proxy().format(ProxyFormat.PLAYWRIGHT)
249
+ )
250
+ page = context.new_page()
251
+ page.goto("https://httpbin.org/ip")
252
+ ```
253
+
254
+ Give each browser context its own `get_proxy()` call to spread contexts across proxies. The same dictionary works for `p.chromium.launch(proxy=...)`.
255
+
256
+ ### Scrapy Proxy
257
+
258
+ Scrapy's built-in `HttpProxyMiddleware` reads the proxy from `request.meta` and takes the credentials out of the URL:
259
+
260
+ ```py
261
+ import scrapy
262
+ from proxyproviders import ProxyFormat
263
+
264
+
265
+ class IpSpider(scrapy.Spider):
266
+ name = "ip"
267
+
268
+ def start_requests(self):
269
+ yield scrapy.Request(
270
+ "https://httpbin.org/ip",
271
+ meta={"proxy": provider.get_proxy().format(ProxyFormat.SCRAPY)},
272
+ )
273
+ ```
274
+
275
+ ### Crawlee for Python Proxy
276
+
277
+ Crawlee rotates proxies and manages sessions itself, so hand it the whole list:
278
+
279
+ ```py
280
+ from crawlee.proxy_configuration import ProxyConfiguration
281
+
282
+ proxy_configuration = ProxyConfiguration(
283
+ proxy_urls=[proxy.to_url() for proxy in provider.list_proxies()]
284
+ )
285
+ ```
286
+
287
+ ### curl and Subprocesses
288
+
289
+ ```py
290
+ import os
291
+ import subprocess
292
+ from proxyproviders import ProxyFormat
293
+
294
+ proxy = provider.get_proxy()
295
+
296
+ # As curl arguments: ["-x", "http://user:pass@host:port"]
297
+ subprocess.run(["curl", *proxy.format(ProxyFormat.CURL), "https://httpbin.org/ip"])
298
+
299
+ # Or as HTTP_PROXY / HTTPS_PROXY for any program that reads them
300
+ subprocess.run(
301
+ ["curl", "https://httpbin.org/ip"],
302
+ env={**os.environ, **proxy.format(ProxyFormat.ENV)},
303
+ )
304
+ ```
305
+
306
+ ## Use Your Own Proxy List
307
+
308
+ If your vendor has no provider here, or you simply have a list of proxies, `ProxyList` gives them the same interface as everything else:
309
+
310
+ ```py
311
+ from proxyproviders import ProxyList
312
+
313
+ provider = ProxyList(
314
+ [
315
+ "http://user:pass@203.0.113.7:8080",
316
+ "203.0.113.8:8080:user:pass",
317
+ "socks5://user:pass@203.0.113.9:1080",
318
+ ]
319
+ )
320
+
321
+ # Or one proxy per line from a file (blank lines and # comments are skipped)
322
+ provider = ProxyList.from_file("proxies.txt")
323
+ ```
324
+
325
+ A single proxy can be parsed with `Proxy.from_string(...)`, which accepts a URL, `user:pass@host:port`, `host:port` or `host:port:user:pass`.
326
+
327
+ ## Automatic Rotation and Retries
328
+
329
+ `RotatingSession` is a `requests.Session` that picks a proxy for every request. If a request cannot get through its proxy, that proxy is set aside for a while and the request is sent again through the next one:
330
+
331
+ ```py
332
+ from proxyproviders import RotatingSession
333
+
334
+ session = RotatingSession(provider, max_retries=3)
335
+
336
+ response = session.get("https://httpbin.org/ip", timeout=10)
337
+ print(response.json(), session.last_proxy)
338
+ ```
339
+
340
+ It is opt-in: nothing changes for code that calls `get_proxy()` itself. What it treats as a failed proxy, and when it sends the request again:
341
+
342
+ - **Could not connect to the proxy, or open a tunnel through it:** retried for any HTTP method, because the request never reached the target.
343
+ - **Any other connection error or timeout**, such as a proxy that drops the connection or stops responding: retried for `GET`, `HEAD` and `OPTIONS` only. A `POST` may already have been acted on, so the error is raised instead of sending it twice.
344
+ - **A `407 Proxy Authentication Required` response**, or any status you list in `retry_statuses`: retried for any method.
345
+
346
+ Ordinary error statuses such as 404 or 500 are returned as usual. File uploads and streamed bodies are sent once and never retried. Deciding that a response means your proxy is blocked is up to you, for example:
347
+
348
+ ```py
349
+ session = RotatingSession(provider, max_retries=3, retry_statuses={403, 429})
350
+ ```
351
+
352
+ For httpx 0.26 or newer, sync or async, the same behaviour comes as a transport. Install it with `pip install "proxyproviders[httpx]"`:
353
+
354
+ ```py
355
+ import httpx
356
+ from proxyproviders.httpx_transport import AsyncRotatingTransport, RotatingTransport
357
+
358
+ with httpx.Client(transport=RotatingTransport(provider, max_retries=3)) as client:
359
+ response = client.get("https://httpbin.org/ip")
360
+
361
+ async with httpx.AsyncClient(transport=AsyncRotatingTransport(provider)) as client:
362
+ response = await client.get("https://httpbin.org/ip")
363
+ ```
364
+
365
+ ## Reporting Failed Proxies
366
+
367
+ When you make requests yourself, tell the provider about a proxy that failed and `get_proxy()` will avoid it for `failure_cooldown` seconds (60 by default):
368
+
369
+ ```py
370
+ import requests
371
+ from proxyproviders import ProxyFormat
372
+
373
+ proxy = provider.get_proxy()
374
+ try:
375
+ response = requests.get(url, proxies=proxy.format(ProxyFormat.REQUESTS), timeout=10)
376
+ except requests.exceptions.ProxyError:
377
+ provider.report_failure(proxy)
378
+ ```
379
+
380
+ `provider.report_success(proxy)` puts it back early, and `provider.available_proxies()` lists the ones not currently set aside. If every proxy has failed, `get_proxy()` still returns one instead of raising. This works the same with every HTTP client, sync or async.
381
+
382
+ ## Choosing Which Proxy You Get
383
+
384
+ `get_proxy()` takes a selection algorithm. The default is round robin.
385
+
386
+ ```py
387
+ from proxyproviders.algorithms import First, Random, RoundRobin
388
+
389
+ proxy = provider.get_proxy() # RoundRobin: next proxy in order
390
+ proxy = provider.get_proxy(Random()) # a random proxy
391
+ proxy = provider.get_proxy(First()) # always the first proxy
392
+
393
+ # A RoundRobin you create keeps its own position, separate from the provider's default
394
+ round_robin = RoundRobin()
395
+ proxy1 = provider.get_proxy(round_robin)
396
+ proxy2 = provider.get_proxy(round_robin) # next in sequence
397
+ ```
398
+
399
+ Write your own by subclassing `Algorithm`:
400
+
401
+ ```py
402
+ from typing import List
403
+ from proxyproviders import Proxy
404
+ from proxyproviders.algorithms import Algorithm
405
+
406
+
407
+ class USProxyAlgorithm(Algorithm):
408
+ """Prefers US proxies, falls back to the first available."""
409
+
410
+ def select(self, proxies: List[Proxy]) -> Proxy:
411
+ for proxy in proxies:
412
+ if proxy.country_code == "US":
413
+ return proxy
414
+ return proxies[0]
415
+
416
+
417
+ proxy = provider.get_proxy(USProxyAlgorithm())
418
+ ```
419
+
420
+ ## Configuration
421
+
422
+ Options shared by every provider live in `ProxyConfig` ([full reference](https://davidteather.github.io/proxyproviders/#proxyproviders.proxy_provider.ProxyConfig)):
423
+
424
+ | Option | Default | Meaning |
425
+ |---|---|---|
426
+ | `refresh_interval` | 600 | Seconds before the proxy list is fetched again. 0 fetches once and never refreshes. |
427
+ | `timeout` | 30 | Seconds to wait on each call to the provider's API |
428
+ | `refresh_retry_delay` | 30 | If a refresh fails, the previous list keeps being served and the API is tried again after this many seconds |
429
+ | `failure_cooldown` | 60 | Seconds `get_proxy()` avoids a proxy after `report_failure()` |
430
+
431
+ ```py
432
+ import os
433
+ import time
434
+ from proxyproviders import ProxyConfig, Webshare
435
+
436
+ config = ProxyConfig(refresh_interval=3)
437
+ ws = Webshare(api_key=os.environ["WEBSHARE_API_KEY"], config=config)
438
+
439
+ ws.list_proxies() # calls the API
440
+ ws.list_proxies() # cached
441
+
442
+ time.sleep(5)
443
+ ws.list_proxies() # calls the API again, since more than 3s have passed
444
+ ```
445
+
446
+ `list_proxies()` and `get_proxy()` are safe to call from several threads.
447
+
448
+ ## SOCKS Proxies
449
+
450
+ A proxy whose `protocols` only lists a SOCKS scheme, e.g. `["socks5"]`, is formatted with that scheme for every client:
451
+
452
+ ```py
453
+ proxy.format(ProxyFormat.REQUESTS)
454
+ # {'http': 'socks5://user:pass@host:1080', 'https': 'socks5://user:pass@host:1080'}
455
+ ```
456
+
457
+ Your HTTP client needs SOCKS support for this to work. For requests, install it with:
458
+
459
+ ```sh
460
+ python -m pip install "proxyproviders[socks]"
461
+ ```
462
+
463
+ httpx needs `httpx[socks]`. aiohttp and Scrapy have no built-in SOCKS support.
464
+
465
+ ## Making Your Own Proxy Provider
466
+
467
+ Subclass `ProxyProvider` and return a list of `Proxy` objects from `_fetch_proxies`. Caching, refreshing, selection, formatting and failure reporting then work without any more code:
468
+
469
+ ```py
470
+ from typing import List, Optional
471
+ from proxyproviders import Proxy, ProxyConfig, ProxyProvider
472
+
473
+
474
+ class MyProxyProvider(ProxyProvider):
475
+ def __init__(self, config: Optional[ProxyConfig] = None):
476
+ super().__init__(config=config)
477
+
478
+ def _fetch_proxies(self) -> List[Proxy]:
479
+ # Call your vendor's API here and convert each entry
480
+ return [
481
+ Proxy(
482
+ id="1",
483
+ username="username",
484
+ password="password",
485
+ proxy_address="192.168.1.1",
486
+ port=80,
487
+ )
488
+ ]
489
+
490
+
491
+ provider = MyProxyProvider()
492
+ proxy = provider.get_proxy()
493
+ ```
494
+
495
+ If you write one for a vendor other people use, please contribute it back. [CONTRIBUTING.md](https://github.com/davidteather/proxyproviders/blob/main/CONTRIBUTING.md) has the steps, and the repository includes an `add-provider` skill that walks a coding agent such as Claude Code through the whole process.
496
+
497
+ ## How to Support The Project
498
+
499
+ - Star the repo 😎
500
+ - Consider [sponsoring](https://go.dteather.com/github-sponsors?src=proxyproviders&placement=readme) me on GitHub
501
+ - Send me an email or a [LinkedIn](https://www.linkedin.com/in/davidteather/) message about what you're doing with it :D
502
+ - Submit PRs for issues or any new providers/features :)
503
+
504
+ **Note:** If you want to learn how to web scrape websites check my [free and open-source course for learning everything web scraping](https://go.dteather.com/everything-web-scraping?src=proxyproviders&placement=readme)