Key takeawaysA Bybit API key with no bound IP becomes invalid after 90 days; bind at least one address and the expiration is removed entirely.
- A Bybit API key with no bound IP becomes invalid after 90 days; bind at least one address and the expiration is removed entirely.
- Since February 10, 2026, whitelist entries on master API keys can’t be added or removed through the API — the edit is browser-only, so provisioning scripts must hand that step to a human.
- Error 10010
Unmatched IPmeans the request arrived from an address outside the key’s list; verify the egress IP through the same route your bot uses. - Bybit answers with 403 to requests from US and mainland China IPs before authentication even starts, so check that Bybit responds from an address before you bind it.
- We ran that check on our own pools: a 1,000-IP datacenter package, a 1,000-IP ISP package, and 50 randomly sampled dedicated proxies — zero blocked subnets in all three sets.
- A V5 signature covers the string
timestamp + api_key + recv_window + parameters, signed with HMAC SHA256 (lowercase hex) or RSA SHA256 (base64); the timestamp must fall within[server_time − recv_window; server_time + 1000).
This summary was created with AI.
How to create an API key on Bybit
Key creation lives in the website only: log in from a desktop browser, open Account → API Management, and click to create a new key. Two conditions catch people at this step. The mobile app doesn’t create API keys at all, and on a fresh account the option may not appear for the first 48 hours — Bybit’s Help Center guide to API keys notes that creation can be restricted after registration for risk control.
The form asks you to pick a key type. System-generated keys use HMAC: Bybit issues both the key and the secret, and the secret is shown once, at creation — store it before closing the page. Self-generated keys use RSA: you create the key pair locally and upload only the public part, so Bybit never holds your private key. Creation is confirmed with 2FA and an email code.
One more thing to fix at this stage is the environment. Mainnet, testnet, and the two demo environments use separate hosts and separate keys. Error 10003 API key is invalid often isn’t about the key at all — it’s a testnet key sent to api.bybit.com or the reverse.
Choosing API key permissions
Permissions are set per product area — contract trading, spot trading, wallet transfers, convert, earn — on top of the base read-only or read-and-write choice. The working rule is the minimum set: a dashboard that only tracks positions needs read-only access; a spot bot needs read-and-write plus the Spot scope and nothing else; transfer and withdrawal rights belong only on keys whose entire job is moving funds, and those keys deserve the strictest IP list of all.
Give each integration its own key instead of reusing one everywhere. Scoped keys limit the damage of a single leak and let you revoke one tool without stopping the rest. What a complete permission set looks like for an automated strategy — and what else a bot needs beyond a working order call — we broke down in the guide to building a trading bot on the Binance API; the exchange differs, the reasoning transfers as is.
Binding an IP address: what the whitelist changes
The whitelist documentation puts the main rule in one line: an API key with no bound IP becomes invalid after 90 days. Bind at least one address and the countdown disappears — the key no longer expires. There’s a second, less-known timer: after an account password change, a key without a bound IP is invalidated within 7 days.
You can watch the countdown from the API itself. GET /v5/user/query-api returns deadlineDay and expiredAt — and only for keys with no bound IP or after a password change; a bound key simply carries no expiry. When an unbound key does lapse, Spot requests return error -2015 Your api key has expired and derivatives requests return 33004.
Editing the list changed in 2026. The V5 changelog states it directly: effective February 10, 2026, users can no longer add or remove IP whitelist entries via the Modify Master API Key endpoint. An API attempt now returns error 141019, which says to go to API Management and select Edit. In practice this means whitelist changes are a manual, browser-based step — deployment pipelines that re-provisioned servers and updated key whitelists automatically need a human in the loop now. That’s one more argument for binding an address that never changes.
If a request comes from an address outside the list, Bybit returns error 10010 Unmatched IP, please check your API key’s bound IP addresses — the most common failure on freshly bound keys, and almost always an egress mismatch rather than a Bybit-side problem.
Binance runs the same 90-day clock, with a different consequence: there, an unrestricted key loses its spot trading permission rather than dying outright. We covered how the Binance API key IP whitelist works separately; if you run bots on both exchanges, the two lists are maintained independently and both point at the same static egress address.
Which address to bind — and our reachability test
The address on the list must be your egress IP — the one Bybit sees, not the one your machine shows locally. Behind NAT the local address is 192.168.x.x while the exchange sees the router; a container platform can change egress between deployments; a residential connection can get a new address from the ISP overnight. Check it through the exact route the bot uses:
import requests
# Plain route — the egress IP of the machine itself:
print(requests.get("https://api.ipify.org").text)
# Through the bot's proxy — the address Bybit will actually see:
proxy = {"https": "http://login:password@PROXY_IP:port"}
print(requests.get("https://api.ipify.org", proxies=proxy).text)
Whatever the second call prints is what belongs in the whitelist. And since every list edit now costs a browser login, a stable address stopped being a convenience and became the design requirement: one static egress IP per key, bound once.
Stability isn’t the only constraint — Bybit must be willing to answer that address at all. The integration guide warns that requests from IPs in the US or mainland China get 403 Forbidden, and several countries are served from their own regional hosts (api.bybit.nl, api.bybit.tr, api.bybit.kz, and others). Beyond geography, Bybit’s edge infrastructure is known to reject requests from IP ranges of major cloud platforms — a 403 before authentication even runs, which is easy to misread as a key problem.
So before binding an address, confirm the exchange responds from it. We ran that check across our own pools — a request to the public V5 API from every address, counting subnets Bybit refused:
| Address set | Addresses checked | Subnets blocked by Bybit |
|---|---|---|
| Datacenter package | 1,000 | 0 |
| ISP (static residential) package | 1,000 | 0 |
| Dedicated proxies, random sample | 50 | 0 |
Every address received a normal API response. The scope is deliberately narrow — this is a reachability check, not a stress test of rate limits or account bans — but it answers the question that matters for a whitelist: an address you bind from these pools is an address Bybit talks to.
That’s the setup our crypto proxies are built for: dedicated IPv4 addresses that stay static for the plan term, one per key or bot, in locations outside Bybit’s restricted regions, with HTTP, HTTPS, SOCKS4, and SOCKS5 on every address and access by IP whitelisting. Bind the same proxy IP in Bybit’s API Management once, and the key neither expires nor trips 10010.
Signing requests: V5 authentication
Binding an IP doesn’t change how requests are signed — V5 authentication rides in four HTTP headers: X-BAPI-API-KEY, X-BAPI-TIMESTAMP (UTC, milliseconds), X-BAPI-RECV-WINDOW (defaults to 5,000 ms), and X-BAPI-SIGN. The string you sign is the concatenation timestamp + api_key + recv_window + queryString for GET requests, with the raw JSON body in place of the query string for POST. HMAC keys sign it with SHA256 and send lowercase hex; RSA keys send base64.
Here’s a complete signature example in Python — the call requests your own key’s state, so it doubles as a whitelist check:
import hmac, hashlib, time, requests
API_KEY = "YOUR_API_KEY"
API_SECRET = "YOUR_API_SECRET"
RECV_WINDOW = "5000"
def signed_get(path, params):
ts = str(int(time.time() * 1000))
query = "&".join(f"{k}={v}" for k, v in params.items())
payload = ts + API_KEY + RECV_WINDOW + query
sign = hmac.new(API_SECRET.encode(), payload.encode(),
hashlib.sha256).hexdigest()
return requests.get(
"https://api.bybit.com" + path,
params=params,
headers={
"X-BAPI-API-KEY": API_KEY,
"X-BAPI-TIMESTAMP": ts,
"X-BAPI-RECV-WINDOW": RECV_WINDOW,
"X-BAPI-SIGN": sign,
},
)
# Returns the key's permissions, bound IPs, and — for unbound keys — expiredAt:
print(signed_get("/v5/user/query-api", {}).json())
Two errors dominate this stage. Error 10004 Error sign means the signed string didn’t byte-match what was sent — the query string in the payload must be exactly the one that goes on the wire, including parameter order. Error 10002 points at the clock: the timestamp must satisfy server_time − recv_window ≤ timestamp < server_time + 1000, so a machine that drifts a second ahead of the exchange starts failing. Keep the clock NTP-synced; the clock-sync practice we described for the Binance API applies to Bybit unchanged.