1.A rule match, not a bot score
Cloudflare gives zone operators a rules engine: block or challenge requests matching conditions on country, ASN, IP range, URI path, HTTP method, header presence, User-Agent string, and more. When a request matches a rule whose action is Block, the edge returns 1020. The decision is deterministic and happens before any bot-scoring or challenge logic gets a say.
This is the crucial difference from the challenge codes. A challenge means "prove you are a browser" and has a defined path to success. A 1020 means "a human wrote a rule that excludes requests like yours", and the only way through is to stop being a request like yours. Retrying identical requests with a heavier engine burns budget and changes nothing.
It also means 1020 is often not about bot detection at all. Plenty of 1020s are geo-blocks, deny-lists for cloud ASNs, or rules protecting a specific path such as /admin or /api. Reading it as "my scraper looks like a bot" sends you optimising fingerprints when the actual trigger was your egress country.
2.Reading the code as a diagnostic
Cloudflare's numeric codes narrow the cause considerably, and the first useful step is simply reading which one you got rather than treating every block as the same event. Each points at a different layer of the stack, and therefore at a different fix.
Note that several of these are not scraper-specific at all — a browser on a residential connection would hit the same wall for 1009 or 1020, because the rule does not care what client you are running.
- 1020 — a WAF or firewall rule matched and blocked. No solve path; change the matching property.
- 1015 — rate limited. Reduce concurrency and request rate; wait out the window.
- 1010 — the client's browser signature was rejected. Common with automation-flagged headless stacks.
- 1009 — the request's country is not permitted for this zone. Change proxy country.
- 1006 / 1007 / 1008 — the specific IP is banned. Rotate egress.
3.Working out which property triggered it
Change one variable at a time. The properties worth testing, roughly in order of how often they turn out to be the cause, are egress country, egress ASN type, the URI path, the HTTP method, the User-Agent string, and the presence or absence of headers a browser would always send. Bisecting is fast because most zones have only a handful of custom rules.
Start with the path. If the homepage returns content and /api/v2/products returns 1020, the rule is path-scoped and no amount of fingerprint work will help — the endpoint is simply not meant to be reached directly. The same test distinguishes a site-wide block from a targeted one in a single pair of requests.
Then test egress. Fetch the same URL through a residential IP in the country the site actually serves. If a datacenter IP gets 1020 and a residential IP in-market gets content, the rule is an ASN or geo rule and your fix is egress selection, not engine selection. Keep the cf-ray value from blocked responses; it is the identifier a site operator would need if you ever have grounds to ask them about the rule.
12345678910# Bisect the rule: same client, three variables changed one at a time.
# 1. Is it path-scoped?
curl -s -o /dev/null -w '%{http_code}\n' https://target.example.com/
curl -s -o /dev/null -w '%{http_code}\n' https://target.example.com/api/v2/products
# 2. Is it method-scoped?
curl -s -o /dev/null -w '%{http_code}\n' -X POST https://target.example.com/api/v2/products
# 3. Keep the cf-ray from any blocked response for later reference.
curl -sI https://target.example.com/api/v2/products | grep -i '^cf-ray'
4.What actually resolves a 1020
Egress is the highest-yield change. Cloud ASNs are the most commonly deny-listed category on the internet, and a residential IP in the market the site serves removes both the ASN rule and the geo rule in one move. Match the country to the content you expect rather than defaulting to US for a European retailer.
Header hygiene is the second. Rules that match on a missing or obviously synthetic User-Agent, or on the absence of the Sec-Fetch family, are cheap for operators to write and common in practice. A browser-grade request carries a coherent header set; a hand-assembled one usually does not, and the gap is easy to write a rule against.
If the rule is genuinely path-scoped, respect it. A 1020 on an internal API endpoint is an operator telling you that endpoint is not public, and routing around it is a different kind of decision from clearing a bot challenge — one with terms-of-service and legal dimensions that are yours to weigh, not a technical problem to escalate past.
5.Configuring OmniScrape against a 1020
The parameter that matters here is proxy, not enable_solver. Set residential egress in the target's market and let the request go through the normal auto path. Because there is no challenge to clear, forcing js_rendering adds cost and latency without improving the outcome.
Read data.status_code and the body rather than assuming the wrapper hid a failure. When the origin's edge returns a 1020 page, that is the response — a successful unlock of a blocked page still contains the block. Check for "error code: 1020" in the body as an explicit condition in your validation layer so these do not silently enter your dataset as short pages.
If a country change resolves it, pin it. Passing session_id alongside a working proxy keeps subsequent requests on the same egress rather than rediscovering the block on the next rotation.
12345678910curl -X POST https://api.omniscrape.io/v1/scrape \
-H "Content-Type: application/json" \
-H "X-API-Key: ${OMNISCRAPE_KEY}" \
-d '{
"url": "https://target.example.com/catalog",
"mode": "auto",
"proxy": "residential:de:sticky",
"session_id": "de-catalog-01",
"output_format": "html"
}'
Frequently asked questions
Why does enabling the solver not fix error 1020?
Because there is no challenge to solve. 1020 means a firewall rule matched and the edge blocked the request outright, before any bot-scoring or challenge logic runs. Solvers clear challenges; they cannot un-match a rule.
What is the difference between 1020 and 1015?
1015 is rate limiting — you sent too much, too fast, and backing off resolves it. 1020 is a rule match, which does not decay with time; retrying the same request an hour later returns the same block unless something about the request changed.
I get 1020 on one path but the homepage loads fine.
That is a path-scoped rule, and it is the most common shape of 1020. Fingerprint and engine changes will not help. Treat it as the operator declaring that endpoint non-public, and weigh whether routing around it is something you have grounds to do.
Does a residential proxy fix 1020?
It fixes the large subset caused by ASN deny-lists and country rules, which together account for most 1020s scrapers encounter. It does nothing for path-, method-, or header-scoped rules, which is why bisecting before buying is worth the two minutes.
Should I keep retrying a 1020?
No. Identical retries produce identical results and consume budget. Change exactly one property — country first, then path, then headers — and retry once per change until you find the trigger.
Related guides