Proxy Manager¶
ProxyManager is the high-level orchestration API for leasing and releasing
proxies. It uses a storage backend that implements the IStorage interface to
keep track of pools, proxies, consumers, and leases.
Creating a Manager¶
from pharox import InMemoryStorage, ProxyManager
storage = InMemoryStorage()
manager = ProxyManager(storage)
The manager itself is stateless; all persistent data lives in the storage backend. You can use the bundled in-memory adapter for tests and scripts, or wire your own adapter for production.
Leasing a Proxy¶
lease = manager.acquire_proxy(
pool_name="latam-residential",
consumer_name="worker-1",
duration_seconds=300,
)
if lease:
print("Leased proxy", lease.proxy_id)
else:
print("No proxy available")
Key points:
- If
consumer_nameis omitted, the manager falls back to the default consumer (ProxyManager.DEFAULT_CONSUMER_NAME). Make sure that consumer exists in storage. duration_secondsdefines when the lease expires. The storage adapter is responsible for releasing expired leases.- The manager automatically calls
cleanup_expired_leases()before trying to allocate a proxy, so stale leases do not block new requests.
Releasing a Proxy¶
if lease:
manager.release_proxy(lease)
Leases should be released as soon as the caller finishes using the proxy. The
storage layer decrements current_leases and updates the lease status.
Using the with_lease Context Manager¶
To avoid manual try/finally blocks, ProxyManager exposes a context manager
that automatically releases the lease when the block exits:
with manager.with_lease(
pool_name="latam-residential",
consumer_name="worker-1",
duration_seconds=120,
) as lease:
if not lease:
raise RuntimeError("No proxy available")
proxy = storage.get_proxy_by_id(lease.proxy_id)
do_work(proxy)
If acquisition fails, the context yields None so your code can decide whether
to retry or fall back to another pool. When a lease is returned, the manager
releases it even if exceptions occur inside the with block.
Filtering Proxies¶
Use ProxyFilters to target specific proxies. Filters apply metadata such as
country, provider, or geolocation:
from pharox import ProxyFilters
filters = ProxyFilters(country="AR", source="oxylabs")
lease = manager.acquire_proxy(
pool_name="latam-residential",
consumer_name="worker-1",
filters=filters,
)
If you need radius-based matching, include latitude, longitude, and
radius_km. Storage adapters are in charge of interpreting these filters.
Handling Concurrency Limits¶
Each Proxy can define max_concurrency. The storage implementation checks the
current lease count and prevents over-leasing.
from pharox import Proxy, ProxyStatus
proxy = Proxy(
host="1.1.1.1",
port=8080,
protocol="http",
pool_id=pool.id,
status=ProxyStatus.ACTIVE,
max_concurrency=2,
)
If all slots are in use, acquire_proxy returns None and the caller can retry
or pick another pool.
Cleaning Up Expired Leases¶
You can trigger cleanup manually when running background jobs:
released = manager.cleanup_expired_leases()
print("Expired leases released:", released)
Well-behaved storage adapters should also perform cleanup on their own cadence (e.g., cron job, background task, or database job).
Lifecycle Callbacks¶
Register callbacks to hook into acquisition and release events—for example, to emit metrics or structured logs:
def on_acquire(lease, pool, consumer, filters):
if lease:
print(f"{consumer} acquired {lease.proxy_id} from {pool}")
else:
print(f"{consumer} failed to acquire a proxy from {pool}")
def on_release(lease):
print(f"Released {lease.proxy_id}")
manager.register_acquire_callback(on_acquire)
manager.register_release_callback(on_release)
Callbacks always run after storage operations complete. The acquire hook receives
the resulting Lease (or None), the pool name, consumer name, and filters used,
so you can record failure rates or latency per pool. The release hook only
triggers when a lease is successfully released.