proxy-scraper-cli 1.9.0__tar.gz → 1.11.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 (115) hide show
  1. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/PKG-INFO +36 -14
  2. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/README.md +34 -13
  3. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxy_scraper_cli.egg-info/PKG-INFO +36 -14
  4. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxy_scraper_cli.egg-info/SOURCES.txt +7 -0
  5. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxy_scraper_cli.egg-info/requires.txt +1 -0
  6. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/__init__.py +1 -1
  7. proxy_scraper_cli-1.11.0/proxyscraper/action.py +96 -0
  8. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/agent.py +7 -4
  9. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/api.py +9 -5
  10. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/cli.py +20 -5
  11. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/completion.py +61 -3
  12. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/exporters.py +43 -0
  13. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/mcp_server.py +11 -3
  14. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/parsing.py +6 -1
  15. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/proxypages.py +5 -1
  16. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/publish.py +10 -4
  17. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/report.py +66 -2
  18. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/server/core.py +23 -11
  19. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/site/index.html +168 -21
  20. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/sites.py +55 -8
  21. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/sources.py +8 -2
  22. proxy_scraper_cli-1.11.0/proxyscraper/speed.py +129 -0
  23. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/pyproject.toml +1 -1
  24. proxy_scraper_cli-1.11.0/tests/test_action.py +69 -0
  25. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/tests/test_agent.py +1 -1
  26. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/tests/test_completion.py +35 -0
  27. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/tests/test_exporters.py +22 -0
  28. proxy_scraper_cli-1.11.0/tests/test_parsing_fuzz.py +83 -0
  29. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/tests/test_proxy_sources.py +29 -0
  30. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/tests/test_report.py +49 -0
  31. proxy_scraper_cli-1.11.0/tests/test_server_closed_writer.py +72 -0
  32. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/tests/test_sites.py +52 -5
  33. proxy_scraper_cli-1.11.0/tests/test_sources_json.py +36 -0
  34. proxy_scraper_cli-1.11.0/tests/test_speed.py +121 -0
  35. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/LICENSE +0 -0
  36. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxy_scraper_cli.egg-info/dependency_links.txt +0 -0
  37. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxy_scraper_cli.egg-info/entry_points.txt +0 -0
  38. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxy_scraper_cli.egg-info/top_level.txt +0 -0
  39. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/__main__.py +0 -0
  40. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/app.py +0 -0
  41. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/asndb.py +0 -0
  42. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/blocklist.py +0 -0
  43. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/charts.py +0 -0
  44. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/checker.py +0 -0
  45. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/compat.py +0 -0
  46. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/fetchcache.py +0 -0
  47. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/geo.py +0 -0
  48. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/geodb.py +0 -0
  49. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/handshake.py +0 -0
  50. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/history.py +0 -0
  51. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/judges.py +0 -0
  52. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/mcp_entry.py +0 -0
  53. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/netio.py +0 -0
  54. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/options.py +0 -0
  55. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/output.py +0 -0
  56. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/pages.py +0 -0
  57. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/paths.py +0 -0
  58. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/pipeline.py +0 -0
  59. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/preferences.py +0 -0
  60. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/server/__init__.py +0 -0
  61. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/server/http.py +0 -0
  62. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/server/pool.py +0 -0
  63. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/server/socks.py +0 -0
  64. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/server/status.py +0 -0
  65. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/server/upstream.py +0 -0
  66. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/site/apple-touch-icon.png +0 -0
  67. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/site/googleaac1161b7853c5b5.html +0 -0
  68. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/site/logo.png +0 -0
  69. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/site/og.png +0 -0
  70. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/sources.json +0 -0
  71. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/targets.py +0 -0
  72. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/ui/__init__.py +0 -0
  73. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/ui/dashboard.py +0 -0
  74. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/ui/keys.py +0 -0
  75. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/ui/report.py +0 -0
  76. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/ui/serve.py +0 -0
  77. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/ui/widgets.py +0 -0
  78. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/proxyscraper/ui/wizard.py +0 -0
  79. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/setup.cfg +0 -0
  80. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/tests/test_api.py +0 -0
  81. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/tests/test_app.py +0 -0
  82. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/tests/test_asndb.py +0 -0
  83. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/tests/test_auth.py +0 -0
  84. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/tests/test_blocklist.py +0 -0
  85. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/tests/test_charts.py +0 -0
  86. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/tests/test_checker.py +0 -0
  87. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/tests/test_compat.py +0 -0
  88. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/tests/test_discovery_growth.py +0 -0
  89. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/tests/test_fetchcache.py +0 -0
  90. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/tests/test_geodb.py +0 -0
  91. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/tests/test_judges.py +0 -0
  92. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/tests/test_learning.py +0 -0
  93. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/tests/test_live_api.py +0 -0
  94. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/tests/test_mcp_server.py +0 -0
  95. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/tests/test_mine_sources.py +0 -0
  96. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/tests/test_options.py +0 -0
  97. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/tests/test_output_stdout.py +0 -0
  98. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/tests/test_packaging.py +0 -0
  99. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/tests/test_pages.py +0 -0
  100. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/tests/test_parsing.py +0 -0
  101. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/tests/test_paths.py +0 -0
  102. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/tests/test_pipeline.py +0 -0
  103. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/tests/test_proxy_pages.py +0 -0
  104. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/tests/test_publish.py +0 -0
  105. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/tests/test_robustness.py +0 -0
  106. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/tests/test_serve_password.py +0 -0
  107. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/tests/test_serve_refill.py +0 -0
  108. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/tests/test_serve_v2.py +0 -0
  109. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/tests/test_server.py +0 -0
  110. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/tests/test_server_fixes.py +0 -0
  111. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/tests/test_tamper.py +0 -0
  112. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/tests/test_targets.py +0 -0
  113. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/tests/test_ui.py +0 -0
  114. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/tests/test_uptime.py +0 -0
  115. {proxy_scraper_cli-1.9.0 → proxy_scraper_cli-1.11.0}/tests/test_wizard.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: proxy-scraper-cli
3
- Version: 1.9.0
3
+ Version: 1.11.0
4
4
  Summary: Finds free HTTP, SOCKS4 and SOCKS5 proxies that actually work: 700+ sources, real checks, honeypot filtering and a rotating local proxy server.
5
5
  Author: Maximilian Feix
6
6
  License-Expression: MIT
@@ -35,6 +35,7 @@ Provides-Extra: mcp
35
35
  Requires-Dist: mcp<3,>=2.2; python_version >= "3.10" and extra == "mcp"
36
36
  Provides-Extra: dev
37
37
  Requires-Dist: pytest; extra == "dev"
38
+ Requires-Dist: hypothesis; extra == "dev"
38
39
  Requires-Dist: ruff==0.16.8; extra == "dev"
39
40
  Dynamic: license-file
40
41
 
@@ -138,11 +139,12 @@ python3 proxy_scraper.py
138
139
 
139
140
  Every [release](https://github.com/maximilianfeix/proxy-scraper/releases/latest) also ships a wheel you can install with `pip install <file>.whl`, and a multi-arch image (amd64/arm64) on `ghcr.io`. In the container the wizard never shows up, it runs straight away. For the proxy server use `--serve --serve-host 0.0.0.0` with `-p 127.0.0.1:8899:8899`, so the port is only open on your own machine.
140
141
 
141
- Tab completion for bash, zsh and fish:
142
+ Tab completion for bash, zsh, fish and PowerShell:
142
143
 
143
144
  ```bash
144
145
  eval "$(proxy-scraper --completion zsh)" # in ~/.zshrc (after compinit), same for bash in ~/.bashrc
145
146
  proxy-scraper --completion fish > ~/.config/fish/completions/proxy-scraper.fish
147
+ proxy-scraper --completion powershell | Out-String | Invoke-Expression # in $PROFILE
146
148
  ```
147
149
 
148
150
  Installed, the learned state lives in your user data folder (`~/Library/Application Support/proxy-scraper`, `%LOCALAPPDATA%\proxy-scraper` or `~/.local/share/proxy-scraper`; override with `PROXY_SCRAPER_HOME`) and results go to `./results`. Run from a clone, both stay inside the project.
@@ -203,7 +205,7 @@ Every protocol and country also has its own page with a plain download, e.g. [SO
203
205
  | HTTP · SOCKS4 · SOCKS5 | `1.2.3.4:8080` | [http.txt](https://raw.githubusercontent.com/maximilianfeix/proxy-scraper/proxy-list/http.txt) · [socks4.txt](https://raw.githubusercontent.com/maximilianfeix/proxy-scraper/proxy-list/socks4.txt) · [socks5.txt](https://raw.githubusercontent.com/maximilianfeix/proxy-scraper/proxy-list/socks5.txt) |
204
206
  | HTTPS-capable only | `type://ip:port` | [https.txt](https://raw.githubusercontent.com/maximilianfeix/proxy-scraper/proxy-list/https.txt) |
205
207
  | Elite only | `type://ip:port` | [elite.txt](https://raw.githubusercontent.com/maximilianfeix/proxy-scraper/proxy-list/elite.txt) |
206
- | Gets through to Google · Reddit · Amazon (no captcha, no 403 in the last run) | `type://ip:port` | [google.txt](https://raw.githubusercontent.com/maximilianfeix/proxy-scraper/proxy-list/works-with/google.txt) · [reddit.txt](https://raw.githubusercontent.com/maximilianfeix/proxy-scraper/proxy-list/works-with/reddit.txt) · [amazon.txt](https://raw.githubusercontent.com/maximilianfeix/proxy-scraper/proxy-list/works-with/amazon.txt) |
208
+ | Gets through to Google · Reddit · Amazon · Instagram · TikTok (no captcha, no block in the last run) | `type://ip:port` | [google.txt](https://raw.githubusercontent.com/maximilianfeix/proxy-scraper/proxy-list/works-with/google.txt) · [reddit.txt](https://raw.githubusercontent.com/maximilianfeix/proxy-scraper/proxy-list/works-with/reddit.txt) · [amazon.txt](https://raw.githubusercontent.com/maximilianfeix/proxy-scraper/proxy-list/works-with/amazon.txt) · [instagram.txt](https://raw.githubusercontent.com/maximilianfeix/proxy-scraper/proxy-list/works-with/instagram.txt) · [tiktok.txt](https://raw.githubusercontent.com/maximilianfeix/proxy-scraper/proxy-list/works-with/tiktok.txt) |
207
209
  | Stable, on the list in 90 %+ of this week's runs | `type://ip:port` | [stable.txt](https://raw.githubusercontent.com/maximilianfeix/proxy-scraper/proxy-list/stable.txt) |
208
210
  | With all details | latency, country, HTTPS, anonymity, exit IP, uptime, sites | [proxies.json](https://raw.githubusercontent.com/maximilianfeix/proxy-scraper/proxy-list/proxies.json) · [proxies.csv](https://raw.githubusercontent.com/maximilianfeix/proxy-scraper/proxy-list/proxies.csv) |
209
211
 
@@ -217,6 +219,8 @@ Every protocol and country also has its own page with a plain download, e.g. [SO
217
219
  curl -s https://raw.githubusercontent.com/maximilianfeix/proxy-scraper/proxy-list/socks5.txt | head
218
220
  ```
219
221
 
222
+ **Over time:** every day the first run's `proxies.json` is kept for good as a gzipped asset of the release of that year, like [snapshots-2026](../../releases/tag/snapshots-2026) – `proxies-2026-09-28.json.gz` and so on, for anyone who wants to look at free proxies over weeks and months.
223
+
220
224
  Every file is also on GitHub Pages, which sits behind a CDN, isn't rate limited like raw.githubusercontent and sends CORS headers, so it works straight from the browser: `https://maximilianfeix.github.io/proxy-scraper/socks5.txt`. [jsDelivr](https://cdn.jsdelivr.net/gh/maximilianfeix/proxy-scraper@proxy-list/) works too, but can lag behind by a few hours.
221
225
 
222
226
  Or let the tool start from it: `proxy-scraper --recheck live` downloads the list and checks it again from **your** network – about 30 seconds instead of a full scan (517 of 1,169 worked from here). With `--serve` you have a rotating proxy in under a minute.
@@ -438,7 +442,7 @@ pool = itertools.cycle(p.url for p in proxies)
438
442
  r = requests.get("https://api.ipify.org", proxies={"https": next(pool)}, timeout=15) # pip install "requests[socks]"
439
443
  ```
440
444
 
441
- Same filters as `find_proxies`, plus `min_uptime`, `works_on` (`["google"]`, `"reddit"`, `"amazon"`) and `limit`. Every result also has `uptime_24h`, `uptime_7d`, `first_seen`, `up_for_hours` and `sites`.
445
+ Same filters as `find_proxies`, plus `min_uptime`, `works_on` (`["google"]`, `"reddit"`, `"amazon"`, `"instagram"`, `"tiktok"`) and `limit`. Every result also has `uptime_24h`, `uptime_7d`, `first_seen`, `up_for_hours` and `sites`.
442
446
 
443
447
  <a id="proxy-server"></a>
444
448
 
@@ -656,14 +660,14 @@ if __name__ == "__main__":
656
660
 
657
661
  In a Scrapy project, put `RotatingProxy` in `middlewares.py` and add it to `DOWNLOADER_MIDDLEWARES` in `settings.py`. For long crawls, reload the pool now and then – the list changes every hour.
658
662
 
659
- **proxychains, Clash / Mihomo** – ready-made configs with `--export`
663
+ **proxychains, Clash / Mihomo, sing-box** – ready-made configs with `--export`
660
664
 
661
665
  ```bash
662
- proxy-scraper --want 30 -y --export proxychains,clash
666
+ proxy-scraper --want 30 -y --export proxychains,clash,singbox
663
667
  proxychains4 -f results/latest/proxychains.conf curl https://api.ipify.org
664
668
  ```
665
669
 
666
- `clash.yaml` has all HTTP and SOCKS5 proxies plus a `url-test` group that always picks the fastest. Both configs leave out HTTP proxies that can't tunnel (`CONNECT`), because these tools tunnel everything.
670
+ `clash.yaml` has all HTTP and SOCKS5 proxies plus a `url-test` group that always picks the fastest. `singbox.json` does the same for sing-box, SOCKS4 included, and opens a local proxy: `sing-box run -c results/latest/singbox.json`, then use `127.0.0.1:2080` as HTTP or SOCKS5 proxy. All three leave out HTTP proxies that can't tunnel (`CONNECT`), because these tools tunnel everything.
667
671
 
668
672
  **In a pipe** – `-o -` prints the hits to stdout, the interface moves to stderr
669
673
 
@@ -679,6 +683,22 @@ export HTTPS_PROXY=http://127.0.0.1:8899 HTTP_PROXY=http://127.0.0.1:8899
679
683
  pip download requests # git, pip, npm & co. now go through the pool
680
684
  ```
681
685
 
686
+ **In a GitHub workflow** – the action picks working proxies for the next steps
687
+
688
+ ```yaml
689
+ - id: proxies
690
+ uses: maximilianfeix/proxy-scraper@v1
691
+ with:
692
+ types: socks5
693
+ https: true
694
+ min-uptime: 90 # on the list for 90 %+ of the week
695
+ works-on: google # optional: google, reddit, amazon
696
+ recheck: true # optional: check them again from the runner
697
+ - run: curl -x "${{ steps.proxies.outputs.proxy }}" https://api.ipify.org
698
+ ```
699
+
700
+ `proxy` is the fastest one, `file` a text file with all of them (up to `limit`, default 20), `count` how many. Without a match the step fails, unless `fail-if-empty: false`.
701
+
682
702
  **Without installing anything** – straight from the live list
683
703
 
684
704
  ```bash
@@ -722,16 +742,16 @@ curl.exe -x (Get-Content "$run\all.txt" -TotalCount 1) http://api.ipify.org
722
742
  | `-t`, `--timeout S` | timeout per proxy (default: 8 s) |
723
743
  | `--discover` | search GitHub for new sources right now |
724
744
  | `--no-cache` | download every list again (unchanged ones are normally skipped via ETag) |
725
- | `--list-sources [N]` | show the source ranking |
745
+ | `--list-sources [N]` | show the source ranking (add `--json` for scripts: url, status, hit rate, checks, last change) |
726
746
  | `--serve-host ADDR` | where the proxy server listens (default `127.0.0.1`; `0.0.0.0` for Docker, with a warning) |
727
747
  | `--serve-password SECRET` | clients must send this password in the proxy login; better set `PROXY_SCRAPER_SERVE_PASSWORD` |
728
748
  | `--rotate STRATEGY` · `--sticky SEC` | how the proxy server picks proxies, see [above](#proxy-server) |
729
749
  | `--serve-refill HOURS` | while serving, check fresh proxies every HOURS and add the hits to the pool |
730
750
  | `--serve [PORT]` | afterwards serve as a rotating proxy on `127.0.0.1:PORT` (default: 8899) |
731
751
  | `-o FILE` | also write all hits to this file; `-o -` prints them to stdout |
732
- | `--export FORMATS` | extra files for other tools: `proxychains`, `clash`, `curl` or `all` |
752
+ | `--export FORMATS` | extra files for other tools: `proxychains`, `clash`, `singbox`, `curl` or `all` |
733
753
  | `-V`, `--version` | print the version |
734
- | `--completion SHELL` | print the tab completion script for bash, zsh or fish |
754
+ | `--completion SHELL` | print the tab completion script for bash, zsh, fish or PowerShell |
735
755
 
736
756
  Everything else: `proxy-scraper --help`
737
757
 
@@ -813,10 +833,12 @@ Only for things that don't matter. Public proxies are run by strangers who can r
813
833
 
814
834
  ## Roadmap
815
835
 
816
- What's next is tracked in the milestone [**v1.8**](../../milestone/8) – ideas and wishes are welcome as an [issue](../../issues/new/choose). During Hacktoberfest there are [beginner-friendly issues](../../issues?q=is%3Aopen+label%3Ahacktoberfest) with pointers on where to start.
836
+ What's next is in the [open issues](../../issues) – ideas and wishes are welcome as an [issue](../../issues/new/choose). During Hacktoberfest there are [beginner-friendly issues](../../issues?q=is%3Aopen+label%3Ahacktoberfest) with pointers on where to start.
837
+
838
+ - [ ] [Measure throughput, not just latency](../../issues/140)
839
+ - [ ] [Daily snapshots as a dataset](../../issues/143)
817
840
 
818
- - [ ] [Protocol detection on the same connection](../../issues/44)
819
- - [ ] [sing-box export](../../issues/113), [PowerShell completion](../../issues/114), [shareable filters on the website](../../issues/116)
841
+ Shipped in v1.8 and v1.9: uptime per proxy and `stable.txt`, which proxies get through to Google, Reddit and Amazon, `live_proxies()` in Python, a page per proxy with its week of checks, a weekly report, live charts in this README, shareable filters on the website, sources mined from other scrapers' lists, untyped lists tried as HTTP and SOCKS5, `--export singbox`, PowerShell completion and recipes for httpx, aiohttp and Scrapy.
820
842
 
821
843
  Shipped in [v1.7](../../milestone/7): an MCP server so AI agents get working proxies and can load pages through them, 28 new sources and a GitHub search that runs daily and keeps what it found, and a cleaner website.
822
844
 
@@ -861,7 +883,7 @@ proxyscraper/
861
883
  ├── geodb.py DB-IP country database (monthly, binary search)
862
884
  ├── targets.py target sites for --target
863
885
  ├── output.py result files
864
- ├── exporters.py proxychains, Clash and curl formats (--export)
886
+ ├── exporters.py proxychains, Clash, sing-box and curl formats (--export)
865
887
  ├── server/ rotating proxy server (--serve): pool · http · upstream · socks · status · core
866
888
  ├── api.py find_proxies() / check_proxies() for Python
867
889
  ├── agent.py MCP tools without the SDK: live list, filters, fetch through proxies
@@ -98,11 +98,12 @@ python3 proxy_scraper.py
98
98
 
99
99
  Every [release](https://github.com/maximilianfeix/proxy-scraper/releases/latest) also ships a wheel you can install with `pip install <file>.whl`, and a multi-arch image (amd64/arm64) on `ghcr.io`. In the container the wizard never shows up, it runs straight away. For the proxy server use `--serve --serve-host 0.0.0.0` with `-p 127.0.0.1:8899:8899`, so the port is only open on your own machine.
100
100
 
101
- Tab completion for bash, zsh and fish:
101
+ Tab completion for bash, zsh, fish and PowerShell:
102
102
 
103
103
  ```bash
104
104
  eval "$(proxy-scraper --completion zsh)" # in ~/.zshrc (after compinit), same for bash in ~/.bashrc
105
105
  proxy-scraper --completion fish > ~/.config/fish/completions/proxy-scraper.fish
106
+ proxy-scraper --completion powershell | Out-String | Invoke-Expression # in $PROFILE
106
107
  ```
107
108
 
108
109
  Installed, the learned state lives in your user data folder (`~/Library/Application Support/proxy-scraper`, `%LOCALAPPDATA%\proxy-scraper` or `~/.local/share/proxy-scraper`; override with `PROXY_SCRAPER_HOME`) and results go to `./results`. Run from a clone, both stay inside the project.
@@ -163,7 +164,7 @@ Every protocol and country also has its own page with a plain download, e.g. [SO
163
164
  | HTTP · SOCKS4 · SOCKS5 | `1.2.3.4:8080` | [http.txt](https://raw.githubusercontent.com/maximilianfeix/proxy-scraper/proxy-list/http.txt) · [socks4.txt](https://raw.githubusercontent.com/maximilianfeix/proxy-scraper/proxy-list/socks4.txt) · [socks5.txt](https://raw.githubusercontent.com/maximilianfeix/proxy-scraper/proxy-list/socks5.txt) |
164
165
  | HTTPS-capable only | `type://ip:port` | [https.txt](https://raw.githubusercontent.com/maximilianfeix/proxy-scraper/proxy-list/https.txt) |
165
166
  | Elite only | `type://ip:port` | [elite.txt](https://raw.githubusercontent.com/maximilianfeix/proxy-scraper/proxy-list/elite.txt) |
166
- | Gets through to Google · Reddit · Amazon (no captcha, no 403 in the last run) | `type://ip:port` | [google.txt](https://raw.githubusercontent.com/maximilianfeix/proxy-scraper/proxy-list/works-with/google.txt) · [reddit.txt](https://raw.githubusercontent.com/maximilianfeix/proxy-scraper/proxy-list/works-with/reddit.txt) · [amazon.txt](https://raw.githubusercontent.com/maximilianfeix/proxy-scraper/proxy-list/works-with/amazon.txt) |
167
+ | Gets through to Google · Reddit · Amazon · Instagram · TikTok (no captcha, no block in the last run) | `type://ip:port` | [google.txt](https://raw.githubusercontent.com/maximilianfeix/proxy-scraper/proxy-list/works-with/google.txt) · [reddit.txt](https://raw.githubusercontent.com/maximilianfeix/proxy-scraper/proxy-list/works-with/reddit.txt) · [amazon.txt](https://raw.githubusercontent.com/maximilianfeix/proxy-scraper/proxy-list/works-with/amazon.txt) · [instagram.txt](https://raw.githubusercontent.com/maximilianfeix/proxy-scraper/proxy-list/works-with/instagram.txt) · [tiktok.txt](https://raw.githubusercontent.com/maximilianfeix/proxy-scraper/proxy-list/works-with/tiktok.txt) |
167
168
  | Stable, on the list in 90 %+ of this week's runs | `type://ip:port` | [stable.txt](https://raw.githubusercontent.com/maximilianfeix/proxy-scraper/proxy-list/stable.txt) |
168
169
  | With all details | latency, country, HTTPS, anonymity, exit IP, uptime, sites | [proxies.json](https://raw.githubusercontent.com/maximilianfeix/proxy-scraper/proxy-list/proxies.json) · [proxies.csv](https://raw.githubusercontent.com/maximilianfeix/proxy-scraper/proxy-list/proxies.csv) |
169
170
 
@@ -177,6 +178,8 @@ Every protocol and country also has its own page with a plain download, e.g. [SO
177
178
  curl -s https://raw.githubusercontent.com/maximilianfeix/proxy-scraper/proxy-list/socks5.txt | head
178
179
  ```
179
180
 
181
+ **Over time:** every day the first run's `proxies.json` is kept for good as a gzipped asset of the release of that year, like [snapshots-2026](../../releases/tag/snapshots-2026) – `proxies-2026-09-28.json.gz` and so on, for anyone who wants to look at free proxies over weeks and months.
182
+
180
183
  Every file is also on GitHub Pages, which sits behind a CDN, isn't rate limited like raw.githubusercontent and sends CORS headers, so it works straight from the browser: `https://maximilianfeix.github.io/proxy-scraper/socks5.txt`. [jsDelivr](https://cdn.jsdelivr.net/gh/maximilianfeix/proxy-scraper@proxy-list/) works too, but can lag behind by a few hours.
181
184
 
182
185
  Or let the tool start from it: `proxy-scraper --recheck live` downloads the list and checks it again from **your** network – about 30 seconds instead of a full scan (517 of 1,169 worked from here). With `--serve` you have a rotating proxy in under a minute.
@@ -398,7 +401,7 @@ pool = itertools.cycle(p.url for p in proxies)
398
401
  r = requests.get("https://api.ipify.org", proxies={"https": next(pool)}, timeout=15) # pip install "requests[socks]"
399
402
  ```
400
403
 
401
- Same filters as `find_proxies`, plus `min_uptime`, `works_on` (`["google"]`, `"reddit"`, `"amazon"`) and `limit`. Every result also has `uptime_24h`, `uptime_7d`, `first_seen`, `up_for_hours` and `sites`.
404
+ Same filters as `find_proxies`, plus `min_uptime`, `works_on` (`["google"]`, `"reddit"`, `"amazon"`, `"instagram"`, `"tiktok"`) and `limit`. Every result also has `uptime_24h`, `uptime_7d`, `first_seen`, `up_for_hours` and `sites`.
402
405
 
403
406
  <a id="proxy-server"></a>
404
407
 
@@ -616,14 +619,14 @@ if __name__ == "__main__":
616
619
 
617
620
  In a Scrapy project, put `RotatingProxy` in `middlewares.py` and add it to `DOWNLOADER_MIDDLEWARES` in `settings.py`. For long crawls, reload the pool now and then – the list changes every hour.
618
621
 
619
- **proxychains, Clash / Mihomo** – ready-made configs with `--export`
622
+ **proxychains, Clash / Mihomo, sing-box** – ready-made configs with `--export`
620
623
 
621
624
  ```bash
622
- proxy-scraper --want 30 -y --export proxychains,clash
625
+ proxy-scraper --want 30 -y --export proxychains,clash,singbox
623
626
  proxychains4 -f results/latest/proxychains.conf curl https://api.ipify.org
624
627
  ```
625
628
 
626
- `clash.yaml` has all HTTP and SOCKS5 proxies plus a `url-test` group that always picks the fastest. Both configs leave out HTTP proxies that can't tunnel (`CONNECT`), because these tools tunnel everything.
629
+ `clash.yaml` has all HTTP and SOCKS5 proxies plus a `url-test` group that always picks the fastest. `singbox.json` does the same for sing-box, SOCKS4 included, and opens a local proxy: `sing-box run -c results/latest/singbox.json`, then use `127.0.0.1:2080` as HTTP or SOCKS5 proxy. All three leave out HTTP proxies that can't tunnel (`CONNECT`), because these tools tunnel everything.
627
630
 
628
631
  **In a pipe** – `-o -` prints the hits to stdout, the interface moves to stderr
629
632
 
@@ -639,6 +642,22 @@ export HTTPS_PROXY=http://127.0.0.1:8899 HTTP_PROXY=http://127.0.0.1:8899
639
642
  pip download requests # git, pip, npm & co. now go through the pool
640
643
  ```
641
644
 
645
+ **In a GitHub workflow** – the action picks working proxies for the next steps
646
+
647
+ ```yaml
648
+ - id: proxies
649
+ uses: maximilianfeix/proxy-scraper@v1
650
+ with:
651
+ types: socks5
652
+ https: true
653
+ min-uptime: 90 # on the list for 90 %+ of the week
654
+ works-on: google # optional: google, reddit, amazon
655
+ recheck: true # optional: check them again from the runner
656
+ - run: curl -x "${{ steps.proxies.outputs.proxy }}" https://api.ipify.org
657
+ ```
658
+
659
+ `proxy` is the fastest one, `file` a text file with all of them (up to `limit`, default 20), `count` how many. Without a match the step fails, unless `fail-if-empty: false`.
660
+
642
661
  **Without installing anything** – straight from the live list
643
662
 
644
663
  ```bash
@@ -682,16 +701,16 @@ curl.exe -x (Get-Content "$run\all.txt" -TotalCount 1) http://api.ipify.org
682
701
  | `-t`, `--timeout S` | timeout per proxy (default: 8 s) |
683
702
  | `--discover` | search GitHub for new sources right now |
684
703
  | `--no-cache` | download every list again (unchanged ones are normally skipped via ETag) |
685
- | `--list-sources [N]` | show the source ranking |
704
+ | `--list-sources [N]` | show the source ranking (add `--json` for scripts: url, status, hit rate, checks, last change) |
686
705
  | `--serve-host ADDR` | where the proxy server listens (default `127.0.0.1`; `0.0.0.0` for Docker, with a warning) |
687
706
  | `--serve-password SECRET` | clients must send this password in the proxy login; better set `PROXY_SCRAPER_SERVE_PASSWORD` |
688
707
  | `--rotate STRATEGY` · `--sticky SEC` | how the proxy server picks proxies, see [above](#proxy-server) |
689
708
  | `--serve-refill HOURS` | while serving, check fresh proxies every HOURS and add the hits to the pool |
690
709
  | `--serve [PORT]` | afterwards serve as a rotating proxy on `127.0.0.1:PORT` (default: 8899) |
691
710
  | `-o FILE` | also write all hits to this file; `-o -` prints them to stdout |
692
- | `--export FORMATS` | extra files for other tools: `proxychains`, `clash`, `curl` or `all` |
711
+ | `--export FORMATS` | extra files for other tools: `proxychains`, `clash`, `singbox`, `curl` or `all` |
693
712
  | `-V`, `--version` | print the version |
694
- | `--completion SHELL` | print the tab completion script for bash, zsh or fish |
713
+ | `--completion SHELL` | print the tab completion script for bash, zsh, fish or PowerShell |
695
714
 
696
715
  Everything else: `proxy-scraper --help`
697
716
 
@@ -773,10 +792,12 @@ Only for things that don't matter. Public proxies are run by strangers who can r
773
792
 
774
793
  ## Roadmap
775
794
 
776
- What's next is tracked in the milestone [**v1.8**](../../milestone/8) – ideas and wishes are welcome as an [issue](../../issues/new/choose). During Hacktoberfest there are [beginner-friendly issues](../../issues?q=is%3Aopen+label%3Ahacktoberfest) with pointers on where to start.
795
+ What's next is in the [open issues](../../issues) – ideas and wishes are welcome as an [issue](../../issues/new/choose). During Hacktoberfest there are [beginner-friendly issues](../../issues?q=is%3Aopen+label%3Ahacktoberfest) with pointers on where to start.
796
+
797
+ - [ ] [Measure throughput, not just latency](../../issues/140)
798
+ - [ ] [Daily snapshots as a dataset](../../issues/143)
777
799
 
778
- - [ ] [Protocol detection on the same connection](../../issues/44)
779
- - [ ] [sing-box export](../../issues/113), [PowerShell completion](../../issues/114), [shareable filters on the website](../../issues/116)
800
+ Shipped in v1.8 and v1.9: uptime per proxy and `stable.txt`, which proxies get through to Google, Reddit and Amazon, `live_proxies()` in Python, a page per proxy with its week of checks, a weekly report, live charts in this README, shareable filters on the website, sources mined from other scrapers' lists, untyped lists tried as HTTP and SOCKS5, `--export singbox`, PowerShell completion and recipes for httpx, aiohttp and Scrapy.
780
801
 
781
802
  Shipped in [v1.7](../../milestone/7): an MCP server so AI agents get working proxies and can load pages through them, 28 new sources and a GitHub search that runs daily and keeps what it found, and a cleaner website.
782
803
 
@@ -821,7 +842,7 @@ proxyscraper/
821
842
  ├── geodb.py DB-IP country database (monthly, binary search)
822
843
  ├── targets.py target sites for --target
823
844
  ├── output.py result files
824
- ├── exporters.py proxychains, Clash and curl formats (--export)
845
+ ├── exporters.py proxychains, Clash, sing-box and curl formats (--export)
825
846
  ├── server/ rotating proxy server (--serve): pool · http · upstream · socks · status · core
826
847
  ├── api.py find_proxies() / check_proxies() for Python
827
848
  ├── agent.py MCP tools without the SDK: live list, filters, fetch through proxies
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: proxy-scraper-cli
3
- Version: 1.9.0
3
+ Version: 1.11.0
4
4
  Summary: Finds free HTTP, SOCKS4 and SOCKS5 proxies that actually work: 700+ sources, real checks, honeypot filtering and a rotating local proxy server.
5
5
  Author: Maximilian Feix
6
6
  License-Expression: MIT
@@ -35,6 +35,7 @@ Provides-Extra: mcp
35
35
  Requires-Dist: mcp<3,>=2.2; python_version >= "3.10" and extra == "mcp"
36
36
  Provides-Extra: dev
37
37
  Requires-Dist: pytest; extra == "dev"
38
+ Requires-Dist: hypothesis; extra == "dev"
38
39
  Requires-Dist: ruff==0.16.8; extra == "dev"
39
40
  Dynamic: license-file
40
41
 
@@ -138,11 +139,12 @@ python3 proxy_scraper.py
138
139
 
139
140
  Every [release](https://github.com/maximilianfeix/proxy-scraper/releases/latest) also ships a wheel you can install with `pip install <file>.whl`, and a multi-arch image (amd64/arm64) on `ghcr.io`. In the container the wizard never shows up, it runs straight away. For the proxy server use `--serve --serve-host 0.0.0.0` with `-p 127.0.0.1:8899:8899`, so the port is only open on your own machine.
140
141
 
141
- Tab completion for bash, zsh and fish:
142
+ Tab completion for bash, zsh, fish and PowerShell:
142
143
 
143
144
  ```bash
144
145
  eval "$(proxy-scraper --completion zsh)" # in ~/.zshrc (after compinit), same for bash in ~/.bashrc
145
146
  proxy-scraper --completion fish > ~/.config/fish/completions/proxy-scraper.fish
147
+ proxy-scraper --completion powershell | Out-String | Invoke-Expression # in $PROFILE
146
148
  ```
147
149
 
148
150
  Installed, the learned state lives in your user data folder (`~/Library/Application Support/proxy-scraper`, `%LOCALAPPDATA%\proxy-scraper` or `~/.local/share/proxy-scraper`; override with `PROXY_SCRAPER_HOME`) and results go to `./results`. Run from a clone, both stay inside the project.
@@ -203,7 +205,7 @@ Every protocol and country also has its own page with a plain download, e.g. [SO
203
205
  | HTTP · SOCKS4 · SOCKS5 | `1.2.3.4:8080` | [http.txt](https://raw.githubusercontent.com/maximilianfeix/proxy-scraper/proxy-list/http.txt) · [socks4.txt](https://raw.githubusercontent.com/maximilianfeix/proxy-scraper/proxy-list/socks4.txt) · [socks5.txt](https://raw.githubusercontent.com/maximilianfeix/proxy-scraper/proxy-list/socks5.txt) |
204
206
  | HTTPS-capable only | `type://ip:port` | [https.txt](https://raw.githubusercontent.com/maximilianfeix/proxy-scraper/proxy-list/https.txt) |
205
207
  | Elite only | `type://ip:port` | [elite.txt](https://raw.githubusercontent.com/maximilianfeix/proxy-scraper/proxy-list/elite.txt) |
206
- | Gets through to Google · Reddit · Amazon (no captcha, no 403 in the last run) | `type://ip:port` | [google.txt](https://raw.githubusercontent.com/maximilianfeix/proxy-scraper/proxy-list/works-with/google.txt) · [reddit.txt](https://raw.githubusercontent.com/maximilianfeix/proxy-scraper/proxy-list/works-with/reddit.txt) · [amazon.txt](https://raw.githubusercontent.com/maximilianfeix/proxy-scraper/proxy-list/works-with/amazon.txt) |
208
+ | Gets through to Google · Reddit · Amazon · Instagram · TikTok (no captcha, no block in the last run) | `type://ip:port` | [google.txt](https://raw.githubusercontent.com/maximilianfeix/proxy-scraper/proxy-list/works-with/google.txt) · [reddit.txt](https://raw.githubusercontent.com/maximilianfeix/proxy-scraper/proxy-list/works-with/reddit.txt) · [amazon.txt](https://raw.githubusercontent.com/maximilianfeix/proxy-scraper/proxy-list/works-with/amazon.txt) · [instagram.txt](https://raw.githubusercontent.com/maximilianfeix/proxy-scraper/proxy-list/works-with/instagram.txt) · [tiktok.txt](https://raw.githubusercontent.com/maximilianfeix/proxy-scraper/proxy-list/works-with/tiktok.txt) |
207
209
  | Stable, on the list in 90 %+ of this week's runs | `type://ip:port` | [stable.txt](https://raw.githubusercontent.com/maximilianfeix/proxy-scraper/proxy-list/stable.txt) |
208
210
  | With all details | latency, country, HTTPS, anonymity, exit IP, uptime, sites | [proxies.json](https://raw.githubusercontent.com/maximilianfeix/proxy-scraper/proxy-list/proxies.json) · [proxies.csv](https://raw.githubusercontent.com/maximilianfeix/proxy-scraper/proxy-list/proxies.csv) |
209
211
 
@@ -217,6 +219,8 @@ Every protocol and country also has its own page with a plain download, e.g. [SO
217
219
  curl -s https://raw.githubusercontent.com/maximilianfeix/proxy-scraper/proxy-list/socks5.txt | head
218
220
  ```
219
221
 
222
+ **Over time:** every day the first run's `proxies.json` is kept for good as a gzipped asset of the release of that year, like [snapshots-2026](../../releases/tag/snapshots-2026) – `proxies-2026-09-28.json.gz` and so on, for anyone who wants to look at free proxies over weeks and months.
223
+
220
224
  Every file is also on GitHub Pages, which sits behind a CDN, isn't rate limited like raw.githubusercontent and sends CORS headers, so it works straight from the browser: `https://maximilianfeix.github.io/proxy-scraper/socks5.txt`. [jsDelivr](https://cdn.jsdelivr.net/gh/maximilianfeix/proxy-scraper@proxy-list/) works too, but can lag behind by a few hours.
221
225
 
222
226
  Or let the tool start from it: `proxy-scraper --recheck live` downloads the list and checks it again from **your** network – about 30 seconds instead of a full scan (517 of 1,169 worked from here). With `--serve` you have a rotating proxy in under a minute.
@@ -438,7 +442,7 @@ pool = itertools.cycle(p.url for p in proxies)
438
442
  r = requests.get("https://api.ipify.org", proxies={"https": next(pool)}, timeout=15) # pip install "requests[socks]"
439
443
  ```
440
444
 
441
- Same filters as `find_proxies`, plus `min_uptime`, `works_on` (`["google"]`, `"reddit"`, `"amazon"`) and `limit`. Every result also has `uptime_24h`, `uptime_7d`, `first_seen`, `up_for_hours` and `sites`.
445
+ Same filters as `find_proxies`, plus `min_uptime`, `works_on` (`["google"]`, `"reddit"`, `"amazon"`, `"instagram"`, `"tiktok"`) and `limit`. Every result also has `uptime_24h`, `uptime_7d`, `first_seen`, `up_for_hours` and `sites`.
442
446
 
443
447
  <a id="proxy-server"></a>
444
448
 
@@ -656,14 +660,14 @@ if __name__ == "__main__":
656
660
 
657
661
  In a Scrapy project, put `RotatingProxy` in `middlewares.py` and add it to `DOWNLOADER_MIDDLEWARES` in `settings.py`. For long crawls, reload the pool now and then – the list changes every hour.
658
662
 
659
- **proxychains, Clash / Mihomo** – ready-made configs with `--export`
663
+ **proxychains, Clash / Mihomo, sing-box** – ready-made configs with `--export`
660
664
 
661
665
  ```bash
662
- proxy-scraper --want 30 -y --export proxychains,clash
666
+ proxy-scraper --want 30 -y --export proxychains,clash,singbox
663
667
  proxychains4 -f results/latest/proxychains.conf curl https://api.ipify.org
664
668
  ```
665
669
 
666
- `clash.yaml` has all HTTP and SOCKS5 proxies plus a `url-test` group that always picks the fastest. Both configs leave out HTTP proxies that can't tunnel (`CONNECT`), because these tools tunnel everything.
670
+ `clash.yaml` has all HTTP and SOCKS5 proxies plus a `url-test` group that always picks the fastest. `singbox.json` does the same for sing-box, SOCKS4 included, and opens a local proxy: `sing-box run -c results/latest/singbox.json`, then use `127.0.0.1:2080` as HTTP or SOCKS5 proxy. All three leave out HTTP proxies that can't tunnel (`CONNECT`), because these tools tunnel everything.
667
671
 
668
672
  **In a pipe** – `-o -` prints the hits to stdout, the interface moves to stderr
669
673
 
@@ -679,6 +683,22 @@ export HTTPS_PROXY=http://127.0.0.1:8899 HTTP_PROXY=http://127.0.0.1:8899
679
683
  pip download requests # git, pip, npm & co. now go through the pool
680
684
  ```
681
685
 
686
+ **In a GitHub workflow** – the action picks working proxies for the next steps
687
+
688
+ ```yaml
689
+ - id: proxies
690
+ uses: maximilianfeix/proxy-scraper@v1
691
+ with:
692
+ types: socks5
693
+ https: true
694
+ min-uptime: 90 # on the list for 90 %+ of the week
695
+ works-on: google # optional: google, reddit, amazon
696
+ recheck: true # optional: check them again from the runner
697
+ - run: curl -x "${{ steps.proxies.outputs.proxy }}" https://api.ipify.org
698
+ ```
699
+
700
+ `proxy` is the fastest one, `file` a text file with all of them (up to `limit`, default 20), `count` how many. Without a match the step fails, unless `fail-if-empty: false`.
701
+
682
702
  **Without installing anything** – straight from the live list
683
703
 
684
704
  ```bash
@@ -722,16 +742,16 @@ curl.exe -x (Get-Content "$run\all.txt" -TotalCount 1) http://api.ipify.org
722
742
  | `-t`, `--timeout S` | timeout per proxy (default: 8 s) |
723
743
  | `--discover` | search GitHub for new sources right now |
724
744
  | `--no-cache` | download every list again (unchanged ones are normally skipped via ETag) |
725
- | `--list-sources [N]` | show the source ranking |
745
+ | `--list-sources [N]` | show the source ranking (add `--json` for scripts: url, status, hit rate, checks, last change) |
726
746
  | `--serve-host ADDR` | where the proxy server listens (default `127.0.0.1`; `0.0.0.0` for Docker, with a warning) |
727
747
  | `--serve-password SECRET` | clients must send this password in the proxy login; better set `PROXY_SCRAPER_SERVE_PASSWORD` |
728
748
  | `--rotate STRATEGY` · `--sticky SEC` | how the proxy server picks proxies, see [above](#proxy-server) |
729
749
  | `--serve-refill HOURS` | while serving, check fresh proxies every HOURS and add the hits to the pool |
730
750
  | `--serve [PORT]` | afterwards serve as a rotating proxy on `127.0.0.1:PORT` (default: 8899) |
731
751
  | `-o FILE` | also write all hits to this file; `-o -` prints them to stdout |
732
- | `--export FORMATS` | extra files for other tools: `proxychains`, `clash`, `curl` or `all` |
752
+ | `--export FORMATS` | extra files for other tools: `proxychains`, `clash`, `singbox`, `curl` or `all` |
733
753
  | `-V`, `--version` | print the version |
734
- | `--completion SHELL` | print the tab completion script for bash, zsh or fish |
754
+ | `--completion SHELL` | print the tab completion script for bash, zsh, fish or PowerShell |
735
755
 
736
756
  Everything else: `proxy-scraper --help`
737
757
 
@@ -813,10 +833,12 @@ Only for things that don't matter. Public proxies are run by strangers who can r
813
833
 
814
834
  ## Roadmap
815
835
 
816
- What's next is tracked in the milestone [**v1.8**](../../milestone/8) – ideas and wishes are welcome as an [issue](../../issues/new/choose). During Hacktoberfest there are [beginner-friendly issues](../../issues?q=is%3Aopen+label%3Ahacktoberfest) with pointers on where to start.
836
+ What's next is in the [open issues](../../issues) – ideas and wishes are welcome as an [issue](../../issues/new/choose). During Hacktoberfest there are [beginner-friendly issues](../../issues?q=is%3Aopen+label%3Ahacktoberfest) with pointers on where to start.
837
+
838
+ - [ ] [Measure throughput, not just latency](../../issues/140)
839
+ - [ ] [Daily snapshots as a dataset](../../issues/143)
817
840
 
818
- - [ ] [Protocol detection on the same connection](../../issues/44)
819
- - [ ] [sing-box export](../../issues/113), [PowerShell completion](../../issues/114), [shareable filters on the website](../../issues/116)
841
+ Shipped in v1.8 and v1.9: uptime per proxy and `stable.txt`, which proxies get through to Google, Reddit and Amazon, `live_proxies()` in Python, a page per proxy with its week of checks, a weekly report, live charts in this README, shareable filters on the website, sources mined from other scrapers' lists, untyped lists tried as HTTP and SOCKS5, `--export singbox`, PowerShell completion and recipes for httpx, aiohttp and Scrapy.
820
842
 
821
843
  Shipped in [v1.7](../../milestone/7): an MCP server so AI agents get working proxies and can load pages through them, 28 new sources and a GitHub search that runs daily and keeps what it found, and a cleaner website.
822
844
 
@@ -861,7 +883,7 @@ proxyscraper/
861
883
  ├── geodb.py DB-IP country database (monthly, binary search)
862
884
  ├── targets.py target sites for --target
863
885
  ├── output.py result files
864
- ├── exporters.py proxychains, Clash and curl formats (--export)
886
+ ├── exporters.py proxychains, Clash, sing-box and curl formats (--export)
865
887
  ├── server/ rotating proxy server (--serve): pool · http · upstream · socks · status · core
866
888
  ├── api.py find_proxies() / check_proxies() for Python
867
889
  ├── agent.py MCP tools without the SDK: live list, filters, fetch through proxies
@@ -9,6 +9,7 @@ proxy_scraper_cli.egg-info/requires.txt
9
9
  proxy_scraper_cli.egg-info/top_level.txt
10
10
  proxyscraper/__init__.py
11
11
  proxyscraper/__main__.py
12
+ proxyscraper/action.py
12
13
  proxyscraper/agent.py
13
14
  proxyscraper/api.py
14
15
  proxyscraper/app.py
@@ -42,6 +43,7 @@ proxyscraper/report.py
42
43
  proxyscraper/sites.py
43
44
  proxyscraper/sources.json
44
45
  proxyscraper/sources.py
46
+ proxyscraper/speed.py
45
47
  proxyscraper/targets.py
46
48
  proxyscraper/server/__init__.py
47
49
  proxyscraper/server/core.py
@@ -62,6 +64,7 @@ proxyscraper/ui/report.py
62
64
  proxyscraper/ui/serve.py
63
65
  proxyscraper/ui/widgets.py
64
66
  proxyscraper/ui/wizard.py
67
+ tests/test_action.py
65
68
  tests/test_agent.py
66
69
  tests/test_api.py
67
70
  tests/test_app.py
@@ -86,6 +89,7 @@ tests/test_output_stdout.py
86
89
  tests/test_packaging.py
87
90
  tests/test_pages.py
88
91
  tests/test_parsing.py
92
+ tests/test_parsing_fuzz.py
89
93
  tests/test_paths.py
90
94
  tests/test_pipeline.py
91
95
  tests/test_proxy_pages.py
@@ -97,8 +101,11 @@ tests/test_serve_password.py
97
101
  tests/test_serve_refill.py
98
102
  tests/test_serve_v2.py
99
103
  tests/test_server.py
104
+ tests/test_server_closed_writer.py
100
105
  tests/test_server_fixes.py
101
106
  tests/test_sites.py
107
+ tests/test_sources_json.py
108
+ tests/test_speed.py
102
109
  tests/test_tamper.py
103
110
  tests/test_targets.py
104
111
  tests/test_ui.py
@@ -3,6 +3,7 @@ certifi
3
3
 
4
4
  [dev]
5
5
  pytest
6
+ hypothesis
6
7
  ruff==0.16.8
7
8
 
8
9
  [fast]
@@ -1,7 +1,7 @@
1
1
  """proxy-scraper: collects public HTTP/SOCKS4/SOCKS5 proxies from hundreds of sources, checks them
2
2
  in parallel with its own protocol handshakes and learns which sources deliver good proxies."""
3
3
 
4
- __version__ = "1.9.0"
4
+ __version__ = "1.11.0"
5
5
 
6
6
 
7
7
  def __getattr__(name):
@@ -0,0 +1,96 @@
1
+ """The GitHub Action (action.yml): working proxies from the hourly list for the next steps of a workflow.
2
+
3
+ Inputs come in as PS_* environment variables, results go to $GITHUB_OUTPUT:
4
+ proxy the fastest one, ready to use (socks5://1.2.3.4:1080)
5
+ file a text file with all of them, one per line, fastest first
6
+ count how many
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import os
12
+ import sys
13
+ import tempfile
14
+ from pathlib import Path
15
+ from typing import List
16
+
17
+ from .api import check_proxies, live_proxies
18
+
19
+
20
+ class InputError(ValueError):
21
+ pass
22
+
23
+
24
+ def _flag(name: str, default: bool = False) -> bool:
25
+ value = os.environ.get(name, "").strip().lower()
26
+ if not value:
27
+ return default
28
+ if value in ("true", "yes", "1"):
29
+ return True
30
+ if value in ("false", "no", "0"):
31
+ return False
32
+ raise InputError(f"{name[3:].lower().replace('_', '-')} must be true or false, not {value!r}")
33
+
34
+
35
+ def _number(name: str, default: int) -> int:
36
+ value = os.environ.get(name, "").strip()
37
+ if not value:
38
+ return default
39
+ if not value.isdigit():
40
+ raise InputError(f"{name[3:].lower().replace('_', '-')} must be a whole number, not {value!r}")
41
+ return int(value)
42
+
43
+
44
+ def _list(name: str) -> List[str]:
45
+ return [part.strip().lower() for part in os.environ.get(name, "").split(",") if part.strip()]
46
+
47
+
48
+ def _output(**values: str) -> None:
49
+ path = os.environ.get("GITHUB_OUTPUT")
50
+ lines = "".join(f"{key}={value}\n" for key, value in values.items())
51
+ if path:
52
+ with open(path, "a", encoding="utf-8") as fh:
53
+ fh.write(lines)
54
+ else:
55
+ sys.stdout.write(lines)
56
+
57
+
58
+ def main() -> int:
59
+ try:
60
+ limit = _number("PS_LIMIT", 20)
61
+ recheck = _flag("PS_RECHECK")
62
+ fail_if_empty = _flag("PS_FAIL_IF_EMPTY", True)
63
+ filters = {"types": _list("PS_TYPES") or ["http", "socks4", "socks5"],
64
+ "countries": os.environ.get("PS_COUNTRIES", "").strip(),
65
+ "https": _flag("PS_HTTPS"), "min_uptime": _number("PS_MIN_UPTIME", 0),
66
+ "works_on": _list("PS_WORKS_ON")}
67
+ # a recheck starts from all of them – the limit applies to what still works from here
68
+ found = live_proxies(**filters, limit=0 if recheck else limit)
69
+ except (InputError, ValueError) as e:
70
+ print(f"::error::{e}")
71
+ return 1
72
+ except ConnectionError as e:
73
+ print(f"::error::The live list couldn't be loaded: {e}")
74
+ return 1
75
+ urls = [p.url for p in found]
76
+ if recheck and urls:
77
+ print(f"Checking {len(urls)} proxies again from this runner …")
78
+ urls = [r.url for r in sorted(check_proxies(urls, https=filters["https"]), key=lambda r: r.latency)]
79
+ if limit:
80
+ urls = urls[:limit]
81
+ # a file of its own per step: a job may use the action twice (socks5 in one step, http in the next)
82
+ fd, name = tempfile.mkstemp(prefix="proxies-", suffix=".txt", dir=os.environ.get("RUNNER_TEMP") or None)
83
+ with os.fdopen(fd, "w", encoding="utf-8") as fh:
84
+ fh.write("".join(f"{u}\n" for u in urls))
85
+ file = Path(name)
86
+ _output(proxy=urls[0] if urls else "", file=str(file), count=str(len(urls)))
87
+ print(f"{len(urls)} working {'proxy' if len(urls) == 1 else 'proxies'}" + (f", fastest: {urls[0]}" if urls else ""))
88
+ if not urls and fail_if_empty:
89
+ print("::error::No proxy matches these filters right now – loosen them (min-uptime, works-on, countries) "
90
+ "or set fail-if-empty: false")
91
+ return 1
92
+ return 0
93
+
94
+
95
+ if __name__ == "__main__":
96
+ sys.exit(main())
@@ -179,17 +179,18 @@ def normalize_countries(countries: Iterable[str]) -> List[str]:
179
179
  def select(rows: Iterable[dict], protocol: str = "any", countries: Iterable[str] = (), https_only: bool = False,
180
180
  elite_only: bool = False, exclude_datacenter: bool = False, exclude_blocklisted: bool = False,
181
181
  stable_only: bool = False, max_latency_ms: int = 0, run_hours: int = 1, min_uptime: int = 0,
182
- works_on: Iterable[str] = ()) -> List[dict]:
182
+ works_on: Iterable[str] = (), min_speed_kbps: int = 0) -> List[dict]:
183
183
  """The rows that pass every filter, fastest first – the same filters as on the website."""
184
184
  return sorted(matching(rows, protocol, countries, https_only, elite_only, exclude_datacenter,
185
- exclude_blocklisted, stable_only, max_latency_ms, run_hours, min_uptime, works_on),
185
+ exclude_blocklisted, stable_only, max_latency_ms, run_hours, min_uptime, works_on,
186
+ min_speed_kbps),
186
187
  key=lambda r: r.get("latency") or 0)
187
188
 
188
189
 
189
190
  def matching(rows: Iterable[dict], protocol: str = "any", countries: Iterable[str] = (), https_only: bool = False,
190
191
  elite_only: bool = False, exclude_datacenter: bool = False, exclude_blocklisted: bool = False,
191
192
  stable_only: bool = False, max_latency_ms: int = 0, run_hours: int = 1,
192
- min_uptime: int = 0, works_on: Iterable[str] = ()) -> Iterator[dict]:
193
+ min_uptime: int = 0, works_on: Iterable[str] = (), min_speed_kbps: int = 0) -> Iterator[dict]:
193
194
  """The rows that pass every filter, in list order – checks the filters before the first row is asked for."""
194
195
  protocol = _check_protocol(protocol)
195
196
  wanted = set(normalize_countries(countries))
@@ -205,7 +206,8 @@ def matching(rows: Iterable[dict], protocol: str = "any", countries: Iterable[st
205
206
  and (not stable_only or (r.get("streak") or 0) >= stable_runs)
206
207
  and (not max_latency_ms or (r.get("latency") or 0) <= max_latency_ms)
207
208
  and (not min_uptime or (r.get("uptime_7d") or 0) >= min_uptime)
208
- and all((r.get("sites") or {}).get(s) for s in sites))
209
+ and all((r.get("sites") or {}).get(s) for s in sites)
210
+ and (not min_speed_kbps or (r.get("speed_kbps") or 0) >= min_speed_kbps))
209
211
 
210
212
 
211
213
  def describe(item: Union[dict, CheckResult], run_hours: Optional[int] = None) -> dict:
@@ -229,6 +231,7 @@ def describe(item: Union[dict, CheckResult], run_hours: Optional[int] = None) ->
229
231
  "up_for_hours": streak * run_hours if streak and run_hours else None,
230
232
  "uptime_7d_percent": item.get("uptime_7d"),
231
233
  "works_on": [name for name, ok in (item.get("sites") or {}).items() if ok],
234
+ "speed_kbps": item.get("speed_kbps"),
232
235
  }
233
236
 
234
237