browsernative
Python client for a self-hosted node-scraper API
Lightweight client for scraping, screenshots, AI analysis and persistent
browser sessions, talking to an API you host that is built on
@monostate/node-scraper.
Single dependency (requests).
v3: the hosted Browser Native API (bnca-api.fly.dev / bnca.monostate.ai) has been retired. There is no default endpoint any more:
base_urlis required and the API key is optional (only if your deployment checks one). See Migrating from 2.x.
Install
pip install browsernative
Usage
import os
from browsernative import BrowserNativeClient
client = BrowserNativeClient(
base_url='http://localhost:3001', # your node-scraper API
api_key=os.environ.get('SCRAPER_API_KEY'), # optional: sent as x-api-key when set
)
# Scrape
result = client.scrape('https://example.com')
result['data']['metadata']['contentStatus'] # 'usable' | 'browser_required_shell' | 'blocked' | 'empty' | 'navigation_error'
# Force a specific scraping method
result = client.scrape('https://example.com', method='lightpanda')
# Screenshot / quick screenshot
client.screenshot('https://example.com')
client.quickshot('https://example.com')
# AI Q&A (the API must be configured with a model provider)
client.analyze('https://example.com', 'What is this site about?')
Convenience functions
from browsernative import quick_scrape, quick_shot, quick_analyze, bulk_scrape
api = dict(base_url='http://localhost:3001', api_key=None)
content = quick_scrape('https://example.com', **api)
shot = quick_shot('https://example.com', **api)
answer = quick_analyze('https://example.com', 'What is this?', **api)
Bulk scraping
result = client.bulk_scrape(
urls,
concurrency=5,
continue_on_error=True,
progress_callback=lambda p: print(f"{p['percentage']:.1f}%"),
)
print(f"{result['stats']['successful']}/{result['stats']['total']} succeeded")
Context manager
with BrowserNativeClient(base_url='http://localhost:3001') as client:
result = client.scrape('https://example.com')
Hosting the API
The client speaks to an HTTP/WebSocket service with these routes, all
POST with a JSON body unless noted:
| Route | Body | Purpose |
|---|---|---|
/scrapeurl |
{ url, method?, screenshot? } |
Scrape; response { success, data, metadata: { method, contentStatus, httpStatus, finalUrl, … } } |
/quickshot |
{ url } |
Fast screenshot |
/aireply |
{ url, question } |
AI answer about a page |
/bulk-scrape |
{ urls, … } |
Bulk scrape (optional) |
GET /health |
Health check | |
GET /stats |
Usage statistics (optional) | |
WS /session?mode=…&apiKey=… |
Persistent browser session (optional) |
Authentication, when enabled, is the x-api-key header (and the apiKey
query parameter for the WebSocket). Build the service on
@monostate/node-scraper, which provides the engines, outcomes and
sessions; the service only adds HTTP, auth and limits of your choosing.
API Reference
Client options
client = BrowserNativeClient(
base_url='http://localhost:3001', # required
api_key=None, # optional
timeout=30, # seconds
retries=2, # retry attempts
verbose=False, # debug logging
)
Methods
| Method | Description |
|---|---|
scrape(url, **opts) |
Extract structured content |
screenshot(url, **opts) |
Screenshot with content extraction |
quickshot(url, **opts) |
Fast screenshot only |
analyze(url, question, **opts) |
AI-powered Q&A |
bulk_scrape(urls, **opts) |
Scrape multiple URLs concurrently |
get_usage(days=30) |
Usage statistics (if your API implements /stats) |
health_check() |
API health status |
create_session(mode=…) |
Persistent browser session over WebSocket |
Scrape options
method--'auto','direct','lightpanda', or'puppeteer'include_screenshot-- Include screenshot in responsewait_for_selector-- CSS selector to wait foruser_agent-- Custom user agent
Response shape
{
'success': True, # the request succeeded
'data': { # the API's JSON body
'data': {'title': ..., 'content': ..., ...},
'metadata': {'method': ..., 'contentStatus': ..., 'httpStatus': ..., 'finalUrl': ..., ...},
},
'responseTime': 1234,
'attempt': 1,
}
Check data['metadata']['contentStatus'] to know whether the content is
usable: node-scraper v3 reports empty bodies, SPA shells and block pages
honestly instead of calling them successes.
Browser sessions
session = client.create_session(mode='auto')
session.goto('https://example.com')
session.click('#login')
session.type('#email', 'user@example.com')
state = session.get_page_state(includeScreenshot=True)
session.on('fallback', lambda e: print(f"{e['from']} -> {e['to']}: {e['reason']}"))
session.close()
Modes: auto (LightPanda with Chrome fallback), headless, visual, computer-use.
Computer use mode
session = client.create_session(mode='computer-use', screen_width=1280, screen_height=800)
session.goto('https://example.com')
session.click_at(640, 400)
session.type_text('hello world')
session.drag(10, 20, 300, 400)
screenshot = session.screenshot()
print(session.vnc_url)
session.close()
Requires websocket-client: pip install websocket-client
Migrating from 2.x
| 2.x | 3.x |
|---|---|
BrowserNativeClient('key') used the hosted API |
BrowserNativeClient(base_url=..., api_key=...) against your own API |
| API key required | optional; sent only when set |
quick_scrape(url, 'key') |
quick_scrape(url, 'key', base_url=...) or quick_scrape(url, base_url=...) |
results had method only |
plus metadata.contentStatus, httpStatus, finalUrl |
Requirements
- Python 3.9+
requests(installed automatically)websocket-client(for browser sessions)
Changelog
See CHANGELOG.md.
License
MIT
Metadata
Release files for browsernative 3.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| browsernative-3.0.0.tar.gz | 20.8 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| browsernative-3.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 32.0 kB
Release files / browsernative-3.0.0.tar.gz
| Download URL | browsernative-3.0.0.tar.gz |
|---|---|
| Size | 20.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
78ac2b1bdc4e90bd7759137b2757a002ce4ca15724ec7baf5b5ff2bff4dd5cbf
|
|
BLAKE2b-256 checksum How to use checksums |
590f7d1708ce145333810cc351872e2c633cf834c46ba798b51a224539f91de3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.5
|
Release files / browsernative-3.0.0-py3-none-any.whl
| Download URL | browsernative-3.0.0-py3-none-any.whl |
|---|---|
| Size | 11.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
fd05cda198179cd033ad1acc88d59baabd6fde50bd898c219073a0f2f60ab7c2
|
|
BLAKE2b-256 checksum How to use checksums |
bf5ee17e7ae5d7158088085584a805fb89dda726be28da8739ca62b6c993bd27
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.5
|
Security for browsernative 3.0.0
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.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.