Skip to main content

bidiwave

WebDriver BiDi for Python — talk to any browser via W3C standard

Cross-browser · Chrome + Firefox + Edge · async-first · zero Selenium · zero Playwright


CI PyPI Python License Docs

WebDriver BiDi for Python — talk to any browser via the W3C standard. bidiwave connects to Chrome, Firefox, and Edge using the WebDriver BiDi protocol. No Selenium, no Playwright, no Node.js. Pure async Python with Pydantic v2 models and full type hints.

Quick demo

30 seconds to your first BiDi call.

pip install bidiwave
import asyncio
from bidiwave import BiDiClient, StringValue

async def main():
    async with await BiDiClient.connect("ws://localhost:9515/session") as client:
        async with await client.browsing.open("https://example.com") as page:
            result = await page.evaluate("document.title")
            match result:
                case StringValue(value=title):
                    print(f"Title: {title}")  # "Example Domain"

asyncio.run(main())

Why bidiwave?

bidiwave is a low-level WebDriver BiDi client for Python. It speaks the W3C WebDriver BiDi protocol directly — no Selenium, no Playwright, no Node.js. Unlike CDP (which is Chromium-specific), BiDi is a W3C standard that works across Chrome, Firefox, and Edge.

It is the BiDi backend that powers wavexis and wavexis-mcp.

Key features

  • W3C WebDriver BiDi — standard protocol, not proprietary CDP
  • Cross-browser — Chrome, Firefox, Edge (Safari when BiDi support lands)
  • Async-first — native async/await with asyncio
  • Browsing — contexts, navigation, screenshots, viewport control with DPR, element waiting, CSS/XPath locators, PDF printing, dialog handling, download events, history traversal, user prompt management
  • Script — evaluate JS, call functions, typed RemoteValue with match pattern narrowing, preload scripts with channel communication, realm inspection, serialization options, user activation
  • Input simulation — clicks, keyboard, scroll, drag & drop, file upload, file dialog events
  • Network interception — block, modify, mock requests, cache behavior control, response body retrieval via data collectors, authentication handling, extra headers
  • Storage — get, set, delete cookies with full attribute and partition descriptor support
  • Emulation — geolocation, locale, screen orientation, timezone, user agent override, network conditions
  • Permissions — grant or deny browser permissions without user dialogs
  • Preload scripts — inject JS before page load for polyfills or monitoring with user context support
  • Web extensions — install and uninstall browser extensions
  • CDP bridge — access Chrome DevTools Protocol for browser-specific features
  • Event streaming — 25 typed events with async handlers and error isolation
  • Type-safe — Pydantic v2 models, full type hints, mypy clean
  • Spec-compliant — W3C WebDriver BiDi wire format aligned with the spec
  • Resilient — automatic reconnection with exponential backoff
  • Lightweight — no Selenium, no Playwright required

How it works

Your Python code
  → BiDiClient.connect() connects to a BiDi endpoint
    → ChromeDriver / Firefox / EdgeDriver speaks BiDi
      → client.browsing.open("https://example.com")
        → Browser navigates and returns a page context
      ← Typed response parsed into Pydantic models
    ← Page stays open for chained calls
  ← Connection closes on context exit

Core concepts

  • BiDiClient — The top-level client. Connects to a BiDi endpoint and provides access to all modules.
  • Page — A browsing context (tab). Created via client.browsing.open(), supports evaluate(), screenshot(), navigate(), etc.
  • Modules — client.session, client.browsing, client.script, client.input, client.network, client.storage, client.emulation, client.permissions, client.preload, client.log, client.web_extension, client.cdp.
  • RemoteValue — Typed wrapper for BiDi serialized values. Use match pattern narrowing to extract StringValue, NumberValue, BooleanValue, NullValue, UndefinedValue, ArrayValue, ObjectValue, etc.

Requirements

  • Python 3.11 or higher
  • A BiDi-capable browser endpoint:
    • Chrome/Edge: ChromeDriver or EdgeDriver running with BiDi support
    • Firefox: Firefox with --remote-debugging-port (BiDi is native, no driver needed)
  • No Selenium, no Playwright, no Node.js

Install

pip install bidiwave

Quick start

import asyncio
from bidiwave import BiDiClient, StringValue

async def main():
    async with await BiDiClient.connect("ws://localhost:9515/session") as client:
        async with await client.browsing.open("https://example.com") as page:
            # Evaluate JS
            result = await page.evaluate("document.title")
            match result:
                case StringValue(value=title):
                    print(f"Title: {title}")

            # Screenshot
            screenshot = await page.screenshot()
            with open("screenshot.png", "wb") as f:
                f.write(screenshot)

asyncio.run(main())

Console log monitoring

async with await BiDiClient.connect(url) as client:
    async def on_log(entry):
        print(f"[{entry.level}] {entry.text}")

    client.on("log.entryAdded", on_log)
    await client.session.subscribe(["log.entryAdded"])

    async with await client.browsing.open("https://example.com") as page:
        await page.evaluate("console.log('hello!')")
        await asyncio.sleep(2)

Input simulation

async with await BiDiClient.connect(url) as client:
    async with await client.browsing.open("https://example.com") as page:
        ctx = page.id

        # Click at coordinates
        await client.input.click(ctx, x=100, y=200)

        # Type text
        await client.input.type_text(ctx, "Hello, world!")

        # Press Enter
        await client.input.press_key(ctx, "Enter")

        # Scroll down 500px
        await client.input.scroll(ctx, delta_y=500)

        # Drag and drop
        await client.input.drag_and_drop(ctx, 100, 100, 300, 300)

Network interception

async with await BiDiClient.connect(url) as client:
    # Block all requests to ads
    intercept = await client.network.add_intercept(
        phases=["beforeRequestSent"],
        url_patterns=["*ads.example.com*"],
    )

    async with await client.browsing.open("https://example.com") as page:
        await asyncio.sleep(2)

    await client.network.remove_intercept(intercept.intercept)

Cookies & storage

from bidiwave import Cookie

async with await BiDiClient.connect(url) as client:
    async with await client.browsing.open("https://example.com") as page:
        # Set a session cookie
        await client.storage.set_cookie(
            page.id,
            cookie=Cookie(
                name="session",
                value="abc123",
                domain="example.com",
                http_only=True,
                secure=True,
            ),
        )

        # Read all cookies
        cookies = await client.storage.get_cookies(page.id)
        for c in cookies:
            print(f"{c.name}={c.value}")

        # Delete one cookie
        await client.storage.delete_cookie(page.id, "session")

Emulation & permissions

from bidiwave import ViewportSize

async with await BiDiClient.connect(url) as client:
    async with await client.browsing.open("https://example.com") as page:
        ctx = page.id

        # Simulate iPhone viewport
        await client.browsing.set_viewport(
            ctx,
            viewport=ViewportSize(width=375, height=812),
            device_pixel_ratio=3.0,
        )

        # Simulate Tokyo location + offline mode
        await client.emulation.set_geolocation(
            coordinates={"latitude": 35.6762, "longitude": 139.6503, "accuracy": 1.0},
            contexts=[ctx],
        )
        await client.emulation.set_network_conditions(offline=True, contexts=[ctx])

        # Grant geolocation permission for an origin
        await client.permissions.set_permission(
            descriptor={"name": "geolocation"},
            state="granted",
            origin="https://example.com",
        )

Preload scripts

async with await BiDiClient.connect(url) as client:
    # Inject a script that runs before every page load
    script_id = await client.preload.add_script(
        "() => { window.__testMode = true; }",
    )

    async with await client.browsing.open("https://example.com") as page:
        value = await page.evaluate("window.__testMode")
        print(f"Test mode: {value}")

    await client.preload.remove_script(script_id)

Web extensions

async with await BiDiClient.connect(url) as client:
    # Install a browser extension
    result = await client.web_extension.install("/path/to/extension.crx")
    print(f"Installed: {result}")

    # Uninstall when done
    await client.web_extension.uninstall(result.extension)

Launch a browser with BiDi

Chrome / Edge

# Chrome — requires ChromeDriver as BiDi proxy
chromedriver --port=9515

# Edge — requires EdgeDriver
msedgedriver --port=9516

Firefox

Firefox implements BiDi natively — no driver needed:

firefox --headless --remote-debugging-port=9223 --no-remote

See Browser Setup for detailed instructions.

Documentation

Full documentation at mathiaspaulenko.github.io/bidiwave

Ecosystem

bidiwave is part of the Wave ecosystem — browser automation tools in 100% Python:

Project Description
cdpwave Chrome DevTools Protocol client
bidiwave WebDriver BiDi client — cross-browser, W3C standard (this repo)
wavexis Browser automation CLI — wraps cdpwave + bidiwave
wavexis-mcp MCP server — 220 browser automation tools for LLMs

Contributing

Contributions are welcome! See CONTRIBUTING.md for guidelines on development setup, code style, testing, and pull request process.

Security

Found a vulnerability? See SECURITY.md for responsible disclosure.

License

MIT

Metadata

Release files for bidiwave 2.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for bidiwave 2.0.0
File Size Uploaded
bidiwave-2.0.0.tar.gz 153.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for bidiwave 2.0.0
File Interpreter ABI Platform
bidiwave-2.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 207.9 kB

Release files / bidiwave-2.0.0.tar.gz

Download URL bidiwave-2.0.0.tar.gz
Size 153.6 kB
Tags Source
SHA-256 checksum
How to use checksums
7ccda98f5007b9d6987b9e0b51ede1dd977dbd39507873ab609fa8f20110b0d3
BLAKE2b-256 checksum
How to use checksums
8ba153b88ab4a40d456c6e2a5f887a15a442d0596019659d6dc44c66d4410477
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 5, 2026.

Transparency log

Release files / bidiwave-2.0.0-py3-none-any.whl

Download URL bidiwave-2.0.0-py3-none-any.whl
Size 54.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
db9389b3ce68961e47a9e490e6f3d8fbe8cebd70e5d1c249b6d65c51345a2ad2
BLAKE2b-256 checksum
How to use checksums
8af6e126d1579d54d27cf680eec0a5f578570d292bebf26c62d09401fc20f05c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 5, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

2.0.0 This release

2 release files

1.8.2

2 release files

1.8.1

2 release files

1.8.0

2 release files

1.7.2

2 release files

1.7.1

2 release files

1.7.0

2 release files

1.6.5

2 release files

1.6.4

2 release files

1.6.3

2 release files

1.6.2

2 release files

1.6.1

2 release files

1.6.0

2 release files

1.5.1

2 release files

1.5.0

2 release files

1.4.0

2 release files

1.3.0

2 release files

1.2.1

2 release files

1.2.0

2 release files

1.1.0

2 release files

1.0.1

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page