Most scrapers fail before a single header gets read.
The server looks at the TLS handshake (cipher suite order, extension ordering, curve preferences) and decides whether you're a browser or a client library pretending to be one. Python requests, Go's net/http, plain curl: all of them hand over a distinctive fingerprint the moment they say hello. Sites that care (Datadome, Akamai, Imperva, the managed side of Cloudflare) drop the connection or serve you a challenge page before your User-Agent string even matters.
That's what unblocker: true solves on FourA. In the last month, we pinned down the pieces that make it work reliably.
What's New
unblocker: true is a single flag on any /api/single call. Flip it on and we do three things: inject the browser header set, send the request through curl-impersonate with a real-browser TLS fingerprint, and decompress whatever the server returns (gzip, brotli, deflate). The first two have been available since beta. The third one (brotli auto-decompression) shipped March 25, and the browser-version pinning work landed the next day to keep headers and TLS in lockstep.
How It Works
Here's what a request looks like:
curl -X POST "https://api.foura.ai/api/single" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"url": "https://example.com/products",
"method": "GET",
"unblocker": true
}'
Three layers run underneath.
Header injection. We set the full browser header bundle: User-Agent, Sec-Ch-Ua, Sec-Ch-Ua-Platform, Sec-Fetch-Site, Sec-Fetch-Mode, Sec-Fetch-Dest, Accept, Accept-Language, and Accept-Encoding. Order matters. Real browsers emit these in a specific sequence, and detection libraries check for it.
TLS fingerprint. curl-impersonate 0.8.2 compiles libcurl against BoringSSL and reorders TLS extensions to match what the target browser version actually sends on the wire. Your JA3 and JA4 hashes come out identical to a real browser session. Standard curl, Python requests, and Go's net/http produce fingerprints that are machine-flagged within milliseconds on protected infrastructure.
Auto-decompression. When unblocker is on, we set Accept-Encoding to gzip, deflate, br and let libcurl unwrap the body. You get a decoded string back (or a Buffer if you pass returnBuffer: true). No manual brotli handling, no headers-vs-body mismatches when a site picks deflate over gzip.
Why Version Pinning Matters
TLS fingerprints are version-locked. A browser's cipher order this month isn't last month's, and a site that fingerprints carefully will notice the drift. curl-impersonate ships profiles for specific browser builds, and we pin our real browser binary to whatever build curl-impersonate currently targets. Headers, navigator objects, and TLS all report the same version.
If that sounds fiddly, it is. We got bitten by a mismatch during the March monorepo migration when the browser auto-updated and the headers drifted out of sync with curl-impersonate. The fix was two commits: pin the browser binary, and never trust the package manager to keep them aligned for you.
Impact
On internal tests against heavily fingerprinted targets (finance, travel, protected e-commerce), the difference between unblocker: false and unblocker: true is the difference between a challenge page and a 200. Plain curl hitting managed Cloudflare lands on a 403 the first time out. The same URL with unblocker: true gets through because the TLS hello looks like a real browser handshake.
But for sites that don't fingerprint (most public APIs, older CMS templates, anything gated only on IP rate limits), leaving unblocker off is fine and saves a few milliseconds of TLS negotiation. Use it where you need it.
For Power Users
A few patterns worth knowing.
Pair unblocker with a residential proxy when the target also checks IP reputation. Datacenter IPs plus a perfect TLS handshake still flag on ASNs the site has blacklisted. Our proxy endpoint (/api/proxy) rotates by target domain, so adding "proxy": "residential" to the request is usually enough.
Skip unblocker when calling JSON APIs that don't care about browsers. The extra headers can actually look suspicious to an API that expects a programmatic client, for example a backend calling its own microservice.
If the site runs JavaScript anti-bot (Turnstile interactive challenges, Perimeter X at its strictest, Akamai Bot Manager with heuristics turned up), unblocker alone won't be enough. You need the browser endpoint, which runs real Chromium and can execute the challenge. That's a different product with different credit pricing, and we wrote it up in Browser Tasks: How to Scrape JavaScript-Heavy Sites.
And you can combine unblocker with the validate block to reject responses that technically return 200 but contain a challenge page:
{
"url": "https://example.com/products",
"method": "GET",
"unblocker": true,
"validate": {
"data": { "fail": ["captcha", "Access Denied"] }
}
}
That turns silent failures into classified failures, which matters for your success-rate tracking in the dashboard.
What's Next
Browsers ship a new stable every four weeks. curl-impersonate's maintainer usually catches up a month later, and we bump our stack when they do. You don't need to touch anything on your side: unblocker: true keeps pointing at whatever browser version we've verified end-to-end.
The harder work is ahead. HTTP/3 fingerprinting is already showing up on managed anti-bot, QUIC transport is trickier to spoof than TLS 1.3, and the migration away from static header bundles toward truly dynamic emulation is starting. Protected sites have moved to checking HTTP/2 frame ordering and JA4+ variants, and the gap between "curl that looks like a browser" and "a browser" is going to shrink from both ends. We'll write about it when we ship it.