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.
@@ -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`).
@@ -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.
@@ -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
+ ]