`BrowserFetchHttpClient`: use a logged-in page as the HTTP client
Author: janbucharCreated Sep 18, 2026Updated Sep 18, 2026
Labelst-tooling
- related to #3040, #3283, discussion #3217; prior art in crawlee-python:
PlaywrightHttpClient(apify/crawlee-python#1134) - Recurring pattern: open a site once in a browser (login, anti-bot), then fetch many API/HTML URLs from that page with in-page
fetch()becauseCheerioCrawlergets blocked where the browser's TLS/HTTP2 fingerprint, cookies and service workers do not. Today people hand-roll a crawler loop inside aPlaywrightCrawlerhandler (fetchNextRequest/reclaimRequest/ retry counting). - With the v4
BaseHttpClientcontract this is a ~50-line client:fetch()runspage.evaluate(fetch(...)), rebuilds aResponseWithUrl, and re-runs alogincallback on 401. Plugged intoCheerioCrawlerashttpClientit gives back the queue, retries, stats, transactions,$/json, and real concurrency (concurrentevaluate(fetch)calls on one page are fine — nothing navigates). Verified locally with retries, 4-way concurrency and a mid-run re-login. - Constraints worth documenting: same-origin (or CORS) with the page the login left behind; forbidden headers (
Cookie,User-Agent) andSet-Cookieare invisible, so the browser jar is authoritative;proxyUrl/fingerprintfromCustomFetchOptionsare ignored (the browser context decides). - Python already has half of this:
PlaywrightHttpClientis the defaulthttp_clientofPlaywrightCrawlerforsend_request, bound to the current page via a contextvar and callingbrowser_context.request.fetch()(APIRequestContext). It shares the context's cookies and proxy and has no same-origin constraint, but it is Playwright's own HTTP stack rather than the browser's (fingerprint TODO: apify/crawlee-python#1055), andcrawl()/stream()areNotImplementedError, so it cannot drive a crawler. Two flavours worth distinguishing here:APIRequestContext-backed (parity with Python, good default forsendRequestinside browser crawlers) and in-page-fetch-backed (browser TLS, origin-bound) — the second is the one that replaces the hand-rolled loop. - Either ship it next to
FetchHttpClientor document the pattern in the HTTP clients guide; it answers the "scraping behind login" asks more directly than a page-level login helper.
Source: apify/crawlee