BuyProxy logo
Log inSign up

SDKs

Python SDK

The official Python library for BuyProxy: generate residential, mobile, ISP and datacenter proxies, check bandwidth, buy more by card or crypto, and run a reseller business. Python 3.9+. Fully typed (py.typed, mypy --strict clean). Built on httpx, with identical sync (BuyProxy) and async (AsyncBuyProxy) clients.

  • Sync (BuyProxy) and async (AsyncBuyProxy) clients with identical APIs

  • Typed dataclass models โ€” autocomplete everywhere, unknown fields never break you

  • Automatic retries with back-off for rate limits and transient errors

  • One-line helpers for requests, httpx, aiohttp, Playwright and Scrapy

Source, issues and releases: github.com/DishantSinghDev2/buyproxy-python

Installation

bash
pip install buyproxy
# or: uv add buyproxy ยท poetry add buyproxy

Python 3.9+. Ships py.typed; passes mypy --strict.

Authentication

Create an API key under Dashboard โ†’ API keys โ€” personal keys start bp_key_, reseller keys bp_live_. The SDK reads BUYPROXY_API_KEY from the environment automatically; you can also pass the key explicitly. A missing key fails immediately with an authentication error, without a network call.

python
# 1. Environment variable (recommended)
bp = BuyProxy()

# 2. Explicit key, e.g. from a secrets manager
bp = BuyProxy(api_key=get_secret("buyproxy"))

# 3. Which kind of key is it?
print(bp.key_kind)  # "personal" | "reseller" | None
Never put an API key in client-side code, a mobile app, a public repository or a log line. Revoke a leaked key from the dashboard and create a new one.

Quick start

python
from buyproxy import BuyProxy

bp = BuyProxy()  # reads BUYPROXY_API_KEY โ€” or BuyProxy(api_key="bp_key_โ€ฆ")

plan = bp.plans.list()[0]
result = bp.plans.generate(plan.id, country="US", city="Austin", count=5)

proxy = result.proxies[0]
print(proxy.url)  # http://user-country-us-city-austin:pass@gw.buyproxy.org:6969

Pass sticky_minutes=10 to keep the same IP for ten minutes, or protocol="socks5" for SOCKS5.

Async:

python
from buyproxy import AsyncBuyProxy

async with AsyncBuyProxy() as bp:
    plans = await bp.plans.list()
    result = await bp.plans.generate(plans[0].id, country="DE", count=3)

Generating proxies

Generation is free and instant โ€” bandwidth is only consumed when traffic flows through a proxy. Every parameter is optional; omit one and the API's default applies. (Names follow each language's conventions, e.g. stickyMinutes / sticky_minutes / StickyMinutes.)

ParameterTypeWhat it does
protocol"http" \"socks5"Proxy protocol. Default http (which also carries HTTPS). Check plan.generate.protocols for what a product supports
countinteger 1โ€“1000How many proxy lines to return. Default 1
countryISO 3166-1 alpha-2e.g. US, DE, JP. Omit for a worldwide IP
statestringState or region, where the product supports it
citystringCity, where the product supports it
ispstringNetwork / carrier, where the product supports it
stickyMinutesintegerKeep the same IP for this many minutes. Omit for a fresh IP on every request. The maximum is plan.generate.maxStickyMinutes; 0 there means the product has no sticky sessions

Each returned line is username:password@host:port with your targeting encoded in the username. Lines are on-demand and not persistent โ€” generate fresh ones when you need them instead of storing them, because credentials can rotate when the pool moves.

python
# Country, state and city โ€” what a plan supports is in plan.generate.targeting
bp.plans.generate(plan.id, country="US", state="California", city="Los Angeles")

# Sticky session: keep one IP for 30 minutes (max is plan.generate.max_sticky_minutes)
bp.plans.generate(plan.id, country="DE", sticky_minutes=30)

# SOCKS5 instead of HTTP
bp.plans.generate(plan.id, protocol="socks5", count=10)

# Bulk: up to 1000 lines per call
result = bp.plans.generate(plan.id, count=1000)
open("proxies.txt", "w").write("\n".join(result.lines))

Every top-up and allocation call takes a product key. Live prices come from the catalog call, so you never hard-code a rate.

KeyProductMin orderBest for
residentialCore Residential1 GBEveryday rotating residential IPs with country targeting
premiumPremium Residential2 GBCity-level targeting, sticky sessions and priority routing
ispISP Rotating1 GBDatacenter speed with ISP-grade trust
mobileMobile Proxies1 GBReal carrier IPs for mobile-only targets
ipv4Datacenter IPv41 GBFast, cheap per-GB datacenter traffic
ipv6IPv6 Proxies1 GBHuge address diversity at the lowest per-GB rate

Pay-as-you-go top-ups of 100 GB or more include a 10% volume bonus, and a single order is capped at 10,000 GB.

Using the proxies with your stack

Each generated proxy is a small object (host, port, username, password, protocol) with helpers that return exactly what common clients expect, so you never hand-build a proxy URL or worry about percent-encoding special characters in a password.

requests

python
import requests

print(requests.get("https://api.ipify.org", proxies=proxy.to_requests()).text)

httpx

python
import httpx

with httpx.Client(proxy=proxy.to_httpx()) as client:
    print(client.get("https://api.ipify.org").text)

aiohttp

python
async with aiohttp.ClientSession() as session:
    async with session.get("https://api.ipify.org", proxy=proxy.to_aiohttp()) as r:
        print(await r.text())

Playwright

python
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(proxy=proxy.to_playwright())
    page = browser.new_page()
    page.goto("https://api.ipify.org")

Scrapy

python
def start_requests(self):
    yield scrapy.Request("https://api.ipify.org", meta=proxy.to_scrapy_meta())

Account, balances & history

Read-only calls for building dashboards, alerts and pre-flight checks. balances is the one to watch: it shows spendable GB per product, including GB from a payment that's still being added (pending).

python
catalog = bp.products.list()           # product keys & live prices โ€” no API key needed
account = bp.account.get()             # email, plan, lifetime GB
balances = bp.balances.list()          # spendable GB per product (+ GB still arriving)
plans = bp.plans.list()                # plan ids and what each can generate
history = bp.transactions.list(limit=20)

Buying bandwidth

  1. Call the top-up method โ€” it returns a hosted payment URL and a transaction id. Card payments are processed by Stripe Checkout; crypto by an invoice page.
  2. Send your user (or yourself) to that URL to pay.
  3. Poll the transaction (or use the SDK's wait helper) until status is succeeded or failed. Crypto payments are checked live on every poll, so they settle even if a webhook is delayed.
  4. The GB shows up under balances within seconds โ€” pendingGb while it's arriving, activeGb once it's live.

Plans are subscriptions billed monthly or yearly by card (crypto only covers pay-as-you-go top-ups). If the account already has a subscription, the call returns the subscription settings page as the URL and a null transaction id โ€” switching plans is prorated there, and you can't accidentally subscribe twice.

python
# Buy 10 GB of Core Residential by card โ€” open pay_url in a browser
checkout = bp.topup.card(product_key="residential", gb=10)
print(checkout.pay_url)

# Or subscribe to a plan (monthly or yearly)
bp.topup.card(plan="pro", interval="year")

# Crypto (pay-as-you-go top-ups only)
bp.topup.crypto(product_key="residential", gb=50)

# Wait for the payment to settle โ€” GB appears on bp.balances.list() right after
tx = bp.transactions.wait(checkout.transaction_id)
print(tx.status)  # "succeeded"

Card payments are processed by Stripe; crypto top-ups are pay-as-you-go only. Subscriptions can be managed or cancelled from Dashboard โ†’ Billing.

Reseller API

The reseller API lets you build a proxy product on top of BuyProxy. The flow is always the same:

  1. Fund your pool โ€” buy GB per product into your reseller pool by card or crypto. Pool GB is priced like a normal top-up (including the volume bonus), fixed at purchase time.
  2. Create a customer โ€” an isolated sub-account with its own credentials and no bandwidth. Store the returned username.
  3. Allocate โ€” move whole GB of one product from your pool onto that customer, with an idempotency key. The first allocation of a product creates the plan; later ones add to it.
  4. Generate โ€” produce proxy lines for the customer from their plan id (found on the customer record), with all the usual targeting options.
  5. Monitor โ€” read the customer's usage (remaining, used, total per plan, plus any GB still arriving) to bill or alert.
  6. Suspend / reactivate โ€” a suspended customer can't generate new lines (403). Lines already handed out may keep working until they rotate, so stop distributing them at the same time. You can also rename a customer.

Allocated GB can't be moved back to the pool, so allocate what a customer has paid for.

python
reseller = BuyProxy(api_key="bp_live_โ€ฆ")

reseller.reseller.pool.topup_card(product_key="residential", gb=500)       # buy into your pool
customer = reseller.reseller.customers.create("Acme Inc")                  # isolated sub-account

reseller.reseller.customers.allocate(
    customer.username, product_key="residential", gb=20, idempotency_key="order-1042",  # safe to retry
)

detail = reseller.reseller.customers.get(customer.username)
result = reseller.reseller.customers.generate(customer.username, plan_id=detail.plans[0].id, country="DE")

reseller.reseller.customers.update(customer.username, status="suspended")

Allocating bandwidth to a customer moves real GB, so it accepts an idempotency key. Send the same key twice and the second call moves nothing and returns alreadyApplied: true with the same result โ€” safe to retry after a timeout or crash.

Use a value you can derive again from your own data (an order id, an invoice number), not a random value created per attempt. Keys are scoped to your account and may be up to 200 characters.

Errors & retries

Every non-success response becomes a typed error carrying the HTTP status and the API's own message, so you can branch on the failure instead of parsing strings.

HTTPExceptionMeaning
400ValidationErrorA parameter was missing or invalid
401AuthenticationErrorMissing or invalid API key (also raised locally when no key is set)
402InsufficientBandwidthErrorEmpty reseller pool, or a plan with no GB left
403PermissionDeniedErrorSuspended account/customer, or the plan doesn't include this endpoint
404NotFoundErrorPlan, customer or transaction not found on this account
429RateLimitErrorOver 60 requests/minute โ€” has retry_after_seconds
5xxServerErrorBuyProxy or an upstream provider is unavailable
โ€”APIConnectionError, APITimeoutErrorNo response (DNS, reset, offline) / exceeded timeout
python
from buyproxy import AuthenticationError, InsufficientBandwidthError, RateLimitError

try:
    bp.plans.generate(plan_id)
except InsufficientBandwidthError:
    print("Out of GB โ€” top up first")
except RateLimitError as e:
    print(f"Slow down; retry in {e.retry_after_seconds}s")
except AuthenticationError:
    print("Check your API key")

Every exception extends BuyProxyError and carries .status and the parsed .body.

Every SDK retries for you, with the same rules:

  • Rate limits (`429`) are always retried, waiting for the Retry-After the API sends (capped at 30 s) or an exponential back-off with jitter.
  • Reads (GET) are also retried on 502/503/504 and on network failures.
  • Writes are not retried on server errors โ€” a repeated checkout or allocation must never double-charge โ€” unless you attach an idempotency key (see below), which makes the retry provably safe.
  • The default is 2 retries; set it to 0 to turn retries off. After the last attempt you get the typed error.

The API allows 60 requests per minute per key. Batch with count (up to 1000 lines per call) rather than looping over single-proxy calls.

Configuration

python
BuyProxy(
    api_key="bp_key_โ€ฆ",            # default: $BUYPROXY_API_KEY
    base_url="https://www.buyproxy.org",
    timeout=30.0,
    max_retries=2,                  # 429s always; 5xx only for safe requests
    http_client=my_httpx_client,    # bring your own httpx.Client
)

Concurrency

python
import asyncio
from buyproxy import AsyncBuyProxy

async def main() -> None:
    async with AsyncBuyProxy() as bp:
        plans = await bp.plans.list()
        results = await asyncio.gather(*(bp.plans.generate(p.id, count=10) for p in plans))

asyncio.run(main())

Testing your code

You don't need a real account or network to unit-test code that uses the SDK โ€” inject a fake transport. This is how the SDK's own test suite works.

python
import httpx
from buyproxy import BuyProxy

def handler(request: httpx.Request) -> httpx.Response:
    return httpx.Response(200, json={"lines": ["u:p@gw.buyproxy.org:6969"]})

# httpx.MockTransport โ€” no network, no extra library
bp = BuyProxy("bp_key_test", http_client=httpx.Client(transport=httpx.MockTransport(handler)))
assert bp.plans.generate("plan_1").proxies[0].host == "gw.buyproxy.org"

API reference

Every method maps one-to-one onto a documented REST endpoint.

CapabilityCall
Catalog & pricesbp.products.list()
Accountbp.account.get()
Spendable GB per productbp.balances.list()
Plansbp.plans.list()
Generate proxiesbp.plans.generate(plan_id, **params)
Card checkoutbp.topup.card(...)
Crypto checkoutbp.topup.crypto(...)
Paymentsbp.transactions.list() ยท .get(id) ยท .wait(id)
Create an accountbp.auth.signup() ยท .verify_otp() ยท .resend_otp()
Reseller summarybp.reseller.me()
Customersbp.reseller.customers.list/create/get/update/usage/allocate/generate
Reseller poolbp.reseller.pool.get/topup_card/topup_crypto

Notes

  • Responses are frozen dataclasses with snake_case attributes; unknown fields from newer API versions are ignored rather than raising.

  • Use it as a context manager (with BuyProxy() as bp:) to close the HTTP connection pool.

  • ProxyEndpoint has helpers for requests, httpx, aiohttp, Playwright, Scrapy and environment variables.

Developing the SDK

bash
python -m venv .venv && . .venv/bin/activate
pip install -e ".[dev]"
ruff check src tests && mypy src && pytest

Was this page helpful?