Rotunda Python
Rotunda gives Python agents a Playwright-compatible browser that is built for real web workflows. It launches the Rotunda Firefox build, creates host-compatible browser fingerprints, and gives each context its own identity without forcing you to manage the browser process by hand.
Use it when you want Playwright ergonomics with a browser that is designed for agent-driven browsing instead of vanilla CDP automation.
Install
pip install -U rotunda
rotunda fetch
Install the GeoIP extra if you use proxies and want Rotunda to align geolocation, timezone, locale, and WebRTC IP with the proxy exit IP:
pip install -U "rotunda[geoip]"
rotunda fetch
If the rotunda console command is not on your path, use python -m rotunda fetch.
Quick Start
from rotunda import NewContext, Rotunda
with Rotunda(headless=False) as browser:
context = NewContext(browser)
page = context.new_page()
page.goto("https://example.com")
print(page.title())
Rotunda(...) launches the browser. NewContext(...) creates a Playwright browser context with a fresh Rotunda fingerprint applied before page scripts run.
Rotunda does not install default add-ons unless you ask for them. If a workflow
needs the bundled default add-ons, launch with Rotunda(default_addons=True).
Async Usage
import asyncio
from rotunda import AsyncNewContext, AsyncRotunda
async def main():
async with AsyncRotunda(headless=False) as browser:
context = await AsyncNewContext(browser)
page = await context.new_page()
await page.goto("https://example.com")
print(await page.title())
asyncio.run(main())
Playwright-Style Launching
If you already use Playwright directly, keep your existing structure and swap in Rotunda's launch helper:
from playwright.sync_api import sync_playwright
from rotunda import NewBrowser, NewContext
with sync_playwright() as playwright:
browser = NewBrowser(playwright, headless=False)
context = NewContext(browser)
page = context.new_page()
page.goto("https://example.com")
browser.close()
Isolated Eval
Rotunda exposes Playwright's isolated utility context when you need custom DOM reads that should avoid page monkeypatches:
from playwright.sync_api import sync_playwright
from rotunda import NewBrowser, NewContext, evaluate_in_utility
with sync_playwright() as playwright:
browser = NewBrowser(playwright)
context = NewContext(browser)
page = context.new_page()
page.goto("https://example.com")
title = evaluate_in_utility(page, "() => document.title")
For async code, use async_evaluate_in_utility(page, expression, arg=None).
Import rotunda before starting sync_playwright() / async_playwright() so
Rotunda can install the Playwright driver preload. Rotunda(...) and
AsyncRotunda(...) handle this automatically.
Proxies And GeoIP
Pass proxies in the same shape Playwright expects. With rotunda[geoip], geoip=True derives location data from the current public IP or proxy exit IP.
from rotunda import NewContext, Rotunda
proxy = {
"server": "http://proxy.example:8080",
"username": "user",
"password": "pass",
}
with Rotunda(headless=False, proxy=proxy, geoip=True) as browser:
context = NewContext(browser, proxy=proxy)
page = context.new_page()
page.goto("https://example.com")
You can also set context options yourself:
context = NewContext(
browser,
locale="en-US",
timezone_id="America/New_York",
geolocation={"latitude": 40.7128, "longitude": -74.0060},
)
Headless And Linux Displays
Rotunda defaults to visible browser windows because that best matches agent workflows. On Linux, headless="virtual" starts Rotunda in an Xvfb-backed virtual display:
from rotunda import NewContext, Rotunda
with Rotunda(headless="virtual") as browser:
context = NewContext(browser)
page = context.new_page()
page.goto("https://example.com")
Install xvfb on the host before using virtual display mode.
Reusing A Fingerprint
Most code should let NewContext() generate a fresh identity. If you need the launch fingerprint and context fingerprint to match exactly, generate one fingerprint and pass it to both:
from rotunda import NewContext, Rotunda
from rotunda.fingerprints import generate_fingerprint
fingerprint = generate_fingerprint()
with Rotunda(headless=False, fingerprint=fingerprint) as browser:
context = NewContext(browser, fingerprint=fingerprint)
page = context.new_page()
page.goto("https://example.com")
Debugging A Site
For a quick interactive check, open a Playwright inspector session:
rotunda test https://example.com
For fingerprint reports, enable Rotunda's debug dump around your minimal repro:
export ROTUNDA_DEBUG_DUMP_DIR=/tmp/rotunda-fingerprint-debug
export ROTUNDA_DEBUG_DUMP=manifest,network,console,vm,returns
export ROTUNDA_VM_ACCESS_SAMPLE_RATE=10
python your_repro_script.py
zip -r rotunda-fingerprint-debug.zip "$ROTUNDA_DEBUG_DUMP_DIR"
Review the dump before sharing it. It can include request and response bodies.
Useful CLI Commands
rotunda fetch # install the Rotunda browser used by the Python package
rotunda version # show package, browser, and GeoIP status
rotunda path # print the local Rotunda data directory
rotunda test <url> # open an inspector session for a URL
rotunda remove # remove local Rotunda browser and data files
Local Development
When developing Rotunda itself, point the Python package at a local browser build:
source upstream.sh
export ROTUNDA_EXECUTABLE_PATH="$PWD/rotunda-$version-$release/obj-aarch64-apple-darwin/dist/Rotunda.app/Contents/MacOS/rotunda"
uv run --group dev python -m rotunda test --debug
On Intel macOS, replace obj-aarch64-apple-darwin with obj-x86_64-apple-darwin.
All docs live at rotunda.com/python.
Metadata
Release files for rotunda 0.5.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| rotunda-0.5.1.tar.gz | 1.4 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| rotunda-0.5.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 2.8 MB
Release files / rotunda-0.5.1.tar.gz
| Download URL | rotunda-0.5.1.tar.gz |
|---|---|
| Size | 1.4 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e425a9c58af2dea2a6e84f0b5d9e0c8493037f295248b1f25a69446a82c34814
|
|
BLAKE2b-256 checksum How to use checksums |
37d59d562a7b696117a9bca07ee496a75b36e97bc1a7754b5155017c08de5e7c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.7 {"installer":{"name":"uv","version":"0.12.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / rotunda-0.5.1-py3-none-any.whl
| Download URL | rotunda-0.5.1-py3-none-any.whl |
|---|---|
| Size | 1.4 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e51bdc5342a71f622d8e1c21b061e823cbde22802ae66c7462e0379e2f2aa582
|
|
BLAKE2b-256 checksum How to use checksums |
ad5ac73fd3770a9c811f90249d845f60d7817bfdfff440038167066c2a6ce292
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.7 {"installer":{"name":"uv","version":"0.12.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|