Short answer: A Binance API key is created under Account, API Management, and lets a program read your account or trade without your password. Binance recommends self-generated Ed25519 keys and calls HMAC keys deprecated without a date (key types page, last modified 30 October 2026); an HMAC secret is shown once. For a bot, allow reading and spot trading only, keep withdrawals and transfers off, and restrict the key to your server's IP.
Facts come from Binance's documentation and support pages, read on 6 November 2026. No live key was used: the key check below ran against a local mock, because the Spot Testnet returned HTTP 404 for /sapi paths that day, and only a random key's rejection was tested live. Never paste a live secret or private key anywhere: chat, AI assistant, support ticket, screenshot, repository or online signature tool.
This is not a recommendation of Binance or any strategy; automated trading can lose money, and availability depends on your jurisdiction and Binance's terms. Article 99/A of Capital Markets Law No. 6362 treats activity by foreign crypto-asset platforms directed at Turkish residents as unauthorized.
How to create a Binance API key in API Management
Create it on the API Management page once two-factor authentication is on, any amount has been deposited to your Spot Wallet and identity verification is complete. Steps from Binance's support article (last updated 20 March 2025):
- Profile icon, Account, API Management, Create API; in the app, the Pro version under More, Services, Other.
- Pick a type: system-generated (HMAC) gives you an API key and a secret key; self-generated means you create an Ed25519 or RSA pair and upload only the public key.
- Enter a label and verify with your 2FA devices or passkeys; a separate key per program makes rotation easier.
Spot Testnet keys come from testnet.binance.vision (Log In with GitHub, read 6 November 2026), a separate system reset about monthly without notice. Related: Binance API Python guide, Binance Futures testnet API key, other exchanges' API keys.
Binance API key and secret key: usage and key types
The API key only names you in the X-MBX-APIKEY header; the signature, made with a secret Binance generated (HMAC) or a private key only you hold (Ed25519, RSA), proves the request. Per the Spot REST page (last modified 30 October 2026), keys are needed only for signed endpoints: TRADE (orders), USER_DATA (account and order data) and USER_STREAM; public market data needs none.
| Type | Binance holds | Status |
|---|---|---|
| Ed25519, self-generated | Your public key | Recommended; supported since 18 July 2023 |
| RSA 2048 or 4096 bits, self-generated | Your public key | Supported since 15 December 2022 |
| HMAC-SHA256, system-generated | The same secret you hold | Deprecated, no date given |
- The secret is shown once. Binance's API FAQ (last updated 13 January 2025): secret keys are visible only at creation, so a lost one means a new key.
- Deprecated since 2023. The deprecation note dates from the page's first version, 18 July 2023 (GitHub history); neither the page nor the Spot changelog (last updated 30 October 2026) gives a cutoff, and the support article still describes system-generated keys.
- Futures REST. USD-M General Info (last modified 30 October 2026) shows only HMAC and RSA examples and its changelog never mentions Ed25519, though the futures WebSocket API takes only Ed25519 for login; test your key type.
Ed25519 setup and migration: the Ed25519 API key guide.
Binance API key permissions: what each switch allows
A trading bot needs reading and spot trading; withdrawals and both transfer permissions should stay off, because they let a stolen key move funds.
| Flag | Allows | Trading bot |
|---|---|---|
enableReading | Account and order queries | On |
enableSpotAndMarginTrading | Spot and margin orders | On for spot |
enableWithdrawals | Withdrawals; always needs an IPv4 restriction | Off |
enableInternalTransfer | Transfers between master and sub-accounts | Off |
permitsUniversalTransfer | The universal transfer endpoint | Off |
enableMargin, enableFutures, enableVanillaOptions, enablePortfolioMarginTrading, enableFixApiTrade, enableFixReadOnly | Margin, futures, options, portfolio margin and FIX access (FIX flags added 8 November 2024) | Only if your written rules need them |
Two official pages, both last modified 30 October 2026, disagree about a new key's default. The Spot REST page says "By default, an API key cannot TRADE"; the USD-M futures General Info page says "By default, API-keys can access all secure routes." Trust neither and read the flags. Per the support article, a key created before your Futures account opened, or any key once Portfolio Margin is active, cannot get Futures permission.
Binance API key IP restriction and expiry rules
Restrict every trading key to your server's IP addresses; no other setting limits where a stolen key works.
- Since 30 January 2023, per the support article, an unrestricted system-generated key can only read; more needs IPv4 restrictions, a self-generated key or disabled default security controls.
- No 90-day lapse. A 26 July 2021 rule ended trading permission after 90 days on keys without whitelisted IPs; Binance's update says it is no longer applicable as of 24 October 2023.
- Deletion. No inactivity deletion rule was found on Binance.com pages on 6 November 2026; the API FAQ mentions only purges of inactive users who used the API before 7 March 2018 and deletion when an account is disabled. Third-party claims of 30-day or 90-day deletion were not confirmed.
Hypothetical example. A key restricted to 203.0.113.7 starts returning -2015 on signed Spot calls after a redeploy moves the server's outbound address to 203.0.113.9, while public market data still loads. Check the outbound IP before creating a new key.
How to view a Binance API key and check what it can do
API Management lists each key and its settings but masks a system-generated secret after creation; from code, GET /sapi/v1/account/apiRestrictions (signed USER_DATA, weight 1, on https://api.binance.com) returns the flags.
The check refuses to start if a key can move funds, lacks an IP restriction or cannot trade; a missing flag counts as a failure. It ran on 6 November 2026 (Python 3.11.3, requests 2.31.0, cryptography 43.0.1) against a local mock using Binance's codes and messages. On production that day, a random key sent here drew HTTP 400 and -2008 "Invalid Api-Key ID." even with a stale or duplicated timestamp, so the mock checks the key first; its later order and other HTTP statuses are assumptions.
import base64, hashlib, hmac, json, secrets, threading, time
from http.server import BaseHTTPRequestHandler, HTTPServer
from urllib.parse import parse_qsl, quote, unquote
import requests
from cryptography.exceptions import InvalidSignature
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
class KeyProblem(Exception):
pass
def hmac_signer(secret): # system-generated key: hex HMAC-SHA256
if not (secret and secret == secret.strip()):
raise KeyProblem("config: secret missing or has whitespace")
return lambda q: hmac.new(secret.encode(), q.encode(), hashlib.sha256).hexdigest()
def ed25519_signer(private_key): # self-generated key: Base64, percent-encoded
if private_key is None:
raise KeyProblem("config: private key missing")
return lambda q: quote(base64.b64encode(private_key.sign(q.encode())), safe="")
def check_key(base, key, sign):
if not (key and key.isascii() and key.isprintable() and " " not in key):
raise KeyProblem("config: API key missing or malformed") # no request sent
query = f"timestamp={time.time_ns() // 1_000_000}"
try:
r = requests.get(f"{base}/sapi/v1/account/apiRestrictions?{query}"
f"&signature={sign(query)}", headers={"X-MBX-APIKEY": key}, timeout=10)
except requests.RequestException as err:
raise KeyProblem(f"network: {type(err).__name__}, key not tested") from None
try:
p = r.json()
except ValueError:
p = None
if not (r.status_code == 200 and isinstance(p, dict)): # status first, then .get
p = p if isinstance(p, dict) else {}
raise KeyProblem(f"{p.get('code', f'HTTP {r.status_code}')}: {p.get('msg', 'no body')}")
bad = [f for f in ("enableWithdrawals", "enableInternalTransfer",
"permitsUniversalTransfer") if p.get(f) is not False]
if p.get("ipRestrict") is not True:
bad.append("no IP restriction")
if p.get("enableSpotAndMarginTrading") is not True:
bad.append("trading off")
if bad:
raise KeyProblem("refuse to start: " + ", ".join(bad))
return "ok"
# Local mock with a synthetic key
ED_KEY, ED_PRIVATE = secrets.token_hex(32), Ed25519PrivateKey.generate()
MOCK = {"ips": {"127.0.0.1"}, "perms": {
"ipRestrict": True, "enableSpotAndMarginTrading": True, "enableWithdrawals": False,
"enableInternalTransfer": False, "permitsUniversalTransfer": False}}
ERRORS = {-2008: "Invalid Api-Key ID.",
-1101: "Duplicate values for a parameter detected.",
-1021: "Timestamp for this request is outside of the recvWindow.",
-2015: "Invalid API-key, IP, or permissions for action.",
-1022: "Signature for this request is not valid."}
class Mock(BaseHTTPRequestHandler):
def do_GET(self):
query = self.path.partition("?")[2]
payload, _, sig = query.rpartition("&signature=")
pairs = parse_qsl(query)
p, now = dict(pairs), time.time() * 1000
ts = int(p.get("timestamp", 0))
key_ok = self.headers.get("X-MBX-APIKEY") == ED_KEY
try:
ED_PRIVATE.public_key().verify(base64.b64decode(unquote(sig)), payload.encode())
signed = True
except (InvalidSignature, ValueError):
signed = False
if not key_ok: # key first, as seen live
code = -2008
elif len(p) < len(pairs): # a dict would silently keep the last value
code = -1101
elif not (ts < now + 1000 and now - ts <= 5000):
code = -1021
elif self.client_address[0] not in MOCK["ips"]:
code = -2015
else:
code = 0 if signed else -1022
body = MOCK["perms"] if code == 0 else {"code": code, "msg": ERRORS[code]}
self.send_response(200 if code == 0 else 401 if code == -2015 else 400)
self.end_headers()
self.wfile.write(json.dumps(body).encode())
def log_message(self, *args):
pass
server = HTTPServer(("127.0.0.1", 0), Mock)
threading.Thread(target=server.serve_forever, daemon=True).start()
BASE, ED = f"http://127.0.0.1:{server.server_port}", ed25519_signer(ED_PRIVATE)
def attempt(label, call):
try:
print(f"{label:18}", call())
except KeyProblem as err:
print(f"{label:18}", err)
doc = ("symbol=LTCBTC&side=BUY&type=LIMIT&timeInForce=GTC&quantity=1&price=0.1"
"&recvWindow=5000×tamp=1499827319559") # Binance's published example
attempt("published HMAC", lambda: hmac_signer(
"NhqPtmdSJYdKjVHjA7PZj4Mge3R5YNiP1e3UZjInClVN65XAbvqqM6A7H5fATj0j")(doc)
== "c8db56825ae71d6d79447849e617115f4a920fa2acdcab2b053c4b2838bd6b71")
attempt("Ed25519 key", lambda: check_key(BASE, ED_KEY, ED))
attempt("empty secret", lambda: check_key(BASE, ED_KEY, hmac_signer("")))
attempt("key with newline", lambda: check_key(BASE, ED_KEY + "\n", ED))
attempt("wrong private key", lambda: check_key(
BASE, ED_KEY, ed25519_signer(Ed25519PrivateKey.generate())))
attempt("unknown key", lambda: check_key(BASE, secrets.token_hex(32), ED))
MOCK["ips"] = {"203.0.113.7"}
attempt("IP not allowed", lambda: check_key(BASE, ED_KEY, ED))
MOCK["ips"], MOCK["perms"]["enableWithdrawals"] = {"127.0.0.1"}, True
attempt("withdrawals on", lambda: check_key(BASE, ED_KEY, ED))
server.shutdown()
server.server_close()
attempt("mock stopped", lambda: check_key(BASE, ED_KEY, ED))
Output:
published HMAC True
Ed25519 key ok
empty secret config: secret missing or has whitespace
key with newline config: API key missing or malformed
wrong private key -1022: Signature for this request is not valid.
unknown key -2008: Invalid Api-Key ID.
IP not allowed -2015: Invalid API-key, IP, or permissions for action.
withdrawals on refuse to start: enableWithdrawals
mock stopped network: ConnectionError, key not tested
A random key drew HTTP 401 and -2015 from /api/v3/account on production and the Spot Testnet on 6 November 2026; that code's single message covers an unknown key, an unlisted IP and a missing permission, so a testnet key there looks like an IP problem (invalid API-key, IP, or permissions). -2008 is on neither the Spot nor the Wallet error list. -1022 means a wrong secret or private key or bytes changed after signing, -1021 clock drift beyond recvWindow (default 5,000 ms, maximum 60,000), -2014 a missing or malformed key header (Binance API error codes). Never retry these in a loop; load keys from environment variables or a secrets manager.
How to rotate a Binance API key, and what to do if it leaks
Rotate by running a new key beside the old one, then deleting the old one; after a possible leak, delete first and investigate afterward.
- Create the new key with the same permissions and IP list, as Ed25519 if the old one is HMAC.
- Deploy it behind a setting and run the key check with it.
- Switch, watch for
-2015,-2008,-1022and-1021, then delete the old key and purge its secret from stores and backups.
Binance's pages read on 6 November 2026 set no rotation interval. On unusual activity, the Spot REST page says to revoke all keys immediately and contact Binance support; after any leak, also review recent orders, transfers and withdrawals. An IP-restricted key without withdrawal rights limits the damage, but whoever controls the allowed server can still trade with it. Storage: API key security for trading bots.
For integration work in general, see Fincode's exchange API integration page; naming Binance here does not mean Fincode integrates it or has built on it. Fincode's security page describes how its software handles client keys.
Frequently asked questions
Can I see my Binance API secret key again?
No. Binance's API FAQ says secret keys are visible only at creation, so create a new key and delete the old one.
Does a Binance API key expire?
No expiry rule was found on 6 November 2026. The 2021 rule removing trading permission after 90 days from keys without whitelisted IPs stopped applying on 24 October 2023.
Is Binance removing HMAC API keys?
Binance calls them deprecated and recommends Ed25519, but as of 6 November 2026 neither its key types page nor its Spot changelog gives a removal date.
Does a Spot Testnet API key work on the live exchange?
No. The testnet is a separate system with its own keys. On 6 November 2026 production rejected a random key with -2015 on /api/v3/account and -2008 on the /sapi permission endpoint.
Sources
- Binance Support: How to Create API Keys on Binance (published 9 April 2018, last updated 20 March 2025) (opens in a new tab)
- Binance Support: Frequently Asked Questions on API (last updated 13 January 2025) (opens in a new tab)
- Binance announcement: Updates to API Key Permission Rules (26 July 2021; rules no longer applicable as of 24 October 2023) (opens in a new tab)
- Binance Spot API documentation: API Key Types (last modified 30 October 2026) (opens in a new tab)
- GitHub: history of the API Key Types page (first published 18 July 2023) (opens in a new tab)
- Binance Spot API documentation: REST API, request security and timing security (last modified 30 October 2026) (opens in a new tab)
- Binance Spot API documentation: Changelog (last updated 30 October 2026) (opens in a new tab)
- Binance Spot API documentation: Error codes (opens in a new tab)
- Binance Wallet API: Get API Key Permission (GET /sapi/v1/account/apiRestrictions) (opens in a new tab)
- Binance Wallet API: Change log (last modified 30 October 2026) (opens in a new tab)
- Binance Wallet API: Error codes (last modified 30 October 2026; no -2008 listed) (opens in a new tab)
- Binance USD-M Futures: General Info (last modified 30 October 2026) (opens in a new tab)
- Binance USD-M Futures: WebSocket API General Info (last modified 30 October 2026) (opens in a new tab)
- Binance Spot Test Network (opens in a new tab)
- Capital Markets Law No. 6362, Article 99/A (Turkish, consolidated text) (opens in a new tab)
This guide is general information about software and markets, not investment, legal or tax advice. Automated trading can lose money, past or backtested performance does not guarantee future results, and the rules that apply depend on your jurisdiction and your exchange's terms. Sources, checks and corrections: see the editorial policy.