You have a 403. The internet has two answers ready: buy a proxy, or buy a CAPTCHA solver. Both are right maybe half the time, and the wrong one costs you a month of engineering before you notice.
This is the ladder I run before spending money on either. Each step is cheaper than the one below it, and most 403s stop at step 2.
1. Read the response, do not guess what blocked you
A 403 from a WAF and a 403 from an application login check look identical in your exception handler and completely different in the headers:
curl -sS -D - -o /dev/null 'https://target.example/page' | head -40
-
cf-raypluscf-mitigated: challenge— Cloudflare is challenging you, not the app. -
x-datadomeor adatadomecookie — DataDome. -
_abckorbm_sz— Akamai Bot Manager. -
_px3or_pxhd— PerimeterX / HUMAN. - A 200 whose body is 2 KB of JavaScript and no content — that is a challenge page, not a success.
That single distinction decides the next seven steps. If the body is the real page and the app says no, no amount of solving will help you.
2. Is it the environment or the IP?
Run the same request from a different network — your laptop on home broadband is enough.
- Works from home, fails from the datacenter: it is IP reputation, and a proxy is the right purchase.
- Fails from both: it is you (fingerprint, headers, cookies) or it is the account. A proxy will only make it worse and more expensive.
3. Real browser or headless?
If a normal browser loads the page and Playwright does not, the difference is TLS/JA4, HTTP/2 settings or header order — not your IP, and not something a solver fixes. Compare your client fingerprint against the browser with any JA3/JA4 checker before spending on either.
Two reliable fixes here: run a real browser build with matching versions, or use a client that reproduces the browser fingerprint. Rotating proxies at this stage is the classic mistake — five hundred IPs behind one headless fingerprint is one user with five hundred hats.
4. If a token is required, which token?
Solver APIs are not interchangeable. A Turnstile token is useless on an Akamai page, and a datadome cookie is not a token at all — it is a session credential bound to the IP and User-Agent that received it. Identify the challenge from step 1, then ask for that specific thing.
| Challenge | How you recognise it | Price per 1,000 solved |
|---|---|---|
| Cloudflare Turnstile |
cf-turnstile widget, 0x4AAA... sitekey |
from $0.20 |
| Cloudflare Interstitial | full-page check, no widget to click | $0.40 |
| Akamai Bot Manager |
_abck, bm_sz cookies |
$0.80 |
| PerimeterX / HUMAN |
_px3 cookie, px-captcha frame |
$1.00 |
| AWS WAF | aws-waf-token |
$1.00 |
| DataDome |
x-datadome header, datadome cookie |
$1.20 |
| Imperva |
incap_ses_, visid_incap_
|
$1.60 |
| Arkose FunCaptcha |
arkoselabs script, funcaptcha frame |
$2.00 |
Prices above are from one vendor — mine, see the note at the bottom — and they are there to make the point that the spread is 10x. Paying FunCaptcha money for a Turnstile page is a silent 10x overrun.
5. Are you submitting the token correctly?
This is the most common self-inflicted 403, and it looks exactly like a solver that does not work:
-
sitekey and page must match. A token minted for
example.com/loginwill not validate onexample.com/checkout. -
actionandcdatamust be carried over. If the page passes them, the token has to be minted with the same values. - Tokens are single-use and short-lived. Turnstile tokens are valid for about 300 seconds and one submission.
-
Post the field name the page expects.
cf-turnstile-response,g-recaptcha-response,h-captcha-response— three different names, one wrong guess sends you back to the challenge.
6. For cookie challenges, identity must not move
DataDome, Akamai, PerimeterX, Imperva and AWS WAF hand you a cookie that is bound to the IP and User-Agent that earned it. Reusing it from a different proxy exit fails instantly, and the failure looks like the solver produced a bad value.
Rule: one identity per session — same proxy, same User-Agent, same cookie jar. Rotate identities between sessions, never inside one.
7. Check your own traffic shape before blaming the target
A scraper that opens 40 parallel requests against one host gets challenged regardless of how clean the requests are. Concurrency of 4 to 8 per host, a small delay, and reusing a session for a whole crawl will outperform any fingerprint trick. If your logs show a challenge on the first request every time, this is not a capacity problem — go back to step 3.
8. Do the unit economics on accepted answers, not attempts
This is the step almost nobody does, and it changes which vendor wins. If you pay per solved task, failed solves are free, so your real cost is:
cost per accepted answer = price per solved task / acceptance rate
A $0.20 solve that the site rejects adds a retry, a new session and possibly a raised risk score. Two retries at $0.20 plus a session reset can cost more than one $1.20 solve that lands first time. Price per 1,000 is a starting point; cost per accepted answer is the number that shows up in your invoice.
Where a solver actually fits
If steps 1 to 7 leave you with a page that is blocked purely because it wants a token or cookie you cannot produce headlessly, that is the moment to buy solving capacity — and to buy it by challenge type, not by volume. I run SolveHub, one REST endpoint for the eight challenge types in the table above, billed only on completed tasks, with the cookie challenges accepting your own proxy so the credential comes back usable.
Client code, if you want it: solvehub-python (stdlib only) and solvehub-node.
The two earlier posts in this series go deeper on the two most common cases: Turnstile tokens and expiry and why rotating proxies makes DataDome worse.
Disclosure: I build the solver API linked above. Everything in steps 1 to 7 applies whether you buy from me or from nobody.
Use only on systems you own or are authorised to test.
Top comments (0)