Binance Web3 API Specification V0.0.6 (2024-01-09)
Web3 API description
- Binance's Partners need to follow the API specification in this doc to provide Binance with a set of endpoints in order to integrate with Binance.
General API Information
- All endpoints return a common JSON object with "code", "message" and "data", no matter it's a success or not.
- Here "data" is either a customized JSON object or a simple type (int, string...). it may vary from endpoint to endpoint.
{
"code": "XXXXXX",
"message": "success",
"data": {
"abc": "efg",
...
}
}
-
General codes in responses:
code | Description ---- | ---- 000000 |success 000001 | too many requests 000002 | system busy 000003 | invalid signature 000004 | invalid recvWindow 000005 | invalid timestamp. timestamp for this request is outside of the recvWindow. Or timestamp for this request was 1000ms ahead of the server's time. 000006 | invalid argument
-
All time and timestamp related fields are in millisecond.
-
HTTP 5XX return codes are used for internal errors. The issue is on partner's side. Binance will NOT treat this as a failure operation; the execution status is UNKNOWN.
-
For GET endpoints, parameters must be sent as a query string.
-
For POST, PUT , and DELETE endpoints, the parameters must be sent in the request body with content type application/x-www-form-urlencoded.
-
Parameters may be sent in any order.
-
Requests out of receiving window must NOT be executed. Partner's server should verify:
- recvWindow <= 10000 (ms)
- partner's server time - 3000 < timestamp < partner's server time + recvWindow.
-
Every partner need to define a url prefix so that Binance can invoke the partner's endpoint using prefix + endpoint_url.
-
Each request and response should be logged in both Binance and partner's sides for further investigation.
Signature
- In order to ensure the data security, all SIGNED endpoints should be signed by Binance and be verified by partners. We would use RSA encrption/signature to do the signature.
- Binance will generate a private public key pair for each partner and send each partner their public key via email.
- When Binance invokes a partner's SIGNED endpoint, Binance will send "timestamp", "recvWindow" in the url (GET) or request body (POST, PUT and DELETE) and "signature" in header (GET, POST, PUT and DELETE).
- Partners should verify the signature of all paratemeters in the url , including timestamp and recvWindow, is correct by the public key. Example:
// privateKey & publicKey here are just examples. Will use different key pair in the Testing & Production environment.
String privateKey = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAJtaheRtvB9xmctbjdWyHVgy7Vblrit4ge9" +
"wxioWNYmXiG4Dskpl0L87emYSrF1IZk+R6sOg3vTJ8VQ8nAocX5lzfQJZIdOA36K9HVbgTDJB3jxvtZ91To1U27anSn0HZ29zK2x0hn4" +
"UqMPCNemXDbX//3NfvEuvasX5h4/WanbRAgMBAAECgYBhrrGxyC4Zt1x0ucSdMbmx05PYp+K0ArnwzIBNxlkzgsyOIFTi4tI27DcyJ1up6/"+
"Qo5B8xkt2eHbxYsyOKV/zjjNo7afmQ/woBPgCxuErNJsdo2g0nH0k8A4Pw0FcLQL4sQocyfYsFMNhP56SY5fkgRAdAYPJ5v5RG47dLVoMGY" +
"QJBANF69BOAa/V+wubh5d5+l04zDkt/xMq7AoeHbeABpEOAEVwEfYqrH2H/BreUod8LixC6CR1KZZ9s+nnSGd9kz+sCQQC92nGk32kU09Oc" +
"XtQzRn1Fi2AHvsSShQ8rwf40Buxl0IZK6sQkkSb2Eg1bA+E5KfAbzfX2YziAH/KcsdaxZ2EzAkEAwlK3tpuMCplDviBSOBrgyzcLjLgC2zm" +
"t+AGGyKVdNwzHjb/QoeFqZGLKXWRw4NL5d1PMfrJ0IPdcR8PCInyHbwJAT2CqzT1fiQa73hBD9qBNNit83iAjvgMGAcydRRFz+2nBDEe19Hf" +
"/6zhG/zvTCfx/2JA3e2mmsOMqo9szIX9QwwJAVfTewPB76mTwrTDbvBXAAXRU1WKpmrDiKHCViRO8Z6iP/KwwQxqpGiZTXr6zN8onidVjRzW" +
"JHGcWq3cCGO0v9w==";
String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCbWoXkbbwfcZnLW43Vsh1YMu1W5a4reIHvcMYqFjWJl4huA7JK" +
"ZdC/O3pmEqxdSGZPkerDoN70yfFUPJwKHF+Zc30CWSHTgN+ivR1W4EwyQd48b7WfdU6NVNu2p0p9B2dvcytsdIZ+FKjDwjXplw21//9zX7x" +
"Lr2rF+YeP1mp20QIDAQAB";
/** Binance Side (Sign)*/
// sign
String parameters = "a=b&c=[\"1\",\"2\",\"3\"]&recvWindow=5000×tamp=1499827319559";
String signature = RSAUtil.sign(parameters, RSAUtil.getPrivateKey(privateKey));
log.debug("signature: {}", signature)
// LOG [debug] signature: VI6k2ILEFuB2ltAIYHrEeFjlxq4ZMHdoPTMLxFyHrg1ylnMpFJo2J/YStRKRdEh0Pv+beVWje0Nz+rZ6z3RzPFFwFkgEGK4XT3PGnpYnZXWvvCBHhQg0OmypNftzktUxcekbazWvF4BSTxoFlIDYBdAt5L69lUnwY7GZ9pOXGoU=
// put signature in the header
/** Partener Side (Verification)*/
// get parameter string from requestBody (POST, PUT, and DELETE) or parameters (GET):
String parameters = "a=b&c=[\"1\",\"2\",\"3\"]&recvWindow=5000×tamp=1499827319559";
// get from header "signature"
String signature = "VI6k2ILEFuB2ltAIYHrEeFjlxq4ZMHdoPTMLxFyHrg1ylnMpFJo2J/YStRKRdEh0Pv+beVWje" +
"0Nz+rZ6z3RzPFFwFkgEGK4XT3PGnpYnZXWvvCBHhQg0OmypNftzktUxcekbazWvF4BSTxoFlIDYBdAt5L69lUnwY7GZ9pOXGoU=";
// verify
boolean isSignatureValid = RSAUtil.verify(parameters, RSAUtil.getPublicKey(publicKey), signature);
RSAUtil
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.Signature;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.X509EncodedKeySpec;
public class RSAUtil {
public static boolean verify(String parameterStr, PublicKey publicKey, String sign) throws Exception {
byte[] keyBytes = publicKey.getEncoded();
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey key = keyFactory.generatePublic(keySpec);
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initVerify(key);
signature.update(parameterStr.getBytes());
return signature.verify(Base64.decodeBase64(sign.getBytes()));
}
public static PublicKey getPublicKey(String publicKey) throws Exception {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
byte[] decodedKey = Base64.decodeBase64(publicKey.getBytes());
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(decodedKey);
return keyFactory.generatePublic(keySpec);
}
public static PrivateKey getPrivateKey(String privateKey) throws Exception {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
byte[] decodedKey = Base64.decodeBase64(privateKey.getBytes());
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decodedKey);
return keyFactory.generatePrivate(keySpec);
}
}
API Specification
Server Time (UNSIGNED)
GET /v1/time
to check the partner's server time
Parameters: None
Response Body:
Name | Type | Mandatory | Description |
---|---|---|---|
code | String | YES | "000000" |
message | String | YES | "success" |
data | Long | YES | timestamp |
Response examples:
{
"code": "000000",
"message": "success",
"data": 1499827319559
}
{
"code": "000002",
"message": "system busy",
"data": null
}
Task Completion (SIGNED)
GET /v1/task/completion
Parameters:
Name | Type | Mandatory | Description |
---|---|---|---|
walletAddress | String | YES | a user's unifed id |
task | String json array | YES | task names. "deposit", "withdraw". if it's not empty, just return specific task completion status. otherwise, return all tasks completion statuses. |
recvWindow | Long | YES | received window |
timestamp | Long | YES | client timestamp of the request |
Headers:
Name | Type | Mandatory | Description |
---|---|---|---|
signature | String | YES | signature of the request |
Response Body:
Name | Type | Mandatory | Description |
---|---|---|---|
code | String | YES | |
message | String | YES | |
data | JSON Object | YES | timestamp |
data:
Name | Type | Mandatory | Description |
---|---|---|---|
walletAddress | String | YES | a user's unifed id |
task | String json array | YES | task names. "deposit", "withdrawal" |
recvWindow | Long | YES | received window |
timestamp | Long | YES | client timestamp of the request |
examples:
GET {prefix} + /v1/task/completion?walletAddress=0xabcdefg&task=["deposit", "withdrawal"]&recvWindow=3000×tamp=1499827319559
Response:
{
"code": "000000",
"message": "success",
"data": {
"withdrawal": true, // complete
"deposit": false, // incomplete
}
}
{
"code": "000003",
"message": "invalid signature",
"data": null
}
{
"code": "000006",
"message": "invalid argument",
"data": null
}