Rate limits your bot will hit on our API, and how to stay under them

2026-10-13 · Bazyl

Your bot can hit two ceilings on our API, and it will meet a third thing that looks like one and isn't. The account API allows 60 requests a minute per account. The partner API allows 120 by default. A 407 from a gateway in the first minute after creating credentials is warm-up, not a limit. After this post you'll know all three and have a poller that never trips the first.

The mistake is polling the balance on every request. A bot that checks /balance before each task spends the budget by the second task of the second worker. Once a minute, from one process, is the whole budget you need.

The account API: 60 a minute, per account, both keys together

The account API at /api/v1 is read-only, seven GETs, and it allows 60 requests a minute per account.

An account holds at most two active keys, and the limit is per account, not per key, so a second key does not buy a second 60. It is there so you can replace the first one without a gap.

Past the ceiling you get 429 with code RATE_LIMITED, a Retry-After header in seconds, and the message Rate limit exceeded (60 requests/min). The body has the shape every account API error has:

{ "error": { "code": "RATE_LIMITED", "message": "Rate limit exceeded (60 requests/min)" } }

Read the header, sleep that many seconds, send again. A bot with 40 tasks that each checked the balance before starting would spend 40 of the 60 in the first second of a drop, and the 41st task would be the one reading this header.

The partner API: 120 a minute by default, more on request

The partner API at /api/reseller/v1 allows 120 requests a minute by default, we raise it on request, and GET /me reports the limit your account has.

Past it, the same answer: 429 RATE_LIMITED with Retry-After in seconds.

Two smaller budgets inside that one matter to a bot. A live balance read with ?fresh=true has its own allowance of 6 a minute; the default read is served from a cache at most about 45 seconds old and is what a dashboard should use. Keep fresh for the moment right after a move you need to confirm. And three answers look like limits but are the system asking for a short wait: 503 SYSTEM_BUSY (retry in seconds), 409 PENDING_OPERATIONS (a move is still settling, retry in about a minute) and 409 REQUEST_IN_FLIGHT, whose name says what to wait for.

What makes a blind retry safe on this API is the Idempotency-Key header. Send one on every POST and DELETE. A retry with the same key returns the stored response, marked with an Idempotency-Replayed: true header, and never moves gigabytes twice. A stored 4xx is replayed too, so once you've fixed whatever caused it, send a new key.

The gateways: a 407 in the first minute is warm-up

New credentials can answer 407 Proxy Authentication Required for about a minute while the gateways pick them up. The partner API says so in the create response, warmupSeconds: 60. That is not a wrong password and it is not a rate limit; retry and it clears.

I publish no request ceiling for the gateways, so I won't quote one here. The two numbers above are the ones your code has to respect. If a string keeps answering 407 after a minute, check that every targeting token sits on the username, because the password carries nothing; the parser post has the full token list.

Stay under: one poller, a cache, and a DM

Four habits keep a bot under 60 a minute without anyone thinking about it.

Poll /balance once a minute from one process and put the number in a cache; every worker reads the cache. That is 1 request of the 60, however many workers you run, and it leaves 59 for everything else.

Let the Discord bot push instead of polling for a threshold. /alerts sends a DM at a balance you set, so the "am I about to run dry" question costs zero requests.

Ask /proxies for lines in bulk. count goes up to 1,000 in one call (the default is 10), and the call spends no data, so a bot that needs 500 lines makes one request, not 500.

Subscribe to nothing, because there is nothing to subscribe to. The account API is seven GETs and no more, so the push channel for a balance is the DM above and the pull channel is the poller below. There is no webhook to wait for.

The poller, with Retry-After backoff

import os
import time

import requests

URL = "https://api.basilproxies.com/api/v1/balance"
HEADERS = {"Authorization": f"Bearer {os.environ['BASIL_API_KEY']}"}


def balance_mb(tries=5):
    """GET /balance with Retry-After backoff. Returns residential MB left."""
    for attempt in range(tries):
        r = requests.get(URL, headers=HEADERS, timeout=10)
        if r.status_code == 429:  # RATE_LIMITED
            wait = int(r.headers.get("Retry-After", 2 ** attempt))
            time.sleep(wait)
            continue
        r.raise_for_status()  # 401 = missing, wrong or revoked key
        return r.json()["residential"]["remainingMb"]
    raise RuntimeError(f"still rate limited after {tries} tries")


# One poller, once a minute: 1 of the 60 requests the account gets per minute.
# Workers read `cache`; nothing else on the box calls /balance.
cache = {"mb": None}
while True:
    cache["mb"] = balance_mb()
    print(f"{cache['mb'] / 1000:.2f} GB left")  # 1 GB = 1000 MB
    time.sleep(60)

Keep the key in the environment, never in the script, and reuse the same loop for /usage if you chart it. The seven endpoints and the error shape are on the API page of the docs.

Mint a key under Tools → API in the dashboard, put it in BASIL_API_KEY, and build the rest against the endpoint list on the bot developers page.

Common questions

How many requests a minute does the account API allow?
60 per account, shared by both of its keys. Past that you get 429 RATE_LIMITED with a Retry-After header in seconds and the message Rate limit exceeded (60 requests/min).
Does a second API key double my limit?
No. The limit is per account, so two keys share the same 60 a minute. The second key is there so you can replace the first without a gap; two active keys is the maximum.
Is a 407 from the gateway a rate limit?
No. New credentials can answer 407 for about a minute while the gateway picks them up; the partner API reports it as warmupSeconds 60. Retry. It is not a bad password and not a limit.
Is it safe to retry a partner API call that timed out?
Yes, with the same Idempotency-Key. The replay returns the stored response and never moves gigabytes twice. A stored 4xx is replayed too, so after fixing the cause send a new key.