sursolver 0.1.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.
- sursolver-0.1.0/.gitignore +26 -0
- sursolver-0.1.0/CHANGELOG.md +16 -0
- sursolver-0.1.0/LICENSE +21 -0
- sursolver-0.1.0/PKG-INFO +235 -0
- sursolver-0.1.0/README.md +209 -0
- sursolver-0.1.0/pyproject.toml +63 -0
- sursolver-0.1.0/src/sursolver/__init__.py +86 -0
- sursolver-0.1.0/src/sursolver/_async_client.py +191 -0
- sursolver-0.1.0/src/sursolver/_client.py +194 -0
- sursolver-0.1.0/src/sursolver/_core.py +362 -0
- sursolver-0.1.0/src/sursolver/_errors.py +163 -0
- sursolver-0.1.0/src/sursolver/_types.py +105 -0
- sursolver-0.1.0/src/sursolver/_version.py +2 -0
- sursolver-0.1.0/src/sursolver/py.typed +0 -0
- sursolver-0.1.0/src/sursolver/task_types.py +75 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Secrets
|
|
2
|
+
.env
|
|
3
|
+
.env.*
|
|
4
|
+
!.env.example
|
|
5
|
+
|
|
6
|
+
# Editors and OS
|
|
7
|
+
.idea/
|
|
8
|
+
.vscode/
|
|
9
|
+
.vs/
|
|
10
|
+
*.swp
|
|
11
|
+
.DS_Store
|
|
12
|
+
Thumbs.db
|
|
13
|
+
|
|
14
|
+
# Python
|
|
15
|
+
__pycache__/
|
|
16
|
+
*.py[cod]
|
|
17
|
+
.venv/
|
|
18
|
+
venv/
|
|
19
|
+
*.egg-info/
|
|
20
|
+
build/
|
|
21
|
+
dist/
|
|
22
|
+
.pytest_cache/
|
|
23
|
+
.mypy_cache/
|
|
24
|
+
.ruff_cache/
|
|
25
|
+
htmlcov/
|
|
26
|
+
.coverage
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0 — unreleased
|
|
4
|
+
|
|
5
|
+
First release. Tested against the SDK specification revision in `SPEC_VERSION` (catalog of
|
|
6
|
+
2026-11-04).
|
|
7
|
+
|
|
8
|
+
- `SurSolver` (synchronous) and `AsyncSurSolver` (asyncio) clients: `create_task`,
|
|
9
|
+
`get_task_result`, `get_balance`, `list_task_types`, `list_services` and the `solve()`
|
|
10
|
+
helper (3 s polling, 210 s deadline, `on_task_created`, asyncio cancellation).
|
|
11
|
+
- Typed errors: `AuthenticationError`, `InvalidTaskError` (with `supported_user_agents`),
|
|
12
|
+
`InsufficientBalanceError`, `NoSlotAvailableError`, `TaskNotFoundError`, `TaskFailedError`,
|
|
13
|
+
`ApiError`, `TransportError` (with `may_have_created_task`), `ProtocolError`,
|
|
14
|
+
`SolveTimeoutError`, `ConfigurationError`.
|
|
15
|
+
- `TaskType` and `ErrorCode` enums generated from the public catalog.
|
|
16
|
+
- One dependency: `httpx`. Fully typed (`py.typed`).
|
sursolver-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 SurSolver
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
sursolver-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: sursolver
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Official Python captcha solver API client for reCAPTCHA v3, Cloudflare Turnstile, Cloudflare Challenge, GeeTest, DataDome, AWS WAF, Akamai, Kasada and image captchas.
|
|
5
|
+
Project-URL: Homepage, https://sursolver.com
|
|
6
|
+
Project-URL: Documentation, https://sursolver.com/docs/api
|
|
7
|
+
Project-URL: Repository, https://github.com/sursolver/sursolver-python
|
|
8
|
+
Project-URL: Issues, https://github.com/sursolver/sursolver-python/issues
|
|
9
|
+
Author: SurSolver
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: akamai,asyncio,aws-waf,captcha,captcha-solver,cf-clearance,cloudflare,datadome,geetest,image-captcha,kasada,recaptcha,recaptcha-v3,sursolver,turnstile
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
22
|
+
Classifier: Typing :: Typed
|
|
23
|
+
Requires-Python: >=3.11
|
|
24
|
+
Requires-Dist: httpx<1,>=0.27
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
# SurSolver for Python — captcha solver API client
|
|
28
|
+
|
|
29
|
+
Solve **reCAPTCHA v3**, **Cloudflare Turnstile**, **Cloudflare Challenge**, **GeeTest**,
|
|
30
|
+
**DataDome**, **AWS WAF**, **Akamai**, **Kasada** and image captchas from Python with the
|
|
31
|
+
official [SurSolver](https://sursolver.com) client. One call submits the captcha and returns the
|
|
32
|
+
token or cookie. Works with plain Python and asyncio.
|
|
33
|
+
|
|
34
|
+
```powershell
|
|
35
|
+
pip install sursolver
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Quick start
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
from sursolver import SurSolver, TaskType
|
|
42
|
+
|
|
43
|
+
API_KEY = "YOUR_API_KEY"
|
|
44
|
+
USER_AGENT = (
|
|
45
|
+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
|
|
46
|
+
"(KHTML, like Gecko) Chrome/155.0.0.0 Safari/537.36"
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
client = SurSolver(API_KEY)
|
|
50
|
+
|
|
51
|
+
result = client.solve({
|
|
52
|
+
"type": TaskType.TurnstileTokenProxyLess,
|
|
53
|
+
"websiteURL": "https://example.com/login",
|
|
54
|
+
"websiteKey": "0x4AAAAAAA...",
|
|
55
|
+
"userAgent": USER_AGENT,
|
|
56
|
+
})
|
|
57
|
+
print(result.solution["token"])
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Send the same `userAgent` with the request that uses the result. Tokens and cookies are
|
|
61
|
+
single-use, so use them right away. The examples below reuse `client` and `USER_AGENT`.
|
|
62
|
+
|
|
63
|
+
## Examples
|
|
64
|
+
|
|
65
|
+
### Solve reCAPTCHA v3 in Python
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
result = client.solve({
|
|
69
|
+
"type": TaskType.ReCaptchaV3TokenProxyLess,
|
|
70
|
+
"websiteURL": "https://example.com/login",
|
|
71
|
+
"websiteKey": "6Le-wvkS...",
|
|
72
|
+
"pageAction": "login",
|
|
73
|
+
"userAgent": USER_AGENT,
|
|
74
|
+
})
|
|
75
|
+
print(result.solution["gRecaptchaResponse"])
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
For reCAPTCHA v3 Enterprise use `TaskType.ReCaptchaV3EnterpriseTokenProxyLess`.
|
|
79
|
+
|
|
80
|
+
### Solve Cloudflare Turnstile
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
result = client.solve({
|
|
84
|
+
"type": TaskType.TurnstileTokenProxyLess,
|
|
85
|
+
"websiteURL": "https://example.com/signup",
|
|
86
|
+
"websiteKey": "0x4AAAAAAA...",
|
|
87
|
+
"userAgent": USER_AGENT,
|
|
88
|
+
})
|
|
89
|
+
# Submit result.solution["token"] as the cf-turnstile-response form field.
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Bypass the Cloudflare Challenge page (cf_clearance)
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
result = client.solve({
|
|
96
|
+
"type": TaskType.CloudflareChallengeCookie,
|
|
97
|
+
"websiteURL": "https://example.com/protected",
|
|
98
|
+
"proxy": "http://user:password@203.0.113.10:8000",
|
|
99
|
+
"userAgent": USER_AGENT,
|
|
100
|
+
})
|
|
101
|
+
print(result.solution["cf_clearance"])
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Replay the cookie through the same proxy and with the same `userAgent`.
|
|
105
|
+
|
|
106
|
+
### Solve GeeTest v4
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
result = client.solve({
|
|
110
|
+
"type": TaskType.GeeTestTokenProxyLess,
|
|
111
|
+
"websiteURL": "https://example.com/login",
|
|
112
|
+
"captchaId": "e392e1d7fd421dc63325744d5a2b9c73",
|
|
113
|
+
"userAgent": USER_AGENT,
|
|
114
|
+
})
|
|
115
|
+
print(result.solution["lot_number"], result.solution["pass_token"])
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
GeeTest v3: send `gt` and a fresh `challenge` instead of `captchaId`.
|
|
119
|
+
|
|
120
|
+
### Solve the DataDome slider
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
result = client.solve({
|
|
124
|
+
"type": TaskType.DataDomeSliderCookie,
|
|
125
|
+
"websiteURL": "https://example.com/",
|
|
126
|
+
"captchaUrl": "https://geo.captcha-delivery.com/captcha/?initialCid=...",
|
|
127
|
+
"proxy": "http://user:password@203.0.113.10:8000",
|
|
128
|
+
"userAgent": USER_AGENT,
|
|
129
|
+
})
|
|
130
|
+
print(result.solution["datadome"])
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Get an AWS WAF token (silent challenge)
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
result = client.solve({
|
|
137
|
+
"type": TaskType.AwsChallengeCookie,
|
|
138
|
+
"websiteURL": "https://example.com/",
|
|
139
|
+
"scriptUrl": "https://example.token.awswaf.com/example/challenge.js",
|
|
140
|
+
"userAgent": USER_AGENT,
|
|
141
|
+
})
|
|
142
|
+
print(result.solution["aws-waf-token"])
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
The visual AWS WAF CAPTCHA is `TaskType.AwsCaptchaCookie` and needs your proxy.
|
|
146
|
+
|
|
147
|
+
### Solve an image captcha (image to text)
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
import base64
|
|
151
|
+
from pathlib import Path
|
|
152
|
+
|
|
153
|
+
result = client.solve({
|
|
154
|
+
"type": TaskType.ImageToTextTask,
|
|
155
|
+
"module": "1",
|
|
156
|
+
"body": base64.b64encode(Path("captcha.png").read_bytes()).decode(),
|
|
157
|
+
})
|
|
158
|
+
print(result.solution["answer"])
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### asyncio
|
|
162
|
+
|
|
163
|
+
```python
|
|
164
|
+
import asyncio
|
|
165
|
+
from sursolver import AsyncSurSolver, TaskType
|
|
166
|
+
|
|
167
|
+
async def main() -> None:
|
|
168
|
+
async with AsyncSurSolver(API_KEY) as client:
|
|
169
|
+
result = await client.solve({
|
|
170
|
+
"type": TaskType.TurnstileTokenProxyLess,
|
|
171
|
+
"websiteURL": "https://example.com/login",
|
|
172
|
+
"websiteKey": "0x4AAAAAAA...",
|
|
173
|
+
"userAgent": USER_AGENT,
|
|
174
|
+
})
|
|
175
|
+
print(result.solution["token"])
|
|
176
|
+
|
|
177
|
+
asyncio.run(main())
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Supported captchas
|
|
181
|
+
|
|
182
|
+
| Captcha | `task.type` | Guide |
|
|
183
|
+
|---|---|---|
|
|
184
|
+
| reCAPTCHA v3 | `ReCaptchaV3TokenProxyLess` | [recaptcha-v3](https://sursolver.com/solvers/recaptcha-v3) |
|
|
185
|
+
| reCAPTCHA v3 Enterprise | `ReCaptchaV3EnterpriseTokenProxyLess` | [recaptcha-v3-enterprise](https://sursolver.com/solvers/recaptcha-v3-enterprise) |
|
|
186
|
+
| Cloudflare Turnstile | `TurnstileTokenProxyLess` | [turnstile](https://sursolver.com/solvers/turnstile) |
|
|
187
|
+
| Cloudflare Challenge | `CloudflareChallengeCookie` | [cloudflare-challenge](https://sursolver.com/solvers/cloudflare-challenge) |
|
|
188
|
+
| GeeTest v3 / v4 | `GeeTestTokenProxyLess` | [geetest](https://sursolver.com/solvers/geetest) |
|
|
189
|
+
| DataDome | `DataDomeSliderCookie` | [datadome](https://sursolver.com/solvers/datadome) |
|
|
190
|
+
| AWS WAF Challenge | `AwsChallengeCookie` | [aws-challenge](https://sursolver.com/solvers/aws-challenge) |
|
|
191
|
+
| AWS WAF CAPTCHA | `AwsCaptchaCookie` | [aws-captcha](https://sursolver.com/solvers/aws-captcha) |
|
|
192
|
+
| Akamai (`_abck`) | `AkamaiCookieProxyLess` | [akamai](https://sursolver.com/solvers/akamai) |
|
|
193
|
+
| Akamai SBSD | `AkamaiSbsdCookie` | [akamai-sbsd](https://sursolver.com/solvers/akamai-sbsd) |
|
|
194
|
+
| Akamai BMP (Android) | `AkamaiBmpTask` | [akamai-bmp](https://sursolver.com/solvers/akamai-bmp) |
|
|
195
|
+
| Kasada | `KasadaTokenProxyLess` | [kasada](https://sursolver.com/solvers/kasada) |
|
|
196
|
+
| Shape | `ShapeCookieProxyLess` | [shape](https://sursolver.com/solvers/shape) |
|
|
197
|
+
| Imperva (reese84 / utmvc) | `ImpervaCookie`, `ImpervaUtmvcCookie` | [imperva](https://sursolver.com/solvers/imperva) |
|
|
198
|
+
| Vercel Security Checkpoint | `VercelCookie` | [vercel](https://sursolver.com/solvers/vercel) |
|
|
199
|
+
| FriendlyCaptcha | `FriendlyCaptchaTokenProxyLess` | [friendlycaptcha](https://sursolver.com/solvers/friendlycaptcha) |
|
|
200
|
+
| MTCaptcha | `MtCaptchaTokenProxyLess` | [mtcaptcha](https://sursolver.com/solvers/mtcaptcha) |
|
|
201
|
+
| Prosopo | `ProsopoTokenProxyLess` | [prosopo](https://sursolver.com/solvers/prosopo) |
|
|
202
|
+
| CaptchaFox | `CaptchaFoxToken` | [captchafox](https://sursolver.com/solvers/captchafox) |
|
|
203
|
+
| Altcha | `AltchaTokenProxyLess` | [altcha](https://sursolver.com/solvers/altcha) |
|
|
204
|
+
| Lemin | `LeminTokenProxyLess` | [lemin](https://sursolver.com/solvers/lemin) |
|
|
205
|
+
| Castle (Android) | `CastleAndroidToken` | [castle-android](https://sursolver.com/solvers/castle-android) |
|
|
206
|
+
| Image to Text | `ImageToTextTask` | [image-to-text](https://sursolver.com/solvers/image-to-text) |
|
|
207
|
+
|
|
208
|
+
Each guide lists the fields to send and where to find them on the page. The live list is
|
|
209
|
+
`client.list_task_types()`; prices are `client.list_services()`.
|
|
210
|
+
|
|
211
|
+
## Errors
|
|
212
|
+
|
|
213
|
+
```python
|
|
214
|
+
from sursolver import NoSlotAvailableError, TaskFailedError
|
|
215
|
+
|
|
216
|
+
try:
|
|
217
|
+
result = client.solve(task)
|
|
218
|
+
except NoSlotAvailableError:
|
|
219
|
+
... # all your threads are busy: try again in a moment
|
|
220
|
+
except TaskFailedError:
|
|
221
|
+
... # this captcha could not be solved; its charge is refunded
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Other errors: `AuthenticationError` (wrong API key), `InvalidTaskError` (a field is missing or
|
|
225
|
+
wrong), `InsufficientBalanceError` (top up), `SolveTimeoutError` (no result in time).
|
|
226
|
+
|
|
227
|
+
## Links
|
|
228
|
+
|
|
229
|
+
- [Documentation](https://sursolver.com/docs) and [API reference](https://sursolver.com/docs/api)
|
|
230
|
+
- [Proxies guide](https://sursolver.com/docs/guides/proxies) and [error codes](https://sursolver.com/docs/guides/errors)
|
|
231
|
+
- [Pricing](https://sursolver.com/pricing)
|
|
232
|
+
|
|
233
|
+
## License
|
|
234
|
+
|
|
235
|
+
MIT
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
# SurSolver for Python — captcha solver API client
|
|
2
|
+
|
|
3
|
+
Solve **reCAPTCHA v3**, **Cloudflare Turnstile**, **Cloudflare Challenge**, **GeeTest**,
|
|
4
|
+
**DataDome**, **AWS WAF**, **Akamai**, **Kasada** and image captchas from Python with the
|
|
5
|
+
official [SurSolver](https://sursolver.com) client. One call submits the captcha and returns the
|
|
6
|
+
token or cookie. Works with plain Python and asyncio.
|
|
7
|
+
|
|
8
|
+
```powershell
|
|
9
|
+
pip install sursolver
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Quick start
|
|
13
|
+
|
|
14
|
+
```python
|
|
15
|
+
from sursolver import SurSolver, TaskType
|
|
16
|
+
|
|
17
|
+
API_KEY = "YOUR_API_KEY"
|
|
18
|
+
USER_AGENT = (
|
|
19
|
+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
|
|
20
|
+
"(KHTML, like Gecko) Chrome/155.0.0.0 Safari/537.36"
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
client = SurSolver(API_KEY)
|
|
24
|
+
|
|
25
|
+
result = client.solve({
|
|
26
|
+
"type": TaskType.TurnstileTokenProxyLess,
|
|
27
|
+
"websiteURL": "https://example.com/login",
|
|
28
|
+
"websiteKey": "0x4AAAAAAA...",
|
|
29
|
+
"userAgent": USER_AGENT,
|
|
30
|
+
})
|
|
31
|
+
print(result.solution["token"])
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Send the same `userAgent` with the request that uses the result. Tokens and cookies are
|
|
35
|
+
single-use, so use them right away. The examples below reuse `client` and `USER_AGENT`.
|
|
36
|
+
|
|
37
|
+
## Examples
|
|
38
|
+
|
|
39
|
+
### Solve reCAPTCHA v3 in Python
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
result = client.solve({
|
|
43
|
+
"type": TaskType.ReCaptchaV3TokenProxyLess,
|
|
44
|
+
"websiteURL": "https://example.com/login",
|
|
45
|
+
"websiteKey": "6Le-wvkS...",
|
|
46
|
+
"pageAction": "login",
|
|
47
|
+
"userAgent": USER_AGENT,
|
|
48
|
+
})
|
|
49
|
+
print(result.solution["gRecaptchaResponse"])
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
For reCAPTCHA v3 Enterprise use `TaskType.ReCaptchaV3EnterpriseTokenProxyLess`.
|
|
53
|
+
|
|
54
|
+
### Solve Cloudflare Turnstile
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
result = client.solve({
|
|
58
|
+
"type": TaskType.TurnstileTokenProxyLess,
|
|
59
|
+
"websiteURL": "https://example.com/signup",
|
|
60
|
+
"websiteKey": "0x4AAAAAAA...",
|
|
61
|
+
"userAgent": USER_AGENT,
|
|
62
|
+
})
|
|
63
|
+
# Submit result.solution["token"] as the cf-turnstile-response form field.
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Bypass the Cloudflare Challenge page (cf_clearance)
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
result = client.solve({
|
|
70
|
+
"type": TaskType.CloudflareChallengeCookie,
|
|
71
|
+
"websiteURL": "https://example.com/protected",
|
|
72
|
+
"proxy": "http://user:password@203.0.113.10:8000",
|
|
73
|
+
"userAgent": USER_AGENT,
|
|
74
|
+
})
|
|
75
|
+
print(result.solution["cf_clearance"])
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Replay the cookie through the same proxy and with the same `userAgent`.
|
|
79
|
+
|
|
80
|
+
### Solve GeeTest v4
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
result = client.solve({
|
|
84
|
+
"type": TaskType.GeeTestTokenProxyLess,
|
|
85
|
+
"websiteURL": "https://example.com/login",
|
|
86
|
+
"captchaId": "e392e1d7fd421dc63325744d5a2b9c73",
|
|
87
|
+
"userAgent": USER_AGENT,
|
|
88
|
+
})
|
|
89
|
+
print(result.solution["lot_number"], result.solution["pass_token"])
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
GeeTest v3: send `gt` and a fresh `challenge` instead of `captchaId`.
|
|
93
|
+
|
|
94
|
+
### Solve the DataDome slider
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
result = client.solve({
|
|
98
|
+
"type": TaskType.DataDomeSliderCookie,
|
|
99
|
+
"websiteURL": "https://example.com/",
|
|
100
|
+
"captchaUrl": "https://geo.captcha-delivery.com/captcha/?initialCid=...",
|
|
101
|
+
"proxy": "http://user:password@203.0.113.10:8000",
|
|
102
|
+
"userAgent": USER_AGENT,
|
|
103
|
+
})
|
|
104
|
+
print(result.solution["datadome"])
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Get an AWS WAF token (silent challenge)
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
result = client.solve({
|
|
111
|
+
"type": TaskType.AwsChallengeCookie,
|
|
112
|
+
"websiteURL": "https://example.com/",
|
|
113
|
+
"scriptUrl": "https://example.token.awswaf.com/example/challenge.js",
|
|
114
|
+
"userAgent": USER_AGENT,
|
|
115
|
+
})
|
|
116
|
+
print(result.solution["aws-waf-token"])
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
The visual AWS WAF CAPTCHA is `TaskType.AwsCaptchaCookie` and needs your proxy.
|
|
120
|
+
|
|
121
|
+
### Solve an image captcha (image to text)
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
import base64
|
|
125
|
+
from pathlib import Path
|
|
126
|
+
|
|
127
|
+
result = client.solve({
|
|
128
|
+
"type": TaskType.ImageToTextTask,
|
|
129
|
+
"module": "1",
|
|
130
|
+
"body": base64.b64encode(Path("captcha.png").read_bytes()).decode(),
|
|
131
|
+
})
|
|
132
|
+
print(result.solution["answer"])
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### asyncio
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
import asyncio
|
|
139
|
+
from sursolver import AsyncSurSolver, TaskType
|
|
140
|
+
|
|
141
|
+
async def main() -> None:
|
|
142
|
+
async with AsyncSurSolver(API_KEY) as client:
|
|
143
|
+
result = await client.solve({
|
|
144
|
+
"type": TaskType.TurnstileTokenProxyLess,
|
|
145
|
+
"websiteURL": "https://example.com/login",
|
|
146
|
+
"websiteKey": "0x4AAAAAAA...",
|
|
147
|
+
"userAgent": USER_AGENT,
|
|
148
|
+
})
|
|
149
|
+
print(result.solution["token"])
|
|
150
|
+
|
|
151
|
+
asyncio.run(main())
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Supported captchas
|
|
155
|
+
|
|
156
|
+
| Captcha | `task.type` | Guide |
|
|
157
|
+
|---|---|---|
|
|
158
|
+
| reCAPTCHA v3 | `ReCaptchaV3TokenProxyLess` | [recaptcha-v3](https://sursolver.com/solvers/recaptcha-v3) |
|
|
159
|
+
| reCAPTCHA v3 Enterprise | `ReCaptchaV3EnterpriseTokenProxyLess` | [recaptcha-v3-enterprise](https://sursolver.com/solvers/recaptcha-v3-enterprise) |
|
|
160
|
+
| Cloudflare Turnstile | `TurnstileTokenProxyLess` | [turnstile](https://sursolver.com/solvers/turnstile) |
|
|
161
|
+
| Cloudflare Challenge | `CloudflareChallengeCookie` | [cloudflare-challenge](https://sursolver.com/solvers/cloudflare-challenge) |
|
|
162
|
+
| GeeTest v3 / v4 | `GeeTestTokenProxyLess` | [geetest](https://sursolver.com/solvers/geetest) |
|
|
163
|
+
| DataDome | `DataDomeSliderCookie` | [datadome](https://sursolver.com/solvers/datadome) |
|
|
164
|
+
| AWS WAF Challenge | `AwsChallengeCookie` | [aws-challenge](https://sursolver.com/solvers/aws-challenge) |
|
|
165
|
+
| AWS WAF CAPTCHA | `AwsCaptchaCookie` | [aws-captcha](https://sursolver.com/solvers/aws-captcha) |
|
|
166
|
+
| Akamai (`_abck`) | `AkamaiCookieProxyLess` | [akamai](https://sursolver.com/solvers/akamai) |
|
|
167
|
+
| Akamai SBSD | `AkamaiSbsdCookie` | [akamai-sbsd](https://sursolver.com/solvers/akamai-sbsd) |
|
|
168
|
+
| Akamai BMP (Android) | `AkamaiBmpTask` | [akamai-bmp](https://sursolver.com/solvers/akamai-bmp) |
|
|
169
|
+
| Kasada | `KasadaTokenProxyLess` | [kasada](https://sursolver.com/solvers/kasada) |
|
|
170
|
+
| Shape | `ShapeCookieProxyLess` | [shape](https://sursolver.com/solvers/shape) |
|
|
171
|
+
| Imperva (reese84 / utmvc) | `ImpervaCookie`, `ImpervaUtmvcCookie` | [imperva](https://sursolver.com/solvers/imperva) |
|
|
172
|
+
| Vercel Security Checkpoint | `VercelCookie` | [vercel](https://sursolver.com/solvers/vercel) |
|
|
173
|
+
| FriendlyCaptcha | `FriendlyCaptchaTokenProxyLess` | [friendlycaptcha](https://sursolver.com/solvers/friendlycaptcha) |
|
|
174
|
+
| MTCaptcha | `MtCaptchaTokenProxyLess` | [mtcaptcha](https://sursolver.com/solvers/mtcaptcha) |
|
|
175
|
+
| Prosopo | `ProsopoTokenProxyLess` | [prosopo](https://sursolver.com/solvers/prosopo) |
|
|
176
|
+
| CaptchaFox | `CaptchaFoxToken` | [captchafox](https://sursolver.com/solvers/captchafox) |
|
|
177
|
+
| Altcha | `AltchaTokenProxyLess` | [altcha](https://sursolver.com/solvers/altcha) |
|
|
178
|
+
| Lemin | `LeminTokenProxyLess` | [lemin](https://sursolver.com/solvers/lemin) |
|
|
179
|
+
| Castle (Android) | `CastleAndroidToken` | [castle-android](https://sursolver.com/solvers/castle-android) |
|
|
180
|
+
| Image to Text | `ImageToTextTask` | [image-to-text](https://sursolver.com/solvers/image-to-text) |
|
|
181
|
+
|
|
182
|
+
Each guide lists the fields to send and where to find them on the page. The live list is
|
|
183
|
+
`client.list_task_types()`; prices are `client.list_services()`.
|
|
184
|
+
|
|
185
|
+
## Errors
|
|
186
|
+
|
|
187
|
+
```python
|
|
188
|
+
from sursolver import NoSlotAvailableError, TaskFailedError
|
|
189
|
+
|
|
190
|
+
try:
|
|
191
|
+
result = client.solve(task)
|
|
192
|
+
except NoSlotAvailableError:
|
|
193
|
+
... # all your threads are busy: try again in a moment
|
|
194
|
+
except TaskFailedError:
|
|
195
|
+
... # this captcha could not be solved; its charge is refunded
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Other errors: `AuthenticationError` (wrong API key), `InvalidTaskError` (a field is missing or
|
|
199
|
+
wrong), `InsufficientBalanceError` (top up), `SolveTimeoutError` (no result in time).
|
|
200
|
+
|
|
201
|
+
## Links
|
|
202
|
+
|
|
203
|
+
- [Documentation](https://sursolver.com/docs) and [API reference](https://sursolver.com/docs/api)
|
|
204
|
+
- [Proxies guide](https://sursolver.com/docs/guides/proxies) and [error codes](https://sursolver.com/docs/guides/errors)
|
|
205
|
+
- [Pricing](https://sursolver.com/pricing)
|
|
206
|
+
|
|
207
|
+
## License
|
|
208
|
+
|
|
209
|
+
MIT
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.25"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "sursolver"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "Official Python captcha solver API client for reCAPTCHA v3, Cloudflare Turnstile, Cloudflare Challenge, GeeTest, DataDome, AWS WAF, Akamai, Kasada and image captchas."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
license-files = ["LICENSE"]
|
|
12
|
+
authors = [{ name = "SurSolver" }]
|
|
13
|
+
requires-python = ">=3.11"
|
|
14
|
+
dependencies = ["httpx>=0.27,<1"]
|
|
15
|
+
keywords = [
|
|
16
|
+
"sursolver", "captcha", "captcha-solver", "recaptcha", "recaptcha-v3", "turnstile", "cloudflare",
|
|
17
|
+
"cf-clearance", "geetest", "datadome", "aws-waf", "akamai", "kasada", "image-captcha", "asyncio",
|
|
18
|
+
]
|
|
19
|
+
classifiers = [
|
|
20
|
+
"Development Status :: 4 - Beta",
|
|
21
|
+
"Intended Audience :: Developers",
|
|
22
|
+
"Operating System :: OS Independent",
|
|
23
|
+
"Programming Language :: Python :: 3",
|
|
24
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
25
|
+
"Programming Language :: Python :: 3.11",
|
|
26
|
+
"Programming Language :: Python :: 3.12",
|
|
27
|
+
"Programming Language :: Python :: 3.13",
|
|
28
|
+
"Programming Language :: Python :: 3.14",
|
|
29
|
+
"Typing :: Typed",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[project.urls]
|
|
33
|
+
Homepage = "https://sursolver.com"
|
|
34
|
+
Documentation = "https://sursolver.com/docs/api"
|
|
35
|
+
Repository = "https://github.com/sursolver/sursolver-python"
|
|
36
|
+
Issues = "https://github.com/sursolver/sursolver-python/issues"
|
|
37
|
+
|
|
38
|
+
[dependency-groups]
|
|
39
|
+
dev = ["pytest>=8", "mypy>=1.13", "ruff>=0.8"]
|
|
40
|
+
|
|
41
|
+
[tool.hatch.version]
|
|
42
|
+
path = "src/sursolver/_version.py"
|
|
43
|
+
pattern = 'VERSION = "(?P<version>[^"]+)"'
|
|
44
|
+
|
|
45
|
+
[tool.hatch.build.targets.wheel]
|
|
46
|
+
packages = ["src/sursolver"]
|
|
47
|
+
|
|
48
|
+
[tool.hatch.build.targets.sdist]
|
|
49
|
+
include = ["src/sursolver", "README.md", "LICENSE", "CHANGELOG.md"]
|
|
50
|
+
|
|
51
|
+
[tool.pytest.ini_options]
|
|
52
|
+
testpaths = ["tests"]
|
|
53
|
+
|
|
54
|
+
[tool.mypy]
|
|
55
|
+
strict = true
|
|
56
|
+
files = ["src", "tests"]
|
|
57
|
+
|
|
58
|
+
[tool.ruff]
|
|
59
|
+
line-length = 110
|
|
60
|
+
target-version = "py311"
|
|
61
|
+
|
|
62
|
+
[tool.ruff.lint]
|
|
63
|
+
select = ["E", "F", "W", "I", "B", "UP", "SIM", "RUF"]
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"""Official Python client for the SurSolver captcha-solving API."""
|
|
2
|
+
|
|
3
|
+
from ._async_client import AsyncSurSolver
|
|
4
|
+
from ._client import SurSolver
|
|
5
|
+
from ._core import (
|
|
6
|
+
DEFAULT_BASE_URL,
|
|
7
|
+
DEFAULT_MAX_SUBMIT_RETRIES,
|
|
8
|
+
DEFAULT_POLL_INTERVAL,
|
|
9
|
+
DEFAULT_REQUEST_TIMEOUT,
|
|
10
|
+
DEFAULT_SOLVE_TIMEOUT,
|
|
11
|
+
SUBMIT_RETRY_JITTER_MS,
|
|
12
|
+
)
|
|
13
|
+
from ._errors import (
|
|
14
|
+
ApiError,
|
|
15
|
+
AuthenticationError,
|
|
16
|
+
ConfigurationError,
|
|
17
|
+
InsufficientBalanceError,
|
|
18
|
+
InvalidTaskError,
|
|
19
|
+
NoSlotAvailableError,
|
|
20
|
+
Operation,
|
|
21
|
+
ProtocolError,
|
|
22
|
+
SolveTimeoutError,
|
|
23
|
+
SurSolverError,
|
|
24
|
+
TaskFailedError,
|
|
25
|
+
TaskNotFoundError,
|
|
26
|
+
TransportError,
|
|
27
|
+
)
|
|
28
|
+
from ._types import (
|
|
29
|
+
AsyncClock,
|
|
30
|
+
AsyncTransport,
|
|
31
|
+
Clock,
|
|
32
|
+
HttpRequest,
|
|
33
|
+
HttpResponse,
|
|
34
|
+
ServiceInfo,
|
|
35
|
+
Solution,
|
|
36
|
+
SolveResult,
|
|
37
|
+
Task,
|
|
38
|
+
TaskResult,
|
|
39
|
+
TaskTypeCatalog,
|
|
40
|
+
TaskTypeInfo,
|
|
41
|
+
Transport,
|
|
42
|
+
)
|
|
43
|
+
from ._version import VERSION
|
|
44
|
+
from .task_types import ErrorCode, TaskType
|
|
45
|
+
|
|
46
|
+
__version__ = VERSION
|
|
47
|
+
|
|
48
|
+
__all__ = [
|
|
49
|
+
"DEFAULT_BASE_URL",
|
|
50
|
+
"DEFAULT_MAX_SUBMIT_RETRIES",
|
|
51
|
+
"DEFAULT_POLL_INTERVAL",
|
|
52
|
+
"DEFAULT_REQUEST_TIMEOUT",
|
|
53
|
+
"DEFAULT_SOLVE_TIMEOUT",
|
|
54
|
+
"SUBMIT_RETRY_JITTER_MS",
|
|
55
|
+
"VERSION",
|
|
56
|
+
"ApiError",
|
|
57
|
+
"AsyncClock",
|
|
58
|
+
"AsyncSurSolver",
|
|
59
|
+
"AsyncTransport",
|
|
60
|
+
"AuthenticationError",
|
|
61
|
+
"Clock",
|
|
62
|
+
"ConfigurationError",
|
|
63
|
+
"ErrorCode",
|
|
64
|
+
"HttpRequest",
|
|
65
|
+
"HttpResponse",
|
|
66
|
+
"InsufficientBalanceError",
|
|
67
|
+
"InvalidTaskError",
|
|
68
|
+
"NoSlotAvailableError",
|
|
69
|
+
"Operation",
|
|
70
|
+
"ProtocolError",
|
|
71
|
+
"ServiceInfo",
|
|
72
|
+
"Solution",
|
|
73
|
+
"SolveResult",
|
|
74
|
+
"SolveTimeoutError",
|
|
75
|
+
"SurSolver",
|
|
76
|
+
"SurSolverError",
|
|
77
|
+
"Task",
|
|
78
|
+
"TaskFailedError",
|
|
79
|
+
"TaskNotFoundError",
|
|
80
|
+
"TaskResult",
|
|
81
|
+
"TaskType",
|
|
82
|
+
"TaskTypeCatalog",
|
|
83
|
+
"TaskTypeInfo",
|
|
84
|
+
"Transport",
|
|
85
|
+
"TransportError",
|
|
86
|
+
]
|