General Info
Spot API Overview¶
- This document lists the base URL for the API endpoints: https://sapi.asterdex-testnet.com
- All API responses are in JSON format.
Attention¶
- TESTUSDT or any other symbols starting with TEST are symbols used for Aster’s INTERNAL TESTING ONLY. Please DO NOT trade on these symbols starting with TEST. Aster does not hold any accountability for loss of funds due to trading on these symbols. However, if you run into issues, you may contact support about this any time, we will try to help you recover your funds.
HTTP return codes¶
- HTTP
4XXstatus codes are used to indicate errors in the request content, behavior, or format. The problem lies with the requester. - HTTP
403status code indicates a violation of WAF restrictions (Web Application Firewall). - HTTP
429error code indicates a warning that the access frequency limit has been exceeded and the IP is about to be blocked. - HTTP
418indicates that after receiving a 429 you continued to access, so the IP has been blocked. - HTTP
5XXerror codes are used to indicate issues on the Aster service side.
API error codes¶
- When using the endpoint
/api/v3, any endpoint may throw exceptions;
The API error codes are returned in the following format:
{
"code": -1121,
"msg": "Invalid symbol."
}
Basic information about the endpoint¶
- Endpoints with the
GETmethod must send parameters in thequery string. - For
POST,PUT, andDELETEendpoints, parameters can be sent in thequery stringwith content typeapplication/x-www-form-urlencoded, or in therequest body. - The order of parameters is not required.
Access restrictions¶
Basic information on access restrictions¶
- The
rateLimitsarray in/api/v3/exchangeInfocontains objects related to REQUEST_WEIGHT and ORDERS rate limits for trading. These are further defined in theenum definitionssection underrateLimitType. - A 429 will be returned when any of the rate limits are violated.
IP access limits¶
- Each request will include a header named
X-MBX-USED-WEIGHT-(intervalNum)(intervalLetter)that contains the used weight of all requests from the current IP. - Each endpoint has a corresponding weight, and some endpoints may have different weights depending on their parameters. The more resources an endpoint consumes, the higher its weight will be.
- Upon receiving a 429, you are responsible for stopping requests and must not abuse the API.
- If you continue to violate access limits after receiving a 429, your IP will be banned and you will receive a 418 error code.
- Repeated violations of the limits will result in progressively longer bans, from a minimum of 2 minutes up to a maximum of 3 days.
- The
Retry-Afterheader will be sent with responses bearing 418 or 429, and will give the wait time in seconds (if 429) to avoid the ban, or, if 418, until the ban ends. - Access restrictions are based on IP, not API Key
You are advised to use WebSocket messages to obtain the corresponding data as much as possible to reduce the load and rate-limit pressure from requests.
Order rate limits¶
- Each successful order response will include a
X-MBX-ORDER-COUNT-(intervalNum)(intervalLetter)header containing the number of order limit units currently used by the account. - When the number of orders exceeds the limit, you will receive a response with status 429 but without the
Retry-Afterheader. Please check the order rate limits inGET api/v3/exchangeInfo(rateLimitType \= ORDERS) and wait until the ban period ends. - Rejected or unsuccessful orders are not guaranteed to include the above header in the response.
- Order placement rate limits are counted per account.
WebSocket connection limits¶
- The WebSocket server accepts a maximum of 5 messages per second. Messages include:
- PING frame
- PONG frame
- Messages in JSON format, such as subscribe and unsubscribe.
- If a user sends messages that exceed the limit, the connection will be terminated. IPs that are repeatedly disconnected may be blocked by the server.
- A single connection can subscribe to up to 1024 Streams.
API authentication types¶
- Each API has its own authentication type, which determines what kind of authentication is required when accessing it.
- If authentication is required, a signer should be included in the request body.
| Security Type | Description |
|---|---|
| NONE | API that does not require authentication |
| TRADE | A valid signer and signature are required |
| USER_DATA | A valid signer and signature are required |
| USER_STREAM | A valid signer and signature are required |
| MARKET_DATA | A valid signer and signature are required |
SIGNED (TRADE AND USER_DATA) Endpoint security¶
Authentication signature payload¶
| Parameter | Description |
|---|---|
| user | Main account wallet address |
| signer | API wallet address |
| nonce | Current timestamp, in microseconds |
| signature | Signature |
Endpoints requiring signature¶
- Security Type: SPOT_TRADE, USER_DATA, USER_STREAM
Example of POST /api/v3/order¶
All parameters are passed through the request body (Python 3.9.6)¶
The following parameters are API registration details. The values for user, signer, and privateKey are for demonstration purposes only (the privateKey corresponds to the signer).¶
| Key | Value | Desc |
|---|---|---|
| user | 0x63DD5aCC6b1aa0f563956C0e534DD30B6dcF7C4e | Login wallet address |
| signer | 0x21cF8Ae13Bb72632562c6Fff438652Ba1a151bb0 | Click Here |
| privateKey | 0x4fd0a42218f3eae43a6ce26d22544e986139a01e5b34a62db53757ffca81bae1 | Click Here |
The nonce parameter is the current system time in microseconds. If it exceeds the system time or lags behind it by more than 5 seconds, the request is considered invalid.¶
#python
nonce = math.trunc(time.time()*1000000)
print(nonce)
#1748310859508867
//java
Instant now = Instant.now();
long microsecond = now.getEpochSecond() * 1000000 + now.getNano() / 1000;
Example: The following parameters are business request parameters.¶
import time
import urllib
import threading
import requests
from eth_account.messages import encode_structured_data
from eth_account import Account
typed_data = {
"types": {
"EIP712Domain": [
{"name": "name", "type": "string"},
{"name": "version", "type": "string"},
{"name": "chainId", "type": "uint256"},
{"name": "verifyingContract", "type": "address"}
],
"Message": [
{ "name": "msg", "type": "string" }
]
},
"primaryType": "Message",
"domain": {
"name": "AsterSignTransaction",
"version": "1",
"chainId": 714,
"verifyingContract": "0x0000000000000000000000000000000000000000"
},
"message": {
"msg": "$msg"
}
}
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
'User-Agent': 'PythonApp/1.0'
}
host = 'https://sapi.asterdex-testnet.com'
# config your user and agent info here
user = '*'
signer = '*'
private_key = '*'
place_order = {"url":"/api/v3/order","method":"POST","params":{"symbol": "ASTERUSDT", "type": "LIMIT", "side": "BUY",
"timeInForce": "GTC", "quantity": "20", "price": "0.5"}}
batch_orders = {"url":"/api/v3/batchOrders","method":"POST","params":{
"batchOrders":"[{'symbol':'ASTERUSDT','type':'LIMIT','side':'BUY','timeInForce':'GTC','quantity':'20','price':'0.5'},{'symbol':'ASTERUSDT','type':'LIMIT','side':'BUY','timeInForce':'GTC','quantity':'20','price':'0.5'}]" }}
listen_key = {"url":"/api/v3/listenKey","method":"POST","params":{}}
_last_ms = 0
_i = 0
_nonce_lock = threading.Lock()
def get_nonce():
global _last_ms, _i
with _nonce_lock:
now_ms = int(time.time())
if now_ms == _last_ms:
_i += 1
else:
_last_ms = now_ms
_i = 0
return now_ms * 1_000_000 + _i
def send_by_url(api) :
my_dict = api['params']
url = host + api['url']
my_dict['nonce'] = str(get_nonce())
my_dict['user'] = user
my_dict['signer'] = signer
param = urllib.parse.urlencode(my_dict)
print(param)
typed_data['message']['msg'] = param
message = encode_structured_data(typed_data)
signed = Account.sign_message(message, private_key=private_key)
url = url + '?' + param + '&signature=' + signed.signature.hex()
print(url)
res = requests.post(url, headers=headers)
print(res.text)
def send_by_body(api) :
my_dict = api['params']
url = host +api['url']
my_dict['nonce'] = str(get_nonce())
my_dict['user'] = user
my_dict['signer'] = signer
param = urllib.parse.urlencode(my_dict)
typed_data['message']['msg'] = param
message = encode_structured_data(typed_data)
signed = Account.sign_message(message, private_key=private_key)
print(signed.signature.hex())
my_dict['signature'] = signed.signature.hex()
print(my_dict)
res = requests.post(url, data=my_dict, headers=headers)
print(res.text)
if __name__ == '__main__':
send_by_url(place_order)
# send_by_url(listen_key)
# send_by_url(batch_orders)
# send_by_body(place_order)
# send_by_body(batch_orders)
Public Endpoints Info¶
Terminology¶
base assetrefers to the asset that is thequantityof a symbol.quote assetrefers
Public API parameters¶
Terminology¶
The terminology in this section applies throughout the document. New users are encouraged to read it carefully for better understanding.
base assetrefers to the asset being traded in a trading pair, i.e., the asset name written first; for example, inBTCUSDT,BTCis thebase asset.quote assetrefers to the pricing asset of a trading pair, i.e., the asset name written at the latter part; for example, inBTCUSDT,USDTis thequote asset.
Enumeration definition¶
Trading pair status (status):
- TRADING - after trade
Trading pair type:
- SPOT - spot
Order status (status):
| Status | Description |
|---|---|
| NEW | Order accepted by the matching engine |
| PARTIALLY_FILLED | Part of the order was filled |
| FILLED | The order was fully filled |
| CANCELED | The user canceled the order |
| REJECTED | The order was not accepted by the matching engine and was not processed |
| EXPIRED | Order canceled by the trading engine, for example: Limit FOK order not filled, Market order not fully filled, orders canceled during exchange maintenance |
Order types (orderTypes, type):
- LIMIT - Limit Order
- MARKET - Market Order
- STOP - Limit Stop Order
- TAKE_PROFIT - Limit Take-Profit Order
- STOP_MARKET - Market Stop Order
- TAKE_PROFIT_MARKET - Market Take-Profit Order
Order response type (newOrderRespType):
- ACK
- RESULT
- FULL
Order direction (direction side):
- BUY - Buy
- SELL - Sell
Valid types (timeInForce):
This defines how long an order can remain valid before expiring.
| Status | Description |
|---|---|
| GTC (Good ‘Til Canceled) | The order remains active until it is fully executed or manually canceled. |
| IOC (Immediate or Cancel) | The order will execute immediately for any amount available. Any unfilled portion is automatically canceled. |
| FOK (Fill or Kill) | The order must be fully executed immediately. If it cannot be filled in full, it is canceled right away. |
| GTX (Good till crossing, Post only) | The post-only limit order will only be placed if it can be added as a maker order and not as a taker order. |
K-line interval:
m (minutes), h (hours), d (days), w (weeks), M (months)
- 1m
- 3m
- 5m
- 15m
- 30m
- 1h
- 2h
- 4h
- 6h
- 8h
- 12h
- 1d
- 3d
- 1w
- 1M
Rate limit type (rateLimitType)
REQUEST_WEIGHT
{
"rateLimitType": "REQUEST_WEIGHT",
"interval": "MINUTE",
"intervalNum": 1,
"limit": 1200
}
ORDERS
{
"rateLimitType": "ORDERS",
"interval": "MINUTE",
"intervalNum": 1,
"limit": 100
}
-
REQUEST_WEIGHT - The maximum sum of request weights allowed within a unit time
-
ORDERS - Order placement frequency limit per time unit
Interval restriction (interval)
- MINUTE - Minute
Filters¶
Filters, i.e. Filter, define a set of trading rules. There are two types: filters for trading pairs symbol filters, and filters for the entire exchange exchange filters (not supported yet)
Trading pair filters¶
PRICE_FILTER Price filter¶
Format in the /exchangeInfo response:
{
"minPrice": "556.72",
"maxPrice": "4529764",
"filterType": "PRICE_FILTER",
"tickSize": "0.01"
}
The Price Filter checks the validity of the price parameter in an order. It consists of the following three parts:
minPricedefines the minimum allowed value forprice/stopPrice.maxPricedefines the maximum allowed value forprice/stopPrice.tickSizedefines the step interval forprice/stopPrice, meaning the price must equal minPrice plus an integer multiple of tickSize.
Each of the above items can be 0; when 0 it means that item is not constrained.
The logical pseudocode is as follows:
price>=minPriceprice\<=maxPrice- (
price-minPrice) %tickSize\== 0
PERCENT_PRICE price amplitude filter¶
Format in the /exchangeInfo response:
{
"multiplierDown": "0.9500",
"multiplierUp": "1.0500",
"multiplierDecimal": "4",
"filterType": "PERCENT_PRICE"
}
The PERCENT_PRICE filter defines the valid range of prices based on the index price.
For the "price percentage" to apply, the "price" must meet the following conditions:
price\<=indexPrice*multiplierUpprice> \=indexPrice*multiplierDown
LOT_SIZE order size¶
Format in the /exchangeInfo response:
{
"stepSize": "0.00100000",
"filterType": "LOT_SIZE",
"maxQty": "100000.00000000",
"minQty": "0.00100000"
}
Lots is an auction term. The LOT_SIZE filter validates the quantity (i.e., the amount) parameter in orders. It consists of three parts:
minQtyindicates the minimum allowed value forquantity.maxQtydenotes the maximum allowed value forquantity.stepSizedenotes the allowed step increment forquantity.
The logical pseudocode is as follows:
quantity>=minQtyquantity\<=maxQty- (
quantity-minQty) %stepSize\== 0
MARKET_LOT_SIZE - Market order size¶
*/exchangeInfo response format:
{
"stepSize": "0.00100000",
"filterType": "MARKET_LOT_SIZE"
"maxQty": "100000.00000000",
"minQty": "0.00100000"
}
The MARKET_LOT_SIZE filter defines the quantity (i.e., the "lots" in an auction) rules for MARKET orders on a trading pair. There are three parts:
minQtydefines the minimum allowedquantity.maxQtydefines the maximum allowed quantity.stepSizedefines the increments by which the quantity can be increased or decreased.
In order to comply with the market lot size, the quantity must satisfy the following conditions:
quantity>=minQtyquantity\<=maxQty- (
quantity-minQty) %stepSize\== 0