ScreenshotNeo

BlogHow-to

How to Intercept Network Requests in Pyppeteer

Learn how to inspect, allow, block, modify, and mock Pyppeteer requests with complete Python examples, troubleshooting, and production guidance.

By the ScreenshotNeo team1 October 20267 min read

Direct answer: enable interception with await page.setRequestInterception(True), attach a request event handler, and resolve every intercepted request with exactly one of await request.continue_(), await request.abort(), or await request.respond(...). Requests remain stalled until one of those actions runs. The Python spelling is continue_(), unlike JavaScript Puppeteer’s continue().

This guide shows how to observe traffic, block selected resources, change request parameters, return synthetic responses, handle redirects, avoid stalled pages, and decide when interception is appropriate. The syntax follows the Pyppeteer API reference and its page.py example. Those references document Pyppeteer 0.0.25, so check the version installed in your project before relying on version-sensitive behavior.

Minimal working example

import asyncio
from pyppeteer import launch

async def main():
    browser = await launch()
    page = await browser.newPage()
    await page.setRequestInterception(True)

    async def intercept(request):
        if request.url.endswith((".png", ".jpg")):
            await request.abort()
        else:
            await request.continue_()

    page.on("request", lambda request: asyncio.ensure_future(intercept(request)))
    await page.goto("https://example.com")
    await browser.close()

asyncio.get_event_loop().run_until_complete(main())

The handler is registered before navigation, so it sees requests generated by goto(). The fallback branch is essential: every request must be continued, aborted, or fulfilled.

How interception works

  1. Launch Chromium and create a page.
  2. Call setRequestInterception(True).
  3. Register a listener for the request event.
  4. Apply a rule based on URL, method, headers, or resource type.
  5. Resolve the request with continue_(), abort(), or respond().
  6. Navigate or perform the action that creates requests.

Relevant page events are request, response, requestfinished, and requestfailed. A response event is not guaranteed: failed requests can emit requestfailed instead. Redirects create a sequence: the original request finishes and another request is issued for the redirect target.

Observe requests without changing them

import asyncio
from pyppeteer import launch

async def main():
    browser = await launch()
    page = await browser.newPage()
    await page.setRequestInterception(True)

    async def log_and_continue(request):
        print(request.method, request.resourceType, request.url)
        await request.continue_()

    page.on("request", lambda request: asyncio.ensure_future(log_and_continue(request)))
    await page.goto("https://example.com", {"waitUntil": "networkidle0"})
    await browser.close()

asyncio.get_event_loop().run_until_complete(main())

Use logging first to learn which requests a site actually needs. Keep logs bounded in production because pages can issue many analytics, font, image, and API requests.

Block requests by URL

BLOCKED_HOSTS = ("google-analytics.com", "doubleclick.net")

async def intercept(request):
    if any(host in request.url for host in BLOCKED_HOSTS):
        await request.abort("blockedbyclient")
        return
    await request.continue_()

URL matching is simple, but suffix checks can miss query strings, alternate extensions, redirects, or CDN URLs. Normalize the policy around hosts or parsed URLs when that matters.

Block by resource type

ALLOWED_TYPES = {"document", "stylesheet", "script", "xhr", "fetch"}

async def intercept(request):
    if request.resourceType not in ALLOWED_TYPES:
        await request.abort()
        return
    await request.continue_()

Documented resource types include document, stylesheet, image, media, font, script, texttrack, xhr, fetch, eventsource, websocket, manifest, and other. Blocking styles, scripts, fonts, or API calls can change layout or application behavior, so validate an allowlist against the target site.

Abort selectively

async def intercept(request):
    if request.resourceType == "image":
        await request.abort()
    elif request.url.startswith("https://ads.example/"):
        await request.abort("blockedbyclient")
    else:
        await request.continue_()

abort(errorCode="failed") is the documented default. Other documented codes include aborted, blockedbyclient, internetdisconnected, namenotresolved, and timedout. Choose a specific code only when downstream logic needs to distinguish failures.

Modify a request before it is sent

async def intercept(request):
    if request.url == "https://example.com/api/data":
        await request.continue_({
            "method": "POST",
            "postData": '{"source":"automation"}',
            "url": "https://example.com/api/data?mode=test"
        })
        return
    await request.continue_()

The documented override keys are url, method, and postData. Treat availability and exact behavior as version-sensitive; consult the API reference shipped with your installed Pyppeteer version. Changing a method or body may require matching headers such as Content-Type.

Return a synthetic response

async def intercept(request):
    if request.url.endswith("/config.json"):
        await request.respond({
            "status": 200,
            "contentType": "application/json",
            "headers": {"Cache-Control": "no-store"},
            "body": '{"featureEnabled":true}'
        })
        return
    await request.continue_()

respond() accepts a response dictionary with status (default 200), optional headers, contentType, and body as text or bytes. Fulfill only requests your page can safely operate with; an incomplete mock often causes later JavaScript errors.

Request methods, headers, cookies, and authentication

Use the request object’s URL, method, resource type, and request metadata to make narrow rules. For site-wide credentials, prefer Pyppeteer’s page or browser context APIs where available. If you override a request body or URL, ensure the resulting request still matches the server’s expected authentication, origin, and content headers. Never print authorization headers or cookie values to shared logs.

Async handler patterns

Pyppeteer’s documented example wraps the coroutine with asyncio.ensure_future when registering the event listener:

page.on("request", lambda request: asyncio.ensure_future(intercept(request)))

Keep the handler short and resolve promptly. If you perform asynchronous work before deciding, protect against exceptions and provide a fallback continuation so one error cannot leave a request waiting indefinitely.

Multiple handlers and version compatibility

The current Puppeteer guide describes request.isInterceptResolutionHandled() checks when several listeners may handle the same request, with a recheck after every await. That API belongs to current Puppeteer documentation and is not verified here for Pyppeteer. Do not copy it into a Pyppeteer project without checking your installed version. In a Pyppeteer codebase, centralize interception in one handler where possible.

Performance and reliability

  • Enable interception only when you need to inspect or change traffic; it adds an asynchronous decision point to every request.
  • Use sets and simple host/type checks instead of expensive parsing for every request.
  • Abort unnecessary media, images, trackers, or fonts only after confirming the page still renders and behaves correctly.
  • Keep synthetic responses deterministic and include the content type expected by the page.
  • Record counts for continued, aborted, responded, failed, and timed-out requests so policy changes are visible.
  • Set navigation and application timeouts separately from interception logic; a missing resolution can look like a navigation timeout.
  • For redirects, record each request URL and do not assume one request object represents the complete navigation.

Troubleshooting

Symptom Likely cause Fix
Navigation hangs A branch never resolved the intercepted request. Ensure every path calls exactly one of continue_(), abort(), or respond(); add exception handling around the rule.
Images or styles disappear A URL or resource-type rule is too broad. Log resource types, allow required CSS/fonts/images, and narrow the match.
JavaScript errors after mocking The synthetic body does not match the schema or content type expected by the page. Return valid data, status, headers, and content type; mock only the endpoint needed.
POST requests fail The override changed method or postData without matching headers. Preserve the original method and body unless the server contract is known; set the expected content type.
Some requests are not logged Interception was enabled after navigation or the activity already occurred. Call setRequestInterception(True) and register the listener before goto() or the triggering action.
Redirect policy behaves unexpectedly Each redirect creates a new request. Apply the rule to every request and record the URL chain.
Code copied from Puppeteer fails JavaScript method names and newer APIs differ. Use Pyppeteer’s continue_() spelling and verify APIs against your installed Pyppeteer version.

When to use interception

Interception is useful for blocking unwanted resources, testing failure paths, replacing unstable APIs, collecting request inventories, and controlling bandwidth in automation. It is the wrong layer for simple page screenshots when you do not need browser-level control: maintaining Chromium, navigation waits, consent handling, and failure policies adds operational work.

Or skip the browser setup

For a clean screenshot from a URL, ScreenshotNeo provides a single API request. Its capture pipeline accepts cookie and consent banners, removes more than 60 known consent platforms plus newsletter popups and chat widgets, and lets each cleanup step be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers.

See the ScreenshotNeo API documentation for all options, including full-page and element capture, device presets, dark mode, custom CSS and JavaScript, request blocking, headers and cookies, geolocation, caching, signed links, asynchronous jobs, bulk capture, PDFs, and usage data.

cURL

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

Python

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)

Node.js

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

ScreenshotNeo also includes an MCP server with take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The Free plan includes 1,000 screenshots per month without a card; paid plans start at $5 for 3,000 screenshots. Create a free ScreenshotNeo account.

FAQ

Does interception affect requests made before the listener is registered?

No. Register interception and the listener before navigation or the action you want to observe.

Can I intercept WebSockets?

The resource type list includes websocket, but the policy and behavior depend on the Pyppeteer version and the page’s protocol usage. Verify with logging before blocking it.

Is continue_() required for every request?

Every intercepted request must be resolved, but the resolution can be continue_(), abort(), or respond().

Should I use URL matching or resource types?

Use URL rules for a specific host or endpoint and resource types for broad classes such as images or media. Start narrow and expand after observing traffic.