Async Python client for the PairBroker API
Project description
pairbroker-sdk
A Python async client for working with accounts and proxies through PairBroker API.
Handles account rotation, rate limits, retries, and proxy management — so you can focus on the actual request logic.
What is this?
pairbroker-sdk is a client library. You install it into the project that actually needs to make outbound requests (scraping, API integrations, automation, etc.) using a pool of rotating accounts and proxies.
It doesn't manage accounts or proxies by itself — it talks over HTTP to PairBroker API, a separate, self-hosted backend service that owns your accounts, proxies, rate limits and usage stats. You deploy your own instance, load it with your accounts/proxies, and point this SDK at it.
Status: PairBroker API isn't public yet — it exists and is self-hosted (Django + PostgreSQL, Docker Compose), but it's not published as an open-source project at this time. This SDK is fully usable against any instance that implements the same REST contract; docs for self-hosting PairBroker API itself will be added here once it's released.
Importantly, PairBroker API never sends the actual outbound requests itself — it only hands out accounts/proxies and tracks their usage. Your project makes the real request to the target site/API directly, using the credentials/proxies it was issued.
Below is an example scheme of using the SDK to make a request to an external resource:
┌──────────────────┐ HTTP (get pair, ┌─────────────────────────┐
│ your project │ report usage, etc.) │ PairBroker API │
│ (uses this SDK) │ ──────────────────────────► │ (self-hosted) │
│ │ ◄────────────────────────── │ │
└────────┬─────────┘ └────────────┬────────────┘
│ │
│ the actual HTTP request — sent directly │
│ by your project, using whatever account/ │
│ proxy it was issued (not through ▼
│ PairBroker API) ┌─────────────────────────┐
▼ │ your accounts & proxies │
┌─────────────────────┐ └─────────────────────────┘
│ external target │
│ (the site/API you │
│ actually wanted) │
└─────────────────────┘
So there are two moving parts, not one:
- PairBroker API — the backend you self-host. It stores accounts/proxies and exposes the REST API this SDK calls.
pairbroker-sdk(this repo) — the client youpip installinto whatever project needs to consume those accounts/proxies.
Features
| Feature | Description |
|---|---|
| Auto-rotation | Gets a fresh account + proxy pair on each call |
| Rate limiting | Tracks per-account limits and enforces delays |
| Retries | Automatic retries on 5xx / 429 / network errors with exponential backoff |
| Auth strategies | TokenAuth(), PasswordAuth(), CookiesAuth() |
| Usage reporting | require_report=True — auto-sends usage stats after each request |
| Proxy management | Auto-applies proxies, disables them on proxy errors |
Prerequisites
Before this SDK is useful, you need a running PairBroker API instance — a self-hosted backend (Django + DRF + PostgreSQL) that stores your accounts/proxies and exposes the REST API this SDK talks to. It's not public yet (see the status note above), so for now this means either your own compatible deployment or an instance provided to you by whoever runs one for your team.
Once you have an instance running:
1. Get an API key
In the PairBroker API admin, create a client for your project — it generates an API key. That's the api_key you'll pass to PairBrokerClient below.
2. Point the SDK at your instance
The SDK needs to know the base URL of wherever PairBroker API is running. Either pass it directly:
client = PairBrokerClient(api_key="...", base_url="https://your-pairbroker-instance.example.com")
or set it via environment variable (see Configuration):
export PAIRBROKER__API_BASE_URL=https://your-pairbroker-instance.example.com
base_url= takes priority if both are set. If neither is provided, PairBrokerClient raises a ValueError on construction.
Installation
pip install pairbroker-sdk
Requirements: Python 3.10+, aiohttp, pydantic, pydantic-settings
Quick start
import asyncio
from pairbroker import PairBrokerClient, TokenAuth
async def main():
async with PairBrokerClient(api_key="YOUR_API_KEY") as client:
pair = await client.get_pair("your_account_type")
async with pair:
resp = await pair.request(
"https://api.example.com/check",
params={"query": "value"},
auth_strategy=TokenAuth(auth_header="Key"),
)
print(resp.json)
asyncio.run(main())
API Reference
Single pair
async with PairBrokerClient(api_key="...") as client:
pair = await client.get_pair("account_type")
async with pair:
resp = await pair.request("https://api.example.com")
Bulk requests
async for pair, url in client.iter_pairs(
account_type="account_type",
items=["url1", "url2", "url3"],
limit=10,
require_report=True,
):
async with pair:
resp = await pair.request(url)
Auth strategies
from pairbroker import TokenAuth, PasswordAuth, CookiesAuth
# Bearer / API key header
token_auth = TokenAuth(
auth_header="Authorization",
prefix="Bearer",
)
# Login + password (posts credentials, stores session cookies)
password_auth = PasswordAuth(
login_url="https://site.com/login",
auth_request_method="POST",
payload_map={"email": "login", "password": "password"},
)
request() parameters
await pair.request(
url="https://api.example.com",
method="GET", # GET / POST / PUT / PATCH / DELETE
use_proxy=True, # whether to use a proxy from PairBroker
auth_strategy=TokenAuth(),
# request params (passed through to aiohttp)
params={"page": 1},
json={"data": "value"},
data={"name": "user"},
headers={"User-Agent": "..."},
cookies={"session": "abc"},
# transport settings
timeout=30, # request timeout in seconds
retries=3, # retry attempts on 429 / 5xx / network error
delay_multiplier=0.5, # exponential backoff multiplier (0.5s → 1s → 2s)
)
TransportResponse fields
| Field | Type | Description |
|---|---|---|
ok |
bool |
True if status_code < 400 |
status_code |
int | None |
HTTP status code |
reason |
str | None |
HTTP reason phrase |
text |
str | None |
Raw response text |
json |
Any | None |
Parsed JSON body |
headers |
Mapping[str, str] | None |
Response headers |
cookies |
SimpleCookie | None |
Response cookies |
url |
str | None |
Final URL (after redirects) |
proxy |
str | None |
Proxy that was used |
method |
str | None |
Request method |
elapsed |
float | None |
Request duration in seconds |
attempt |
int | None |
Successful attempt number |
exception |
Exception | None |
Exception raised, if any |
error |
str | None |
Serialized error string |
Convenience properties:
| Property | Type | Description |
|---|---|---|
result |
str | None |
Human-readable result summary |
is_proxy_error |
bool |
True if the error was proxy-related |
Configuration
SDK uses pydantic-settings for configuration. All settings can be overridden via environment variables with the PAIRBROKER__ prefix.
from pairbroker.config import settings
print(settings.API_BASE_URL)
print(settings.DEFAULT_RETRIES)
| Parameter | Type | Default | Description |
|---|---|---|---|
API_BASE_URL |
str | None |
None |
Base URL of your PairBroker API instance. Can also be passed as base_url= to PairBrokerClient(), which takes priority; one of the two is required |
MIN_SECONDS_BETWEEN_REQUESTS |
float |
0.25 |
Min pause between requests per client |
DEFAULT_RETRIES |
int |
3 |
Retry attempts on network / 5xx / 429 |
DEFAULT_RETRY_DELAY_MULTIPLIER |
float |
0.5 |
Exponential backoff multiplier |
ITER_PAIRS_MAX_CONSECUTIVE_ERRORS |
int |
10 |
Max consecutive errors before stopping iteration |
Example .env:
PAIRBROKER__API_BASE_URL=https://your-pairbroker-instance.example.com
PAIRBROKER__DEFAULT_RETRIES=5
PAIRBROKER__MIN_SECONDS_BETWEEN_REQUESTS=0.1
Examples
See the examples/ folder:
| Scenario | File |
|---|---|
| Single request | basic_usage.py |
| Bulk requests | iter_pairs_usage.py |
| Login / password auth | password_auth_usage.py |
| VirusTotal API | virustotal_real.py |
Contributing
- Clone the repo and install dependencies including the dev group:
uv sync --group dev - Make your changes
- Lint:
uv run ruff check src/ tests/ - Test:
uv run pytest tests/ - Push your branch and open a PR — CI runs lint and tests on every PR automatically
Releases are published to PyPI automatically by CI when a maintainer pushes a vX.Y.Z tag — no manual twine upload needed. See CHANGELOG.md for release history.
License
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pairbroker_sdk-1.0.0.tar.gz.
File metadata
- Download URL: pairbroker_sdk-1.0.0.tar.gz
- Upload date:
- Size: 25.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e7459292658f83c2dfbb34a7bc6601fbae205af28e16d879aa82cf5429316b38
|
|
| MD5 |
4a2f320822011a4dcb8c4de271332e69
|
|
| BLAKE2b-256 |
cdd15ea3a69708d89da8f46ecb56aff9f83a4b5c7f90081d5775275a2fd68a28
|
Provenance
The following attestation bundles were made for pairbroker_sdk-1.0.0.tar.gz:
Publisher:
publish.yml on maximunya/pairbroker-sdk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pairbroker_sdk-1.0.0.tar.gz -
Subject digest:
e7459292658f83c2dfbb34a7bc6601fbae205af28e16d879aa82cf5429316b38 - Sigstore transparency entry: 2115566364
- Sigstore integration time:
-
Permalink:
maximunya/pairbroker-sdk@c7e17c54ab11a37a55998538e73a72471436e0f0 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/maximunya
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@c7e17c54ab11a37a55998538e73a72471436e0f0 -
Trigger Event:
push
-
Statement type:
File details
Details for the file pairbroker_sdk-1.0.0-py3-none-any.whl.
File metadata
- Download URL: pairbroker_sdk-1.0.0-py3-none-any.whl
- Upload date:
- Size: 20.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
577e7a2d4acd8ae8619beca82d0209af2527de59e498a965809a93ca1f2be17d
|
|
| MD5 |
208afd5a6c6b5da16a6d050792a29e3c
|
|
| BLAKE2b-256 |
5cc9fd7f304e89dd2524f46d106fdf2a48bf176ac8fcdff183e3aeadd5f1dbd7
|
Provenance
The following attestation bundles were made for pairbroker_sdk-1.0.0-py3-none-any.whl:
Publisher:
publish.yml on maximunya/pairbroker-sdk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pairbroker_sdk-1.0.0-py3-none-any.whl -
Subject digest:
577e7a2d4acd8ae8619beca82d0209af2527de59e498a965809a93ca1f2be17d - Sigstore transparency entry: 2115566621
- Sigstore integration time:
-
Permalink:
maximunya/pairbroker-sdk@c7e17c54ab11a37a55998538e73a72471436e0f0 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/maximunya
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@c7e17c54ab11a37a55998538e73a72471436e0f0 -
Trigger Event:
push
-
Statement type: