Sdk Python — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited Sdk Python (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Stop getting blocked. Aluvia routes your AI agent's web traffic through premium US mobile carrier IPs — the same IPs used by real people on their phones. Websites trust them, so your agent stops hitting 403s, CAPTCHAs, and rate limits.
This SDK gives you everything you need:
AluviaApipip install aluvia-mcp and run aluvia-mcp. See aluvia_mcp/README.md for details.pip install aluvia-sdk playwright
export ALUVIA_API_KEY="your-api-key"Aluvia automatically detects website blocks and uses mobile IPs when necessary.
from aluvia_sdk import AluviaClient
import asyncio
async def main():
client = AluviaClient(api_key="your-api-key", start_playwright=True)
connection = await client.start()
page = await connection.browser.new_page()
await page.goto("https://example.com")
print(await page.title())
await connection.close()
asyncio.run(main())For AI agent frameworks that support MCP (Claude Desktop, Claude Code, Cursor, etc.), use the Aluvia MCP server:
pip install aluvia-mcp
export ALUVIA_API_KEY="your-api-key"
aluvia-mcpThe MCP server exposes all Aluvia CLI functionality as structured tools. See aluvia_mcp/README.md for configuration and usage.
The Aluvia client runs a local rules-based proxy server on your agent's host, handles authentication and connection management, and provides ready-to-use adapters for popular tools like Playwright, Selenium, and httpx.
Simply point your automation tool at the local proxy address (127.0.0.1) and the client handles the rest. For each request, the client checks the destination hostname against user-defined (or agent-defined) routing rules and decides whether to send it through Aluvia's mobile IPs or direct to the destination.
┌──────────────────┐ ┌──────────────────────────┐ ┌──────────────────────┐
│ │ │ │ │ │
│ Your Agent │─────▶ Aluvia Client ─────▶ gateway.aluvia.io │
│ │ │ 127.0.0.1:port │ │ (Mobile IPs) │
│ │ │ │ │ │
└──────────────────┘ │ Per-request routing: │ └──────────────────────┘
│ │
│ not-blocked.com ──────────────▶ Direct
│ blocked-site.com ─────────────▶ Via Aluvia
│ │
└──────────────────────────┘Benefits:
pip install aluvia-sdkRequirements: Python 3.9 or later
This example shows how an agent can use the Aluvia client to dynamically unblock websites. It demonstrates starting the client, using the Playwright integration adapter, configuring geo targeting and session ID, detecting blocks, and updating routing rules on the fly.
import asyncio
from playwright.async_api import async_playwright
from aluvia_sdk import AluviaClient
async def main():
# Initialize the Aluvia client with your API key
client = AluviaClient(api_key="your-api-key")
# Start the client (launches local proxy, fetches connection config)
connection = await client.start()
# Configure geo targeting (use California IPs)
await client.update_target_geo("us_ca")
# Set session ID (requests with the same session ID use the same IP)
await client.update_session_id("agentsession1")
# Launch browser using the Playwright integration adapter
# The adapter returns proxy settings in Playwright's expected format
async with async_playwright() as p:
browser = await p.chromium.launch(proxy=connection.as_playwright())
# Track hostnames we've added to proxy rules
proxied_hosts = set()
async def visit_with_retry(url: str) -> str:
page = await browser.new_page()
try:
response = await page.goto(url, wait_until="domcontentloaded")
hostname = url.split("//")[1].split("/")[0]
# Detect if the site blocked us (403, 429, or challenge page)
status = response.status if response else 0
title = await page.title()
is_blocked = status in (403, 429) or "blocked" in title.lower()
if is_blocked and hostname not in proxied_hosts:
print(f"Blocked by {hostname} — adding to proxy rules")
# Update routing rules to proxy this hostname through Aluvia
# Rules update at runtime—no need to restart the browser
proxied_hosts.add(hostname)
await client.update_rules(list(proxied_hosts))
# Rotate to a fresh IP by changing the session ID
import time
await client.update_session_id(f"retry{int(time.time())}")
await page.close()
return await visit_with_retry(url)
return await page.content()
finally:
await page.close()
try:
# First attempt goes direct; if blocked, retries through Aluvia
html = await visit_with_retry("https://example.com/data")
print("Success:", html[:200])
finally:
# Always close the browser and connection when done
await browser.close()
await connection.close()
if __name__ == '__main__':
asyncio.run(main())For even simpler setup, the SDK can automatically launch a Chromium browser that's already configured with the Aluvia proxy. This eliminates the need to manually import Playwright and configure proxy settings.
import asyncio
from aluvia_sdk import AluviaClient
async def main():
# Initialize with start_playwright option to auto-launch browser
client = AluviaClient(
api_key="your-api-key",
start_playwright=True, # Automatically launch and configure Chromium
)
# Start the client - this also launches the browser
connection = await client.start()
# Browser is already configured with Aluvia proxy
browser = connection.browser
page = await browser.new_page()
# Configure geo targeting and session ID
await client.update_target_geo("us_ca")
await client.update_session_id("session1")
# Navigate directly - proxy is already configured
await page.goto("https://example.com")
print("Title:", await page.title())
# Cleanup - automatically closes both browser and proxy
await connection.close()
if __name__ == '__main__':
asyncio.run(main())Note: To use start_playwright=True, you must install Playwright:
pip install playwright
playwright install chromiumThe Aluvia client provides ready-to-use adapters for popular automation and HTTP tools. Check the integration examples in the Node.js SDK docs for reference patterns that can be adapted to Python.
The client is split into two independent planes:
┌─────────────────────────────────────────────────────────────────┐
│ AluviaClient │
├─────────────────────────────┬───────────────────────────────────┤
│ Control Plane │ Data Plane │
│ (ConfigManager) │ (ProxyServer) │
├─────────────────────────────┼───────────────────────────────────┤
│ • Fetches/creates config │ • Local HTTP proxy (proxy.py) │
│ • Polls for updates (ETag) │ • Per-request routing decisions │
│ • PATCH updates (rules, │ • Uses rules engine to decide: │
│ session, geo) │ direct vs gateway │
└─────────────────────────────┴───────────────────────────────────┘/account/connections/...)127.0.0.1The Aluvia client has two operating modes: Client Proxy Mode (default) and Gateway Mode.
How it works: The SDK runs a local proxy on 127.0.0.1. For each request, it checks your routing rules and sends traffic either direct or through Aluvia.
Why use it:
Best for: Using per-hostname routing rules.
Set local_proxy=False to enable.
How it works: No local proxy. Your tools connect directly to gateway.aluvia.io and ALL traffic goes through Aluvia.
Why use it:
Best for: When you want all traffic proxied without selective routing.
client = AluviaClient(
api_key=os.environ["ALUVIA_API_KEY"],
connection_id=123, # Optional: reuse an existing connection
local_proxy=True, # Optional: default True (recommended)
start_playwright=True, # Optional: auto-launch Chromium browser
)connection = await client.start()This starts the local proxy and returns a connection object you'll use with your tools. Understanding the connection object
Pass the connection to your automation tool using the appropriate adapter:
browser = await p.chromium.launch(proxy=connection.as_playwright())While your agent is running, you can update routing rules, rotate IPs, or change geo targeting—no restart needed:
await client.update_rules(["blocked-site.com"]) # Add hostname to proxy rules
await client.update_session_id("newsession") # Rotate to a new IP
await client.update_target_geo("us_ca") # Target California IPsawait connection.close() # Stops proxy, polling, and releases resourcesThe Aluvia Client starts a local proxy server that routes each request based on hostname rules that you (or our agent) set. Rules can be updated at runtime without restarting the agent.
Traffic can be sent either:
await client.update_rules(["*"]) # Proxy all traffic
await client.update_rules(["target-site.com", "*.google.com"]) # Proxy specific hosts
await client.update_rules(["*", "-api.stripe.com"]) # Proxy all except specified
await client.update_rules([]) # Route all traffic direct| Pattern | Matches |
|---|---|
* | All hostnames |
example.com | Exact match |
*.example.com | Subdomains of example.com |
google.* | google.com, google.co.uk, and similar |
-example.com | Exclude from proxying |
Most proxy solutions require you to decide upfront which sites to proxy. If a site blocks you later, you're stuck—restart your workers, redeploy your fleet, or lose the workflow.
With Aluvia, your agent can unblock itself. When a request fails with a 403 or 429, your agent adds that hostname to its routing rules and retries. The update takes effect immediately—no restart, no redeployment, no lost state.
This turns blocking from a workflow-ending failure into a minor speed bump.
response = await page.goto(url)
if response and response.status in (403, 429):
# Blocked! Add this hostname to proxy rules and retry
hostname = url.split("//")[1].split("/")[0]
await client.update_rules([*current_rules, hostname])
await page.goto(url) # This request goes through AluviaYour agent learns which sites need proxying as it runs. Sites that don't block you stay direct (faster, cheaper). Sites that do block you get routed through mobile IPs automatically.
Every tool has its own way of configuring proxies—Playwright wants a dict with server/username/password, Selenium wants a string, httpx wants an agent, and some tools don't support proxies at all. The SDK handles all of this for you:
| Tool | Method | Returns |
|---|---|---|
| Playwright | connection.as_playwright() | {"server": "...", "username": "...", "password": "..."} |
| Playwright | connection.browser | Auto-launched Chromium browser (if start_playwright=True) |
| Selenium | connection.as_selenium() | "--proxy-server=..." |
| httpx | connection.as_httpx() | httpx.HTTPTransport(proxy=...) |
| requests | connection.as_requests() | {"http": "...", "https": "..."} |
| aiohttp | connection.as_aiohttp() | "http://username:password@host:port" |
Playwright auto-launch: Set start_playwright=True in the client options to automatically launch a Chromium browser that's already configured with the Aluvia proxy. The browser is available via connection.browser and is automatically cleaned up when you call connection.close().
AluviaApi is a typed wrapper for the Aluvia REST API. Use it to manage connections, query account info, or build custom tooling—without starting a proxy.
AluviaApi is built from modular layers:
┌───────────────────────────────────────────────────────────────┐
│ AluviaApi │
│ Constructor validates api_key, creates namespace objects │
├───────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ account │ │ geos │ │ request │ │
│ │ namespace │ │ namespace │ │ (escape │ │
│ │ │ │ │ │ hatch) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌────────────────────────────────────────────────────┐ │
│ │ request_and_unwrap / request │ │
│ │ (envelope unwrapping, error throwing) │ │
│ └────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────────┐ │
│ │ request_core │ │
│ │ (URL building, headers, timeout, JSON parsing) │ │
│ └────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ httpx / requests │
└───────────────────────────────────────────────────────────────┘| Endpoint | Description |
|---|---|
api.account.get() | Get account info (balance, usage) |
api.account.connections.list() | List all connections |
api.account.connections.create() | Create a new connection |
api.account.connections.get(id) | Get connection details |
api.account.connections.patch(id) | Update connection (rules, geo, session) |
api.account.connections.delete(id) | Delete a connection |
api.geos.list() | List available geo-targeting options |
from aluvia_sdk import AluviaApi
api = AluviaApi(api_key=os.environ["ALUVIA_API_KEY"])
# Check account balance
account = await api.account.get()
print(f"Balance: {account['balance_gb']} GB")
# Create a connection for a new agent
connection = await api.account.connections.create(
description="pricing-scraper",
rules=["competitor-site.com"],
target_geo="us_ca",
)
print(f"Created: {connection['connection_id']}")
# List available geos
geos = await api.geos.list()
print("Geos:", [g["code"] for g in geos])Tip: AluviaApi is also available as client.api when using AluviaClient.
MIT — see LICENSE
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.