Skip to main content

Camoufox Connector

PyPI version Python 3.9+ License: MIT

WebSocket bridge for multi-language Playwright access to Camoufox anti-detect browser

Connect to Camoufox from any programming language that has Playwright bindings - Node.js, Go, Java, .NET, Python, and more.

Architecture

flowchart TB
    subgraph clients["Client Applications"]
        NodeJS["Node.js<br/>Playwright"]
        Go["Go<br/>Playwright"]
        Java["Java<br/>Playwright"]
        Python["Python<br/>Playwright"]
        Other["Other Languages<br/>Playwright"]
    end
    
    subgraph connector["Camoufox Connector"]
        API["HTTP API<br/>:8080<br/>GET /next"]
        LB["Round-Robin<br/>Load Balancer"]
        
        subgraph pool["Browser Pool"]
            B1["Camoufox 1<br/>WS :9222<br/>Fingerprint A"]
            B2["Camoufox 2<br/>WS :9223<br/>Fingerprint B"]
            B3["Camoufox N<br/>WS :922X<br/>Fingerprint N"]
        end
        note1["Note: WebSocket ports<br/>are dynamically assigned"]
    end
    
    NodeJS -->|"WebSocket"| API
    Go -->|"WebSocket"| API
    Java -->|"WebSocket"| API
    Python -->|"WebSocket"| API
    Other -->|"WebSocket"| API
    
    API -->|"Round-Robin"| LB
    LB -->|"Distribute"| B1
    LB -->|"Distribute"| B2
    LB -->|"Distribute"| B3
    
    style clients fill:#e1f5ff
    style connector fill:#fff4e1
    style pool fill:#e8f5e9
    style API fill:#ffebee
    style LB fill:#f3e5f5

How it works:

  1. Clients (Node.js, Go, Python, etc.) connect via Playwright
  2. HTTP API provides endpoints via GET /next (round-robin)
  3. Load Balancer distributes connections across browser instances
  4. Browser Pool maintains multiple Camoufox instances with unique fingerprints
  5. Each client gets a WebSocket endpoint to connect directly to a browser
  6. WebSocket ports are dynamically assigned by camoufox (use host network mode in Docker for pool mode)

Sponsored by Scrappey

Tired of getting blocked while scraping the web?

Rotating proxies, Anti-Bot technology and headless browsers to CAPTCHAs. It's never been this easy using our simple-to-use API.

👉 Try Scrappey for free


Why Camoufox Connector?

Camoufox is a powerful anti-detect browser based on Firefox, but its Python-only interface limits accessibility. Camoufox Connector solves this by:

  • Exposing WebSocket endpoints that any Playwright client can connect to
  • Managing browser pools for high-volume scraping with fingerprint rotation
  • Providing health monitoring via HTTP API
  • Simplifying deployment with Docker support

Features

  • Multi-language support - Connect from Node.js, Go, Python, Java, .NET, or any Playwright-compatible language
  • Single & Pool modes - One persistent browser or multiple rotating browsers
  • Round-robin load balancing - Distribute connections across browser instances
  • Request priority - Reserve browsers with a priority; higher-priority requests jump the queue and can (optionally) preempt lower-priority ones
  • Proxy pool - Assign a different proxy per instance, with runtime blacklist + rotation
  • Fingerprint rotation - Each browser instance has a unique fingerprint
  • Health check API - Monitor browser health and statistics
  • Docker ready - Production-ready containerization
  • High performance - Async architecture optimized for concurrent connections

Quick Start

Installation

# Install from PyPI
pip install camoufox-connector

Or install from source:

# Clone the repository
git clone https://github.com/pim97/camoufox-connector.git
cd camoufox-connector

# Install with pip
pip install -e .

# Or install from PyPI (when published)
pip install camoufox-connector

Start the Server

# Single browser mode (default)
camoufox-connector

# Pool mode with 5 browsers
camoufox-connector --mode pool --pool-size 5

# With proxy
camoufox-connector --proxy http://user:pass@host:port

Language Examples

Full working examples are available for many programming languages:

Language Directory Playwright Support
Node.js examples/nodejs/ Full
TypeScript examples/typescript/ Full
Python examples/python/ Full
Go examples/go/ Full
Java examples/java/ Full
Kotlin examples/kotlin/ Full
C# (.NET) examples/csharp/ Full
Ruby examples/ruby/ API only
PHP examples/php/ API only
Rust examples/rust/ API only
cURL/Shell examples/curl/ API only

Connect from Node.js

import { firefox } from 'playwright';

// Get endpoint from the connector API
const response = await fetch('http://localhost:8080/next');
const { endpoint } = await response.json();

// Connect to Camoufox
const browser = await firefox.connect(endpoint);
const page = await browser.newPage();

await page.goto('https://example.com');
console.log(await page.title());

await browser.close();

Connect from Go

package main

import (
    "github.com/playwright-community/playwright-go"
)

func main() {
    pw, _ := playwright.Run()
    defer pw.Stop()
    
    // Get endpoint from connector API
    // endpoint := getEndpointFromAPI()
    endpoint := "ws://localhost:9222/abc123"
    
    browser, _ := pw.Firefox.Connect(endpoint)
    page, _ := browser.NewPage()
    
    page.Goto("https://example.com")
    title, _ := page.Title()
    println(title)
    
    browser.Close()
}

Connect from Python

import httpx
from playwright.async_api import async_playwright

async def main():
    # Get endpoint from connector API
    async with httpx.AsyncClient() as client:
        response = await client.get("http://localhost:8080/next")
        endpoint = response.json()["endpoint"]
    
    async with async_playwright() as p:
        browser = await p.firefox.connect(endpoint)
        page = await browser.new_page()
        
        await page.goto("https://example.com")
        print(await page.title())
        
        await browser.close()

Operating Modes

Single Mode (Default)

One browser instance with a consistent fingerprint. Ideal for:

  • Maintaining logged-in sessions
  • Sequential scraping tasks
  • Development and testing
camoufox-connector --mode single

Pool Mode

Multiple browser instances with different fingerprints, distributed via round-robin. Ideal for:

  • High-volume scraping
  • Avoiding detection through fingerprint rotation
  • Parallel processing
camoufox-connector --mode pool --pool-size 5

Note: Since each browser instance maintains its own fingerprint, use pool mode when you need fingerprint rotation between requests. Use single mode when you need session persistence.

Proxy Pool

Instead of a single proxy shared by every browser, you can supply a list of proxies. Each instance is assigned one proxy (round-robin), so a 5-browser pool can egress from 5 different IPs.

# One proxy per instance
camoufox-connector --mode pool --pool-size 3 \
  --proxies "http://user:pass@host1:port,http://user:pass@host2:port,http://user:pass@host3:port"
# Or via env (comma or newline separated); or a JSON list in --config
export CAMOUFOX_PROXIES="http://host1:port,http://host2:port"

--proxies takes precedence over --proxy. If a proxy starts failing, rotate an instance onto a different one from the pool — and optionally blacklist the bad proxy so it isn't reused:

# Relaunch instance 0 with a new proxy, blacklisting its current one
curl -X POST "http://localhost:8080/restart/0?blacklist=true"

/stats reports proxy assignments and the blacklist (credentials are masked).

Request Priority (leases)

The /acquire + /release API lets you reserve a browser with a priority. When the pool is at capacity, acquirers wait in a priority-ordered queue, so a higher-priority request is served before lower-priority ones as soon as a browser frees up. This is separate from /next (which is unchanged and never blocks).

# Reserve a browser at priority 5, waiting up to 30s for capacity
curl "http://localhost:8080/acquire?priority=5&timeout=30"
# -> {"endpoint":"ws://...","lease_id":"ab12...","instance":2,"priority":5}

# ... connect Playwright to endpoint, do your work, then:
curl -X POST "http://localhost:8080/release/ab12..."

Each instance serves --max-concurrency-per-instance leases at once (default 1, i.e. one lease = one exclusive browser).

Preemption (opt-in)

With --preemption, a higher-priority /acquire can reclaim a browser from a strictly lower-priority lease when the pool is full. The lower-priority holder is flagged preempted (visible via GET /lease/{lease_id}) and given a grace period (--preempt-grace, default 10s) to /release voluntarily; if it doesn't, the browser is restarted so the high-priority request gets a clean instance.

camoufox-connector --mode pool --pool-size 5 --preemption --preempt-grace 15

How this maps to the architecture: the connector hands out browser endpoints — clients connect to the browser directly, so it can't transparently pause and resume an opaque in-flight session. Preemption is therefore cooperative (yield within the grace window) with a hard fallback (restart). A well-behaved high-value client should poll GET /lease/{id} and release promptly when preempted is true.

HTTP API

The connector exposes an HTTP API for health monitoring and browser management.

Endpoint Method Description
/ GET Server info and version
/health GET Health check (returns 200/503)
/next GET Get next browser endpoint (round-robin)
/acquire GET/POST Acquire a priority lease on a browser (?priority=&timeout=)
/release/{lease_id} POST Release a previously acquired lease
/lease/{lease_id} GET Inspect a lease (incl. whether it's been asked to yield)
/endpoints GET List all available endpoints
/stats GET Pool statistics, lease + proxy state
/restart/{n} POST Restart browser instance N (?rotate_proxy=&blacklist=)

Example API Responses

GET /next

{
  "endpoint": "ws://localhost:9222/abc123def456"
}

GET /health

{
  "status": "healthy",
  "mode": "pool",
  "instances": [
    {"index": 0, "healthy": true, "endpoint": "ws://..."},
    {"index": 1, "healthy": true, "endpoint": "ws://..."},
    {"index": 2, "healthy": true, "endpoint": "ws://..."}
  ]
}

GET /stats

{
  "mode": "pool",
  "total_instances": 3,
  "healthy_instances": 3,
  "active_connections": 5,
  "total_connections": 142,
  "instances": [
    {"index": 0, "uptime": 3600.5, "connections": 2, "total_connections": 48},
    {"index": 1, "uptime": 3600.3, "connections": 2, "total_connections": 47},
    {"index": 2, "uptime": 3600.1, "connections": 1, "total_connections": 47}
  ]
}

Configuration

Command Line Options

Usage: camoufox-connector [OPTIONS]

Options:
  --mode {single,pool}   Operating mode (default: single)
  --pool-size N          Number of browser instances in pool mode (default: 3)
  --api-port PORT        HTTP API port (default: 8080)
  --api-host HOST        HTTP API host (default: 0.0.0.0)
  --ws-port-start PORT   Starting port for WebSocket endpoints (default: 9222)
  --headless             Run browsers in headless mode (default)
  --no-headless          Run browsers in headed mode
  --geoip                Enable GeoIP spoofing (default)
  --no-geoip             Disable GeoIP spoofing
  --humanize             Enable humanization (default)
  --no-humanize          Disable humanization
  --block-images         Block image loading
  --proxy URL            Single proxy URL (http://user:pass@host:port)
  --proxies URLS         Comma-separated proxy pool (one per instance)
  --max-concurrency-per-instance N   Max simultaneous /acquire leases per browser (default 1)
  --preemption           Let higher-priority /acquire requests preempt lower ones
  --preempt-grace SECONDS            Grace before a preempted lease is reclaimed (default 10)
  --config FILE          Load configuration from JSON file
  --debug                Enable debug logging

Environment Variables

All options can be set via CAMOUFOX_ prefixed environment variables:

export CAMOUFOX_MODE=pool
export CAMOUFOX_POOL_SIZE=5
export CAMOUFOX_HEADLESS=true
export CAMOUFOX_PROXY=http://user:pass@host:port

camoufox-connector

JSON Configuration

{
  "mode": "pool",
  "pool_size": 5,
  "headless": true,
  "geoip": true,
  "humanize": true,
  "proxy": "http://user:pass@host:port"
}
camoufox-connector --config config.json

Docker

Quick Start with Docker

# Build the image
docker build -t camoufox-connector .

# Run in single mode
docker run -p 8080:8080 -p 9222:9222 \
  --shm-size=2gb \
  -v camoufox-cache:/root/.cache/camoufox \
  camoufox-connector

# Run in pool mode (Linux: use host network for dynamic ports)
docker run --network host \
  -e CAMOUFOX_MODE=pool \
  -e CAMOUFOX_POOL_SIZE=5 \
  --shm-size=4gb \
  -v camoufox-cache:/root/.cache/camoufox \
  camoufox-connector

Note: Pool mode uses --network host because camoufox assigns WebSocket ports dynamically. On Windows/Mac, run natively or use a Linux VM.

Docker Compose

# Single mode
docker compose up

# Pool mode
docker compose --profile pool up

Custom docker-compose.yml

services:
  camoufox-single:
    build: .
    ports:
      - "8080:8080"
      - "9222:9222"
    environment:
      - CAMOUFOX_MODE=single
      - CAMOUFOX_HEADLESS=true
    shm_size: 2gb
    volumes:
      - camoufox-cache:/root/.cache/camoufox
    restart: unless-stopped

  camoufox-pool:
    build: .
    # Use host network for dynamic WebSocket port access
    network_mode: host
    environment:
      - CAMOUFOX_MODE=pool
      - CAMOUFOX_POOL_SIZE=5
      - CAMOUFOX_HEADLESS=true
    shm_size: 4gb
    volumes:
      - camoufox-cache:/root/.cache/camoufox
    restart: unless-stopped

volumes:
  camoufox-cache:
    name: camoufox-browser-cache

Note: The camoufox-cache volume persists browser binaries between container restarts, improving startup time. Pool mode requires network_mode: host on Linux to support dynamically assigned WebSocket ports.

Use Cases

High-Volume Web Scraping

// Distribute scraping across multiple fingerprints
async function scrapeUrls(urls) {
  const results = await Promise.all(urls.map(async (url) => {
    // Each request gets a different browser/fingerprint
    const { endpoint } = await fetch('http://localhost:8080/next').then(r => r.json());
    const browser = await firefox.connect(endpoint);
    
    try {
      const page = await browser.newPage();
      await page.goto(url);
      return await page.content();
    } finally {
      await browser.close();
    }
  }));
  
  return results;
}

Session Persistence

// Use a specific endpoint for session persistence
const { endpoints } = await fetch('http://localhost:8080/endpoints').then(r => r.json());
const sessionEndpoint = endpoints[0];  // Always use the same browser

// Login once
let browser = await firefox.connect(sessionEndpoint);
let page = await browser.newPage();
await page.goto('https://example.com/login');
// ... perform login
await browser.close();

// Subsequent requests use the same session
browser = await firefox.connect(sessionEndpoint);
page = await browser.newPage();
await page.goto('https://example.com/dashboard');  // Already logged in

Load Balancing with Health Checks

async function getHealthyEndpoint() {
  const health = await fetch('http://localhost:8080/health').then(r => r.json());
  
  if (health.status !== 'healthy') {
    throw new Error('No healthy browsers available');
  }
  
  const { endpoint } = await fetch('http://localhost:8080/next').then(r => r.json());
  return endpoint;
}

Performance Tips

  1. Use pool mode for parallel tasks - Each browser instance can handle multiple pages concurrently
  2. Set appropriate pool size - Rule of thumb: 1-2 browsers per CPU core
  3. Enable --block-images - Significantly speeds up page loads for text-based scraping
  4. Use --headless - Reduces memory and CPU usage
  5. Monitor with /stats - Watch connection distribution and adjust pool size accordingly

Troubleshooting

Browser fails to start

# Check if Camoufox is installed with GeoIP support
python -c "from camoufox.sync_api import Camoufox; print('OK')"

# Install with GeoIP support (required for --geoip flag)
pip install camoufox[geoip]
python -m playwright install firefox

GeoIP database error

If you see InvalidDatabaseError: Error opening database file, install camoufox with GeoIP support:

pip install camoufox[geoip]

Or disable GeoIP if you don't need it:

camoufox-connector --no-geoip

Connection refused

# Check if server is running
curl http://localhost:8080/health

# Check if browser WebSocket is accessible
curl -I ws://localhost:9222

Out of memory in Docker

# Increase shared memory (required for browsers)
docker run --shm-size=2gb camoufox-connector

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

MIT License - see LICENSE for details.

Credits

Links

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

camoufox_connector-1.1.0.tar.gz (39.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

camoufox_connector-1.1.0-py3-none-any.whl (29.2 kB view details)

Uploaded Python 3

File details

Details for the file camoufox_connector-1.1.0.tar.gz.

File metadata

  • Download URL: camoufox_connector-1.1.0.tar.gz
  • Upload date:
  • Size: 39.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for camoufox_connector-1.1.0.tar.gz
Algorithm Hash digest
SHA256 56a503630b1dabd7c36b17d1b104867ad6a2c76ace7d318b31cbf6c9b7a1e88f
MD5 d5a778970dff9a43c34b389f1e84f30b
BLAKE2b-256 c4871103dd4e25ed652205ca8b8619977b0c9cb87372aed32dc081e5daa4a070

See more details on using hashes here.

Provenance

The following attestation bundles were made for camoufox_connector-1.1.0.tar.gz:

Publisher: publish.yml on pim97/camoufox-connector

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file camoufox_connector-1.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for camoufox_connector-1.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 38926871c7e2e4d3a010b7781f2243cb9eb284263f992594523085b60d74504c
MD5 a2dd15742b0714200a125aaf75793814
BLAKE2b-256 73b2e9a0ea3596dfe97cd17b862a4c25bbf7aa1ac1aa3eb98a1777d47804425f

See more details on using hashes here.

Provenance

The following attestation bundles were made for camoufox_connector-1.1.0-py3-none-any.whl:

Publisher: publish.yml on pim97/camoufox-connector

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

1.1.0 This release

2 files

1.0.3

2 files

1.0.2

2 files

1.0.1

2 files

1.0.0

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page