Connection, rate limits and bans
How the Binance connection is built and cached, why it signs requests slightly late, what happens when you exhaust Binance's rate limit, and the Redis key that can mute the whole spot stack.
Everything the platform sends to Binance goes through one process-wide manager. Understanding what it caches, when it re-reads configuration and how it reacts to being throttled explains most of the behaviour operators find surprising.
One connection, cached for the life of the process
The manager keeps a map of live connections keyed by provider name, and there is never more than one Binance connection per backend process. It is built lazily, on the first request that needs it, and it is not rebuilt afterwards.
Consequences:
.envchanges need a restart. Credentials are read when the connection is constructed, from the environment the process started with.- Saving a proxy does not need one. Writing the proxy URL explicitly evicts the cached connection, so the next request rebuilds through the proxy.
- The backend and the cron worker have separate connections. They are separate processes. Restart both when you change credentials.
- Concurrent callers do not stampede. Requests that arrive while the connection is being built queue and are handed the same instance.
The connection itself is created with a 30-second request timeout, client-side rate limiting enabled, and Binance's maximum tolerance window for signed requests.
Credential resolution
The variable names are assembled at runtime from the active provider's alias:
APP_BINANCE_API_KEY
APP_BINANCE_API_SECRET
APP_BINANCE_API_PASSPHRASE ← read, but Binance does not use oneIf the key or secret is empty or absent, the manager logs it, records a failed attempt and returns nothing. After three failed attempts it refuses to try again for thirty minutes. That is a deliberate guard against hammering an exchange with bad credentials, and it means a fix applied during the cooldown appears not to work until the cooldown expires or the process restarts.
The clock
Binance rejects any signed request whose timestamp is more than a second ahead of its own clock. The tolerance window only widens the "behind" side. So the platform deliberately signs from slightly behind the server.
Before markets are loaded, and every five minutes thereafter in the background, the manager brackets a server-time call between two local readings, places the server's stamp at the midpoint so network latency is not mistaken for drift, and stores the offset as local minus server plus a 500 ms safety margin.
If the time call itself fails, it falls back to signing a full second behind. Being early is the only failure mode; being late is absorbed by the window.
The background resync claims its slot before running, so a burst of concurrent requests produces one time call rather than dozens. Callers never wait for it.
Loading markets on Binance calls the currency catalogue first, and that is a signed request. It happens before ccxt's own time adjustment would run, so the very first authenticated call is only as good as the offset set here. A clock-skew failure at that point is caught, the connection is thrown away, a fresh one is built with a fresh sync, and the load is retried — up to three times.
Errors treated as clock problems: Binance code -1021, ccxt's invalid-nonce
error, and any message mentioning a timestamp, the receive window, or being
ahead of or behind the server.
The silent fallback
If loading markets fails for a reason that is not a rate limit and not a clock problem, the manager does not give up. It closes the authenticated connection and builds a second one with no credentials at all, so public data keeps flowing.
This is why a broken key can look like a working install. Tickers update, charts render, the market list is populated — and at the same time the currency import returns an empty catalogue and deposit addresses cannot be issued, because both need a signed request.
The fallback is logged as a warning naming exactly what is now unavailable. If you only ever look at the site, you will not see it.
Rate limits
Two layers throttle requests, and only one of them is under your control.
Client-side. ccxt's own rate limiter is enabled on every connection, so calls are spaced according to Binance's published weights rather than fired as fast as the code can issue them.
Server-side. Binance counts request weight per IP and per key, and answers
an exhausted budget with a rate-limit error — code -1003 — sometimes
accompanied by an explicit ban until a given time.
What the platform does when it is throttled
| Trigger | Response |
|---|---|
| A rate-limit error while building the connection | A one-minute ban is recorded and the initialisation sleeps for a minute before retrying |
An error message containing IP banned until <time> |
That time is parsed and recorded as the ban expiry |
| A ban expiry more than 24 hours away | Clamped to 24 hours, with a warning naming the original value |
| A ban expiry already in the past, or unparseable | Ignored |
| Repeated errors in the ticker stream | Exponential backoff — 5 s, 10 s, 20 s, then 30 s |
| An error naming specific symbols | Those markets are switched off and the stream continues |
| An HTML error page instead of JSON | Reported as "Exchange service temporarily unavailable" rather than a parse error |
The ban key
The expiry is stored in Redis under exchange:ban_status, as an epoch
millisecond value, with a TTL that matches the remaining time so it clears
itself.
Every path that needs Binance — the ticker stream, the price cron, the deposit verifier, the withdrawal reconciler, order placement, the admin finance screens — asks for a connection and receives nothing. They log a warning and return. No screen turns red. The platform reports itself healthy and simply stops moving.
The one place it surfaces in the admin panel is the chart settings response, which reports whether a ban is active and how many seconds remain.
Nothing deletes the key early. If you are certain the ban has been lifted on
Binance's side, removing exchange:ban_status from Redis is what unwedges the
stack — but doing that while Binance is still throttling you simply earns a
longer ban.
The load you control
Two workloads dominate request weight, and both are configuration rather than traffic:
- The ticker stream. For Binance the platform polls
fetchTickerswith every enabled market on a roughly one-second cadence, accumulating results and flushing them to subscribed browsers and to a Redis cache once a second. Weight scales with how many markets you enabled, not how many customers are watching. - Chart cache builds. History fetches walk every market and every interval. The per-request delay defaults to 500 ms and can be raised to 10 seconds. Raise it for large rebuilds rather than lowering it.
Per-symbol data on the trade screen is different: price, candles and order book there arrive over Binance's own WebSocket streams, which do not consume REST weight in the same way.
The network path
Without a proxy the outbound agent forces IPv4, with keep-alive and a 30-second timeout. Exchange IP allowlists are IPv4, and a dual-stack server that happens to prefer IPv6 would present an address that was never allowlisted.
With a proxy configured on the provider row, that agent is replaced. http://,
https://, socks4:// and socks5:// are recognised; an unrecognised scheme
falls back to an HTTPS proxy agent with a warning; an unparseable URL is
rejected and the connection is built without a proxy.
The proxy test distinguishes three failures that look identical from the outside: the proxy itself is unreachable, the proxy authenticated poorly, or the proxy connected fine and its region is also blocked by Binance.
A region block from Binance is HTTP 451. It is the one connection failure a proxy is the correct answer to.
Admin endpoints for the provider
The screens these serve are described in the core admin panel reference; the permission keys themselves are listed in the permissions reference.