# Can Jev Tell a Block Page From Content?

> We gave Jev 134 real scraper responses. It judged 131 correctly, status codes 111. Accept above 0.8, reject below 0.5, and 128 pages had zero errors.

[Home](https://quanticdata.io/)/[Blog](https://quanticdata.io/blog/)/Can Jev Tell a Block Page From Content?

# Can Jev Tell a Block Page From Content?

Anti-botSep 28, 2026·8 min read·By [Aldo Morese](https://quanticdata.io/about/), founder of QuanticData

How well three methods tell a usable scraped page from a block or error page, on 134 hand-labelled responses from 70 protected sites: status code 111 correct, keyword rule 127, the Jev model 131

On this page [The setup](/blog/jev-block-page-detection/#the-setup) [The result](/blog/jev-block-page-detection/#the-result) [The probabilities are the useful part](/blog/jev-block-page-detection/#the-probabilities-are-the-useful-part) [The traps, case by case](/blog/jev-block-page-detection/#the-traps-case-by-case) [One more question made it worse](/blog/jev-block-page-detection/#one-more-question-made-it-worse) [Speed and cost](/blog/jev-block-page-detection/#speed-and-cost) [Where this belongs in a pipeline](/blog/jev-block-page-detection/#where-this-belongs-in-a-pipeline)

Every scraper eventually stores a page that is not the page it asked for: a "Robot or human?" interstitial served with 200 OK, a region notice, a country selector, a homepage instead of the search it requested. The status code does not catch these, and the keyword lists people write to catch them are never finished. Jev, the decision model TypeSafe AI released on 15 September 2026, is built for exactly this kind of yes-or-no judgment, so on 28 September 2026 we tested it. We fetched 70 heavily protected sites twice each, labelled 134 responses by hand, and asked Jev one question. It judged 131 correctly. The status code judged 111.

## The setup

We picked 70 URLs on sites that routinely block scrapers: large retailers, travel and real estate portals, job boards, review sites, ticketing, news with paywalls, plus a few easy controls such as Wikipedia and the Python docs. Each URL was fetched twice:

- by a plain HTTP client announcing itself as python-requests, which is what most scripts look like to a firewall;

- by a browser-fingerprinted fetch through residential proxies, over plain HTTP without JavaScript, using our [web scraping API](https://quanticdata.io/web-scraping-api/).

That gave 140 responses. Three failed (two client timeouts and one error in our own pipeline) and three were genuinely ambiguous (a paywalled page showing only its navigation, and both fetches of a flights route page whose content we could not confirm), so they are left out. The remaining 134 were labelled by hand with one criterion: **is this the content the URL asks for, usable as is?** 55 were, 79 were not. The python-requests client got a usable page on 16 of its 66 labelled attempts.

Each page was reduced to its title and visible text (scripts and styles removed, capped at 20,000 characters) and sent to Jev with the URL. We compared three ways of deciding.

## The result

| Method | Correct of 134 | Bad pages accepted | Good pages rejected |
| --- | --- | --- | --- |
| HTTP status is 200 | 111 (82.8%) | 20 | 3 |
| Keyword rule plus a 300-character minimum | 127 (94.8%) | 7 | 0 |
| Jev, one Noul, threshold 0.5 | 131 (97.8%) | 3 | 0 |

Two caveats that make the comparison conservative rather than flattering. The keyword rule was written by us *after* reading the corpus, so it is tuned to these exact pages; a rule written in advance would do worse. And adding the HTTP status to Jev's input changed nothing: 131 either way, which says the model was reading the page, not the code.

Jev's three mistakes are the interesting part. All three were real pages about the wrong thing: Expedia answered a request for Austin hotels with a list of hotels in Orangeburg, South Carolina, and Hotels.com and Vrbo returned their homepages instead of the Austin search. Neither a status code nor a keyword can see that. Jev let them through, but not confidently.

## The probabilities are the useful part

Jev does not just say yes or no. The Noul returns a probability, and TypeSafe trains it to be calibrated. On this corpus the scores separated almost completely:

| Group | Lowest score | Median | Highest score |
| --- | --- | --- | --- |
| Usable pages (55) | 0.62 | 0.95 | 0.98 |
| Not usable (79) | 0.00 | 0.02 | 0.71 |

Which suggests a policy rather than a threshold: accept above 0.8, reject below 0.5, and send everything in between to a second opinion. On our 134 pages that policy accepted 52 pages and rejected 76 with no errors in either group, and left 6 in the middle: the three wrong-city pages, an Amazon product page, and two Rotten Tomatoes pages that were real but heavy with sign-in chrome. Six of 134 is 4.5% of the traffic going to a slower check, a render or a larger model, which is what you would want. A word of honesty: we picked 0.8 by looking at these same scores. Use it as a starting point and tune it on your own labelled sample.

## The traps, case by case

| Response | Status | What it really was | Keyword rule | Jev score | Jev's label |
| --- | --- | --- | --- | --- | --- |
| walmart.com, python client | 200 | "Robot or human?" press-and-hold challenge | caught | 0.02 | anti-bot |
| adidas.com | 200 | 32 characters: a bot-protection stub | caught by length | 0.14 | empty |
| bestbuy.com | 200 | International country selector | missed | 0.03 | elsewhere |
| realtor.com | 200 | "Not available in your region" | missed | 0.02 | elsewhere |
| footlocker.com, python client | 200 | Redirected to the European site's cookie page | missed | 0.06 | elsewhere |
| autotrader.com | 200 | "Site currently unavailable" | caught by length | 0.02 | error |
| ebay.com | 403 | "Something went wrong on our end" (a block dressed as an error) | caught by length | 0.01 | error |
| ticketmaster.com, python client | 403 | The JSON body {"response":"block"} | caught by length | 0.09 | anti-bot |
| stackoverflow.com | 403 | The full question list, 12,219 characters | kept | 0.94 | content |
| chewy.com | 429 | The full category page, 38,644 characters | kept | 0.96 | content |
| cars.com | 403 | The full listing page, 58,730 characters | kept | 0.96 | content |
| doordash.com | 200 | Real page that also says "Checking if the site connection is secured" | kept | 0.85 | content |
| expedia.com | 200 | Hotels in the wrong city | missed | 0.56 | content |

The middle three rows are why "status is 200" is a bad rule in both directions: some sites return the full page with a 403 or 429 attached, so a status filter throws away good data as well as letting bad data in. The same thing, in reverse, is what we found on Kick, whose normal pages contain Cloudflare's challenge script and fool string matching ([details in our Kick post](https://quanticdata.io/blog/kick-proxies/)).

Jev's second question, a Choice between content, anti-bot, error, elsewhere and empty, labelled the 79 unusable pages as 45 anti-bot, 17 empty, 10 error, 4 elsewhere and 3 content (the wrong-city trio). That breakdown is what tells you what to do next: an anti-bot page needs a different fetch, an empty shell needs a render, a region notice needs an exit in another country, and an error page may just need a retry.

## One more question made it worse

To catch the wrong-city pages we added a second Noul: "the page is specifically about the place or search named in the URL, not a generic homepage". Requiring both to pass dropped accuracy to 126 of 134. It still let Hotels.com and Vrbo through, and it now rejected six good pages, including the DoorDash and Instacart homepages, because we had asked for homepages to fail and those URLs *were* homepages. The lesson is the one TypeSafe's own docs make about decomposing questions: every question is another classifier with its own errors, and a badly scoped one costs more than it catches. Write questions that are true or false on their own, and test each one.

## Speed and cost

We called Jev through OpenRouter's Decisions API, which needs no TypeSafe account (direct signups have been paused since 22 September). Over 134 calls the median input was 700 tokens and the largest 7,092; median latency from Europe was 324 ms and the 90th percentile 393 ms. The whole run cost $0.0107, which is about $0.08 per 1,000 pages. That is small next to the cost of fetching the pages in the first place, and very small next to storing a few thousand challenge pages in a dataset someone paid for.

```
import re, requests

OR_KEY = "your-openrouter-key"

def visible_text(html):
    html = re.sub(r"(?is)<(script|style|noscript)[^>]*>.*?</\1>", " ", html)
    return re.sub(r"\s+", " ", re.sub(r"(?s)<[^>]+>", " ", html)).strip()

def is_usable(url, title, html):
    body = {
        "model": "typesafe/jev-1.13",
        "state": {"url": url, "title": title, "page_text": visible_text(html)[:20000] or "(no text)"},
        "questions": {
            "usable": {"type": "noul", "instructions":
                "The page is the content that was requested at this URL, not an anti-bot challenge, "
                "access-denied notice, error page, region notice, redirect, a different page, or an empty shell."},
            "kind": {"type": "choice", "instructions": "What did the server actually return?",
                "criteria": {"content": "The requested content itself",
                             "antibot": "An anti-bot challenge, captcha, or access-denied notice",
                             "error": "An error, not-found or site-unavailable page",
                             "elsewhere": "A redirect, region notice, country selector or a different page",
                             "empty": "A title or script shell with no readable content"}},
        },
    }
    r = requests.post("https://openrouter.ai/api/alpha/decisions",
                      headers={"Authorization": "Bearer " + OR_KEY}, json=body, timeout=30)
    a = r.json()["answers"]
    p = a["usable"]["noul"]
    if p > 0.8:
        return "accept", a["kind"]["choice"]
    if p < 0.5:
        return "reject", a["kind"]["choice"]
    return "review", a["kind"]["choice"]
```

For long pages, convert to Markdown before sending rather than stripping tags by hand: in [our measurement of 20 real pages](https://quanticdata.io/blog/jev-web-scraping/), raw HTML fit Jev's 32,000-token state on only 4, while Markdown fit on all of them.

## Where this belongs in a pipeline

1. **Free checks first.** Network errors, empty bodies and obvious status codes such as 404 need no model.

2. **Jev on everything else.** One call per page, about $0.00008 at our median size, 0.3 seconds.

3. **Act on the label.** Anti-bot: refetch through a better exit, such as a [residential proxy](https://quanticdata.io/residential-proxies/) or a rendered request. Empty: render. Elsewhere: change the country. Error: retry later. Our guides to [403](https://quanticdata.io/blog/web-scraping-403-forbidden/) and [429](https://quanticdata.io/blog/429-too-many-requests-web-scraping/) responses cover the fetch side.

4. **Review the middle band.** Scores between 0.5 and 0.8 go to a render, a larger model or a human, which on our sample was 4.5% of pages.

Our API already decides this for the fetch itself: it bills only pages that come back usable and retries blocks for free. A Jev check on top is worth it for the cases a fetcher cannot see, such as the right site answering with the wrong page.

### Sources & further reading

- [OpenRouter API reference: Submit a Decisions request](https://openrouter.ai/docs/api/api-reference/alphadecisions/submit-a-decisions-request)

- [OpenRouter model page: TypeSafe Jev 1.13](https://openrouter.ai/typesafe/jev-1.13)

- [TypeSafe AI docs: Models (limits and pricing)](https://docs.typesafe.ai/models)

- [Introducing System One Models and Jev, TypeSafe AI blog](https://typesafe.ai/blog/introducing-system-one-models-and-jev)

- [Jev (AI model), Wikipedia](https://en.wikipedia.org/wiki/Jev_(AI_model))

## FAQ

Quick answers on jev block page detection.

[Something else? Ask us →](mailto:hello@quanticdata.io)

### Can Jev detect captcha and block pages?

Yes, in our 28 September 2026 test it judged 131 of 134 hand-labelled scraper responses correctly, against 111 for the HTTP status code. Its three misses were real pages about the wrong thing (hotels in the wrong city, homepages instead of searches), not challenge pages.

### Why not just check the HTTP status code?

Because it is wrong in both directions. In our sample 20 unusable pages came back with 200 OK, such as a Walmart robot check and a Realtor.com region notice, and three complete, usable pages came back with 403 or 429.

### What threshold should I use for Jev's answer?

On our corpus every page scored above 0.8 was usable and every page below 0.5 was not, leaving 6 of 134 in between for a second check. We chose those cut-offs on the same data, so treat them as a starting point and tune them on a labelled sample of your own traffic.

### How much does it cost to check pages with Jev?

Our 134 calls cost $0.0107 in total through OpenRouter, about $0.08 per 1,000 pages, with a median input of 700 tokens. Median latency was 324 ms.

### How do I call Jev without a TypeSafe account?

OpenRouter serves it through its Decisions API: POST to https://openrouter.ai/api/alpha/decisions with model typesafe/jev-1.13, a state and your questions, using an OpenRouter API key. TypeSafe paused direct signups on 22 September 2026.

### Does adding more questions make Jev more accurate?

Not automatically. A second question we added to catch wrong-city pages lowered accuracy from 131 to 126 of 134, because it was scoped badly and rejected good homepages. Each question is its own classifier and needs its own test.

## Fetch pages worth judging

Our web scraping API retries blocks for free and bills only pages that come back usable, over plain HTTP or rendered, through residential exits. Every account gets $2 of free API usage each month.

[Start free — $2/month included](https://quanticdata.io/signup/)[Explore Web Scraping API](https://quanticdata.io/web-scraping-api/)

## Related reading

[Anti-bot Is Browser Fingerprinting Legal? Browser fingerprinting is legal but regulated: consent rules for tracking, more room for fraud detection, and separate questions for anyone resisting it in automation. Read →](https://quanticdata.io/blog/is-browser-fingerprinting-legal/) [Anti-bot Is Device Fingerprinting Legal? Device fingerprinting is conditionally legal: ePrivacy consent and GDPR balancing in the EU, notice and opt-out in the US. A practical, layer-by-layer breakdown. Read →](https://quanticdata.io/blog/is-device-fingerprinting-legal/) [Anti-bot How to Browser Fingerprint: Methods The signals, hashing and detection logic behind browser fingerprinting — plus how automation stacks get caught, and what it costs to avoid the problem entirely. Read →](https://quanticdata.io/blog/how-to-browser-fingerprint/)

## Also on this site

Quantic**Data**

Residential proxies & web data APIs for AI.

#### Proxies

- [Residential Basic](https://quanticdata.io/residential-proxies/#basic)

- [Residential Premium](https://quanticdata.io/residential-proxies/#plans)

- [Cheap Residential](https://quanticdata.io/cheap-residential-proxies/)

- [Mobile Proxies](https://quanticdata.io/mobile-proxies/)

- [Datacenter Proxies](https://quanticdata.io/datacenter-proxies/)

- [ISP Proxies](https://quanticdata.io/isp-proxies/)

- [Rotating Proxies](https://quanticdata.io/rotating-proxies/)

- [Sneaker Proxies](https://quanticdata.io/sneaker-proxies/)

- [SOCKS5 Proxies](https://quanticdata.io/socks5-proxies/)

- [IPv6 Proxies](https://quanticdata.io/ipv6-proxies/)

- [Proxy locations](https://quanticdata.io/proxies/)

#### Data APIs

- [MCP Server](https://quanticdata.io/mcp-server/)

- [Web Scraper API](https://quanticdata.io/web-scraping-api/)

- [SERP API](https://quanticdata.io/serp-api/)

- [Collectors](https://quanticdata.io/collectors/)

- [Web Data for AI](https://quanticdata.io/web-data-api-for-ai/)

- [Quantic AI](https://quanticdata.io/ai-web-scraping-service/)

- [Crawl & Map](https://quanticdata.io/crawl-map/)

- [SEO Audit](https://quanticdata.io/seo-audit/)

#### Use cases

- [Company data](https://quanticdata.io/scrape-company-data/)

- [Price monitoring](https://quanticdata.io/competitor-price-monitoring/)

- [Market research](https://quanticdata.io/market-research-data/)

- [Real estate data](https://quanticdata.io/real-estate-data-scraping/)

- [Scrape job postings](https://quanticdata.io/scrape-job-postings/)

#### Company

- [Documentation](https://quanticdata.io/docs/)

- [Blog](https://quanticdata.io/blog/)

- [Free tools](https://quanticdata.io/tools/)

- [Partners](https://quanticdata.io/partners/)

- [About](https://quanticdata.io/about/)

- [Alternatives](https://quanticdata.io/alternatives/)

- [Pricing](https://quanticdata.io/pricing/)

- [FAQ](https://quanticdata.io/#faq)

- [For AI agents](https://quanticdata.io/#ai)

#### Free tools

- [All tools](https://quanticdata.io/tools/)

- [Website to Markdown](https://quanticdata.io/tools/website-to-markdown/)

- [PDF to Markdown](https://quanticdata.io/tools/pdf-to-markdown/)

- [WAF detector](https://quanticdata.io/tools/waf-detector/)

- [AI visibility audit](https://quanticdata.io/tools/ai-visibility-audit/)

- [AI crawler checker](https://quanticdata.io/tools/ai-crawler-checker/)

- [robots.txt tester](https://quanticdata.io/tools/robots-txt-tester/)

- [robots.txt generator](https://quanticdata.io/tools/robots-txt-generator/)

- [User agent](https://quanticdata.io/tools/user-agent/)

- [cURL converter](https://quanticdata.io/tools/curl-converter/)

- [Proxy tester](https://quanticdata.io/tools/proxy-tester/)

© 2026 QuanticData ·

- [quanticdata.io](https://quanticdata.io/)

·

- [Terms](https://quanticdata.io/terms/)

·

- [Privacy](https://quanticdata.io/privacy/)

If you are an AI agent:

- [llms.txt](https://quanticdata.io/llms.txt)

·

- [llms-full.txt](https://quanticdata.io/llms-full.txt)

---

Source: https://quanticdata.io/blog/jev-block-page-detection/ · Site index for AI: https://quanticdata.io/llms.txt · Full dump: https://quanticdata.io/llms-full.txt
