Every postmortem I have read from someone whose trading bot got drained has the same shape. The key had more permissions than the bot needed, it lived in more places than anyone remembered, and by the time the owner noticed, the interesting part was already over. The leak itself is usually mundane. A key sitting in a git commit from eight months ago, or printed into a CI log, or saved in a shared Postman collection that someone exported to a personal account. Exchange API security is one of the few areas in trading where the defenses are cheap, well understood, and mostly a one-afternoon job, and people still skip them because the bot runs fine without them right up until it very much does not.
Withdrawal permission stays off, forever
Every major exchange scopes API keys into some version of read, trade, and withdraw. Some go more granular, with separate toggles for futures, margin, or internal transfers, but those three are the core. The rule I would tattoo on every bot builder's forearm is that a bot key never gets withdrawal permission. There is no convenience case that justifies it and no temporary exception that stays temporary. If your architecture seems to require the bot to move funds off the exchange, the architecture is wrong, and the fix is a human doing withdrawals manually, or a separate and heavily locked down process that shares nothing with the trading loop.
The part most people miss is that a trade-only key is still worth real money to an attacker. The classic move is the illiquid pair siphon. The attacker takes your leaked key, finds a dead market where they can be the only meaningful seller, places asks at a silly multiple of fair value from their own account, and then uses your key to market buy into their own orders. Your balance converts into their profit through fills that look, from the exchange's side, like ordinary bad trading. No withdrawal ever happens and the money is still gone. So trade-only scope removes the instant drain scenario and shrinks the damage, but it does not make a leaked key harmless, which is what the next two layers are for.
Allowlists and subaccounts do the actual containment
An IP allowlist binds the key to the egress address of the machine the bot runs on. Once it is set, a copy of the key stolen from a repo or a log file is close to useless, because requests from the attacker's box come from the wrong address and get rejected before permissions even come into play. The practical requirements are a stable IP for the bot, so a VPS or a NAT gateway rather than a home connection that reissues addresses, and remembering to update the allowlist when you migrate servers, ideally before the old box goes away rather than after. It is worth noticing that several large exchanges quietly expire or restrict keys that have no allowlist after roughly ninety days, and some will not enable higher risk permissions at all without one, which tells you how they rank this control internally.
The allowlist does nothing when the attacker is on your server itself, and that is the case subaccounts handle. One subaccount per bot, funded with only what the strategy needs to run, with the treasury sitting on the master account behind no API key at all. When a strategy needs a top up, a human moves the funds, or a narrowly scoped transfer job does it on a schedule with hard limits. A full compromise of one bot, key and server and everything, is then capped at that subaccount's balance instead of your whole stack. As a side effect, per strategy accounting gets dramatically cleaner, which you only really appreciate after you have tried to untangle three strategies sharing one account.
Rotation that does not break the bot
Key rotation has a bad reputation because most people do it the naive way exactly once, take their bot down for half an hour in the middle of a position, and never try it again. The naive way is delete first, create second. The correct way is an overlap. Exchanges generally allow several active keys per account, so you create the new key with identical scopes, add it to the same IP allowlist, push it to the bot through your secrets manager, watch authenticated calls succeed on the new credential, and only then revoke the old one. The bot never sees a gap. If your bot cannot reload credentials without a restart, that is worth fixing eventually, but even with a restart the overlap method costs seconds instead of a scramble. Label every key with the bot name and creation date so that revoking the right one later takes no detective work.
For cadence, roughly quarterly is a sane default for trade keys, and I would not agonize over the exact interval. The rotations that actually matter are the event driven ones. Someone with key access leaves the team. A vendor you shared a read-only key with reports an incident. A laptop goes missing. You pasted a key into a chat to debug something and told yourself you would rotate it later. Any of those outranks the calendar. And if a key has ever appeared in git history, even in a long deleted file on a private repo, treat it as burned and rotate the same day. Scrapers hunt for leaked exchange credentials continuously, and historically the gap between public exposure and first abuse has been measured in minutes.
The first ten minutes after a suspected leak
Speed beats diagnosis here. You can work out how it leaked later. In order:
- Delete the key at the exchange. Deletion invalidates it immediately, while editing permissions on a live key does not always cut off sessions already in flight, depending on the venue.
- Cancel every open order on the affected account, then read the order list again and look for anything you did not place, especially in markets your bot never touches.
- Pull recent trade history and scan for uneconomic fills, meaning tiny pairs, prices far from mid, or sizes that do not match your strategy. This is where the illiquid pair siphon shows up.
- Check the account itself for new API keys, changed withdrawal addresses, and changes to email or two factor settings. If any of those moved, you are dealing with a full account takeover rather than a leaked key, and the playbook escalates to locking the account and contacting the exchange directly.
- Rotate every other credential that lived in the same secrets store, on the same server, or in the same repo as the burned key. At minute ten you do not know the leak path yet, and siblings tend to leak together.
Once the bleeding stops, resist the urge to mint a replacement key and move on. If you never find the leak path, the new key inherits the same exposure, and you will be doing this again in a few months with worse luck.
We ended up encoding a chunk of this into Blockcircle's execution layer, since everything there runs non-custodially on user keys, and a key that arrives with withdrawal enabled gets flagged at connection time because there is no legitimate reason for us to hold one. But none of it depends on any particular platform. The full kit is trade-only scope, an IP allowlist, one subaccount per strategy, overlap rotation on a loose quarterly rhythm, and the ten minute playbook written down somewhere you can find it while stressed. That is an afternoon of setup, and it turns the worst day of your trading career into an annoying Tuesday.