Introduction
base = 'https://uat.sheeldmatch.com/api'
This document describes the REST API offered by Aplo.
The REST API allows market participants to access Aplo’s Prime Brokerage platform. All of our services and features are API-first: trading, data, custody, clearing, virtual accounts and back-office.
Market participants must test their connection using the User Acceptance Testing server before going to production.
Production server: https://sheeldmatch.com/api
UAT server: https://uat.sheeldmatch.com/api
Market participants must retrieve credentials from Aplo. Everyday, they also have to retrieve the instruments and venues information using the dedicated routes.
The payload of the requests will be a JSON object (see standard JSON specification) sent in the body of the request. Its structure will be described in this documentation, using the following data types:
- json A JSON object.
- array A JSON array with zero to multiple elements.
- string A JSON string defined as a sequence of characters gathered in a quoted string (see standard JSON rules).
- number A sequence of digits followed by an optional decimal point and additional digits. This sequence is sent as a JSON string.
- integer A sequence of digits representing a JSON number.
- time The number of milliseconds since UNIX epoch UTC i.e. 1st Jan 1970. This value is sent as a JSON number.
Authentication
The REST API handles authentication through API keys.
Generate an API key
API keys can be created on a user basis. They are restricted to a role, a set of accounts and a set of IP addresses. A dedicated tab is available in the app by clicking on the user icon in the top right corner.
Authenticate each Request
Pass the X-API-Key header with each request:
import requests
api_key = 'bxaxSu64vYmkvTUl8IQd8gw7Wl0hfNGt'
headers = {'X-API-Key': api_key}
response = requests.get(endpoint, headers=headers)
Once done, market participants must design all trading API calls following the X-API-Key header authentication scheme. This scheme consists in a HTTP request with the X-API-Key header that contains your API key.
For example, the client has to include the following header in his requests:
X-API-Key: bxaxSu64vYmkvTUl8IQd8gw7Wl0hfNGt
Pagination
Some endpoints use pagination via these query parameters:
| Parameter | Allowed values | Default value |
|---|---|---|
| page | Any positive number | 0 |
| pageSize | 5, 10, 25 or 50 | 10 |
Endpoints using pagination will always return a JSON object with the following schema:
{
"data": [
/* data object */
],
"count": 34
}
With count being the total number of rows available for this endpoint.
Universe
Get Available Instruments
import requests
endpoint = '%s/instruments' % base
response = requests.get(endpoint, headers=headers)
This route allows market participants to retrieve all instruments available for trading, alongside their name, ID and microstructures (trading rules).
To get the restricted list of instruments that your account has access to, see the assetUniverse property in the GET /user route accounts object.
You can trade an instrument if you have access to both the assetId and currencyId.
HTTP Request
GET /instruments
This endpoint doesn't require any parameters.
Response fields
Example of JSON structure returned by the request:
[
{
"date": "2010-01-01",
"name": "BTC-USDT.SPOT",
"iid": 1,
"assetId": "BTC",
"currencyId": "USDT",
"lot": 2,
"tick": 4,
"availableForTrade": true,
"availableForOTC": true,
"asset": {
"minWithdrawalAmount": "0.001",
"rate": "67000.3",
"id": "BTC",
"name": "BTC",
"description": "Bitcoin",
"networkId": "e2f100f8-c549-5e44-a263-2e22dc21676d",
"underlyingId": null,
"currencyId": null,
"issuingVenue": null,
"marginId": null,
"expiration": null,
"strikePrice": null,
"durationCode": null
}
}
]
The response is a JSON array of instruments with the following fields:
| Field | Type | Description |
|---|---|---|
| date | string | Date of the current trading day |
| name | string | Aplo Instrument name |
| iid | integer | Aplo Instrument ID |
| assetId | string | Asset e.g. BTC, used for quantity |
| currencyId | string | Currency e.g. USDT, used for price |
| lot | integer | Maximum number of digits for the volume |
| tick | integer | Maximum number of digits for the price |
| availableForTrade | boolean | Is the instrument tradable from the Trade endpoint ? |
| availableForOTC | boolean | Is the instrument tradable from the OTC endpoint ? |
| asset | json | General information about the asset |
Get Available Venues
import requests
endpoint = '%s/venues' % base
response = requests.get(endpoint, headers=headers)
This route allows market participants to retrieve the venues available for trading.
HTTP Request
GET /venues
This request doesn't require any parameters.
Response fields
Example of JSON structure returned by the request:
[
{
"code": "1",
"date": "2010-01-01",
"vid": 1,
"name": "SheeldMatch"
},
{
"code": "B",
"date": "2010-01-01",
"vid": 2,
"name": "Binance"
}
]
The response is a JSON array of venues with the following fields :
| Field | Type | Description |
|---|---|---|
| code | string | Aplo Venue code |
| date | string | Date of the current trading day |
| vid | integer | Aplo Venue ID |
| name | string | Venue name e.g. Binance |
Get Available Assets
import requests
endpoint = '%s/assets' % base
response = requests.get(endpoint, headers=headers)
This route allows market participants to retrieve ALL assets available in the Aplo universe.
To get the restricted list of assets that your account has access to, see the assetUniverse property in the GET /user route accounts object.
Assets have an automatically generated name based on their characteristics and the following schema:
{asset}-{currency}.{classification}-{margin}-{venue}-{durationCode}{expiration.format("YYMMDD")}-{strikePrice}
HTTP Request
GET /assets
This request doesn't require any parameters.
Response fields
Example of JSON structure returned by the request:
[
{
"id": "USD",
"name": "USD",
"description": "US Dollar",
"minWithdrawalAmount": "100",
"rate": "1",
"classification": "S",
"logo": "usd.svg",
"availableProgramTrading": true,
"underlyingId": null,
"currencyId": null,
"issuingVenue": null,
"marginId": null,
"expiration": null,
"strikePrice": null,
"durationCode": null
},
{
"id": "wBTC_n",
"name": "BTC-USDT.PERP-USDT-Binance2",
"description": "Bitcoin Perpetual @ Binance2",
"minWithdrawalAmount": "100000000",
"rate": "36732.5",
"classification": "W",
"logo": "btc.svg",
"availableProgramTrading": false,
"underlyingId": "BTC",
"currencyId": "USDT",
"marginId": "USDT",
"issuingVenue": "Binance2",
"expiration": null,
"durationCode": null,
"strikePrice": null
}
]
The response is a JSON array of venues with the following fields :
| Field | Type | Description |
|---|---|---|
| id | string | Asset Id |
| name | string | Automatically computed asset name |
| description | string | Human readable name |
| minWithdrawalAmount | string | Minimum amount for a withdrawal request |
| rate | string | Current exchange rate (in USD) |
| classification | string | Internal classification for assets, can be "S" (Spot), "F" (Future), "T" (Test) or "W" (Perpetual) |
| logo | string | Internal logo name |
| availableProgramTrading | boolean | Whether this asset can be used for the program trading product |
| underlyingId | string | The underlying asset, null for Spot assets. |
| currencyId | string | The currency asset, null for Spot assets. Used to quote the contract |
| issuingVenue | string | The issuing Venue name (see the /venues route), null for Spot assets. |
| marginId | string | The margin asset, null for Spot assets. Used as collateral |
| expiration | date | The expiration date, null for Spot and Perpetual assets. |
| durationCode | string | The expiration duration code "W" (Week), "M" (Month), "Q" (Quarter) or "Y" (Year). Is null for Spot and Perpetual assets |
| strikePrice | string | The strike price for options (Call or Put) assets, null for Spot, Perpetual or Future assets |
Get Available Networks
import requests
endpoint = '%s/networks' % base
response = requests.get(endpoint, headers=headers)
This route allows market participants to retrieve the networks used in the Aplo universe.
HTTP Request
GET /networks
This request doesn't require any parameters.
Response fields
Example of JSON structure returned by the request:
[
{
"id": "FIAT",
"name": "Fiat",
"explorerUrl": null,
"transferDuration": 0,
"hasMemo": false,
"assets": [
{
"minWithdrawalAmount": "100",
"rate": "1",
"strikePrice": null,
"id": "USD",
"name": "USD",
"description": "US Dollar",
"classification": "S",
"logo": "usd.svg",
"availableProgramTrading": true,
"underlyingId": null,
"currencyId": null,
"issuingVenue": null,
"marginId": null,
"expiration": null,
"durationCode": null,
"withdrawalNetworkFees": "50"
},
{
"minWithdrawalAmount": "50",
"rate": "1.09037",
"strikePrice": null,
"id": "EUR",
"name": "EUR",
"description": "Euro",
"classification": "S",
"logo": "eur.svg",
"availableProgramTrading": true,
"underlyingId": null,
"currencyId": null,
"issuingVenue": null,
"marginId": null,
"expiration": null,
"durationCode": null,
"withdrawalNetworkFees": "50"
}
]
}
]
The response is a JSON array of venues with the following fields :
| Field | Type | Description |
|---|---|---|
| id | string | Network Id |
| name | string | Human readable name |
| explorerUrl | string | Chain explorer URL (can be null) |
| transferDuration | number | Transfer duration of the network in seconds |
| hasMemo | boolean | Whether the network has the destination memo or tag feature |
| assets | array | List of assets available on this network (see the /assets route) |
Trading
Usage guidelines
Aplo offers a low-latency and optimized trading environment. However, connections to external venues can sometimes increase the native latency and requests may timeout (especially posting Immediate-or-Cancel Routed orders).
For this reason, POST/PATCH/DELETE routes documented below can respond with the following:
| case | status | message |
|---|---|---|
| success | 200 | Success |
| timeout | 202 | Request processing |
| error | 400 | See below for reason |
Where the reason can be one of the following:
Temporarily UnavailablePending locates requestTime-In-Force ExpiryInsufficient BalanceLimit breachBad Market Participant IDBad Instrument IDBad Token or Dead OrderBad PriceRace ConditionBad SideBad Time or Time-In-ForceBad VolumeGeneral failureBad VenueMarket not open
Virtual Account Trading
Users can pass orders for a specific virtual acccount using its unique Id, all routes described below may take an additional optional parameter:
| Parameter | Type | Description |
|---|---|---|
| virtualAccountId | number | Specific virtual account passing the order |
Token generation
import time
token = round(time.time() * 1000)
Market participants must provide unique tokens within a trading day to identify their orders.
Using unique tokens based on the current time in milliseconds is strongly recommended.
Order schema
An Order object is a JSON Object with the following schema:
{
"mpid": "SHLD",
"qid": "8912784723",
"token": "123456789",
"instrument": "BTC-USDT.SPOT",
"venue": "SheeldMatch",
"price": "11052.59",
"volume": "15",
"time": "2020-12-14T11:07:18.670Z",
"closingTime": null,
"initialDay": "2020-12-14",
"day": "2020-12-14",
"type": "Routed",
"subType": "TWAP",
"status": "received",
"executedVolume": "0",
"side": "B",
"averagePrice": "0",
"executionCount": 0,
"assetId": "BTC",
"currencyId": "USDT",
"lot": 4,
"tick": 2,
"classification": "S",
"requestedVolume": "20",
"requestedPrice": "11055",
"total": "221100",
"virtualAccountId": null,
"parameters": {
"lifespan": "60"
}
}
| Field | Type | Description |
|---|---|---|
| mpid | string | 4-character Market participant ID as obtained from Aplo |
| qid | integer | A unique Quote ID provided by Aplo to identify the order |
| token | string | A token provided by the participant to uniquely identify the order |
| instrument | string | The Instrument Name |
| venue | string | The Venue Name |
| price | string | Quote price |
| volume | string | Quote size |
| time | string | ISO string date of order |
| closingTime | string | ISO string date of when the order was closed or null to indicate an order not closed yet |
| initialDay | string | ISO 8601 date of order opening |
| day | string | ISO 8601 date of last active session |
| type | string | deprecated Type of the order, can be "Auction", "Conditional", "Direct", "Lotted", "Pegged", "Routed" or "Scheduled" |
| subType | string | |
"GTC", "IOC", "DAY", "POD", "FOK", "VIO", "TWAP", "VWAP", "IGO", "IDO", "ISO", "IPO", "MGO", "MDO", "MSO", "MPO", "SLO", "TPO", "VTO", "PTO", "LMT", "MKT" or "DCA" |
||
| status | string | Status of the order, can be "received", "open" or "closed" |
| executedVolume | string | The quote's executed volume |
| side | string | "B" (buy) or "S" (sell) |
| averagePrice | string | The quote's average price of execution |
| executionCount | number | The quote's number of execution |
| assetId | string | Asset name as retrieved using the instruments route |
| currencyId | string | Currency name as retrieved using the instruments route |
| classification | string | Can be "S" (spot), "F" (future), "T" (test) or "W" (perpetual swap) |
| requestedVolume | string | Initially requested volume when the order was created |
| requestedPrice | string | Initially requested price when the order was created |
| total | string | Total volume of currency traded for this Order |
| virtualAccountId | number | Virtual account id (null indicates the "Main Account") |
| parameters | Object | Optional parameters depending on the order subType (described below), can be empty. |
parameters is an object with the following properties (based on subType)
| Field | Type | Description |
|---|---|---|
| stop | string | Stop price of volume |
| lifespan | number | Lifespan for "TWAP" or "VWAP" orders, in minutes |
| priority | string | "price", "time" or "both", used for SOR (Smart Order Routing) orders |
| anchor | string | "mid", "bif", "offer" or "last", used for "SLO" or "TPO" orders |
| participation | number | Participation in percent, used for "VIO" orders |
Request collateral locates
import requests
endpoint = '%s/participants/%s/orders/locates' % (base, mpid)
body = {
"token": "123456789",
"instrument": "BTCB99-USDT.PERP",
"side": "buy",
"volume": "15",
"price": "11052.59"
}
response = requests.post(endpoint, headers=headers, json=body)
This route allows market participants to request a collateral locates when trading perpetual instruments.
It is advised to call this route before submitting an order to ensure the required collateral is available and deployed.
HTTP Request
POST /participants/:mpid/orders/locates
Body Parameters
| Parameter | Type | Description |
|---|---|---|
| token | string | A token provided by the participant to uniquely identify the order |
| instrument | string | Instrument Name as retrieved using the /instruments route, the Instrument MUST be a Perpetual instrument |
| side | string | "buy" or "sell" |
| volume | string | Order size |
| price | string | Order price |
| virtualAccountId | number | optional Virtual account (main account if not provided) |
Response fields
Success means the collateral is available and deployed.
The specific error reason Pending locates request means the collateral is being deployed by our services.
Subsequent calls using the same token as previously sent can be made to request an update on the collateral deployment.
Add a DMA Order
Direct market access (DMA) orders are executed instantly on a single exchange.
Here are the available DMA order types:
- IoC, Immediate or Cancel: Executed, partially or wholly, when placed into the system. Any non-executed quantity is immediately cancelled.
- GTC, Good Till Cancel: Any unexecuted portion of the order, will remain in effect until executed, cancelled by the entering party, or expiration.
- Day order: Same as a GTC order but automatically cancelled at the end of the trading session.
- POD, Post-Only Day order: Provide liquidty to the market without the guarantee not to take existing liquidty.
HTTP Request
POST /participants/:mpid/orders/dma/ioc
POST /participants/:mpid/orders/dma/day
POST /participants/:mpid/orders/dma/gtc
POST /participants/:mpid/orders/dma/pod
import requests
endpoint = '%s/participants/%s/orders/dma/ioc' % (base, mpid)
body = {
"token": "123456789",
"instrument": "BTC-USDT.SPOT",
"side": "buy",
"volume": "15",
"price": "11052.59",
"venue": "SheeldMatch"
}
response = requests.post(endpoint, headers=headers, json=body)
All 3 endpoints require the same parameters:
Body Parameters
| Parameter | Type | Description |
|---|---|---|
| token | string | A token provided by the participant to uniquely identify the order |
| instrument | string | Instrument Name as retrieved using the /instruments route |
| side | string | "buy" or "sell" |
| volume | string | Order size |
| price | string | Order price |
| venue | string | The Venue name as defined in the /venues route |
| virtualAccountId | number | optional Virtual account (main account if not provided) |
Response
See Usage guidelines.
Add a SOR Order
Smart Order Routing (SOR) orders are working across several exchanges in order to optimise your execution to get the best price.
Here are the available SOR order types:
- ISO, Intermarket Sweep Order: Intends to fill the full quantity in one shot across all venues we are connected to. Any non-executed quantity remaining during this only attempt is immediately cancelled.
- IDO, Intermarket Day Order: Same characteristics as an ISO, except it stays open until full quantity has been filled.
- IGO: Intermarket Good Till Cancel Order: Combination between an IDO and a GTC order.
- IPO: Intermarket Post-Only Order: Like an IDO but is always maker (never taker)
HTTP Request
POST /participants/:mpid/orders/sor/iso
POST /participants/:mpid/orders/sor/ido
POST /participants/:mpid/orders/sor/igo
POST /participants/:mpid/orders/sor/ipo
import requests
endpoint = '%s/participants/%s/orders/sor/iso' % (base, mpid)
body = {
"token": "123456789",
"instrument": "BTC-USDT.SPOT",
"side": "buy",
"volume": "15",
"price": "11052.59",
"priority": "price"
}
response = requests.post(endpoint, headers=headers, json=body)
All 3 endpoints require the same parameters:
Body Parameters
| Parameter | Type | Description |
|---|---|---|
| token | string | A token provided by the participant to uniquely identify the order |
| instrument | string | Instrument Name as retrieved using the /instruments route |
| side | string | "buy" or "sell" |
| volume | string | Order size |
| price | string | Order price |
| priority | string | optional Can be "price", "time" or "both" (defaults to "both"), see below for details |
| virtualAccountId | number | optional Virtual account (main account if not provided) |
| Priority | Description |
|---|---|
"both" |
This is the preferred algorithm which strikes a good compromise between maximizing the fill ratio, minimizing the execution time and optimizing the fill price. It is used by default. |
"price" |
This algorithm should be used when execution price is the most important criterion and fill ratio and speed of execution are secondary. |
"time" |
This algorithm should be used when speed of execution is paramount. |
Response
See Usage guidelines.
Add a DSA Order
Direct strategy access (DSA) orders are conductors, which are going to slice orders into smaller parcels of SOR orders, with the aim of minimising market impact, maximising execution price and fill rate. Here are the available DSA order types:
- VIO, Volume Inline Order: Executed by participating to a certain % of the overall volume traded in the different exchanges
- TWAP, Time-Weighted Average Price Order: Executed according to the TWAP during a specified duration
- VWAP, Volume-Weighted Average Price Order: Executed according to the VWAP during a specified duration.
HTTP Request
POST /participants/:mpid/orders/dsa/vio
import requests
endpoint = '%s/participants/%s/orders/dsa/vio' % (base, mpid)
body = {
"token": "123456789",
"instrument": "BTC-USDT.SPOT",
"side": "buy",
"volume": "15",
"price": "11052.59",
"participation": 15
}
response = requests.post(endpoint, headers=headers, json=body)
Body Parameters
| Parameter | Type | Description |
|---|---|---|
| token | string | A token provided by the participant to uniquely identify the order |
| instrument | string | Instrument Name as retrieved using the /instruments route |
| side | string | "buy" or "sell" |
| volume | string | Order size |
| price | string | Order price |
| participation | integer | Percentage of participation based on market average, must be an integer between 1 and 99 |
| virtualAccountId | number | optional Virtual account (main account if not provided) |
HTTP Request
POST /participants/:mpid/orders/dsa/twap
POST /participants/:mpid/orders/dsa/vwap
import requests
endpoint = '%s/participants/%s/orders/dsa/vwap' % (base, mpid)
body = {
"token": "123456789",
"instrument": "BTC-USDT.SPOT",
"side": "buy",
"volume": "15",
"price": "11052.59",
"lifespan": 75
}
response = requests.post(endpoint, headers=headers, json=body)
Body Parameters
| Parameter | Type | Description |
|---|---|---|
| token | string | A token provided by the participant to uniquely identify the order |
| instrument | string | Instrument Name as retrieved using the /instruments route |
| side | string | "buy" or "sell" |
| volume | string | Order size |
| price | string | Order price |
| lifespan | integer | Lifespan of the order in minutes after its creation (limited to the current trading day closing time, if value exceeds it) |
| virtualAccountId | number | optional Virtual account (main account if not provided) |
Response
See Usage guidelines.
Add a TMO Order
Tailor-made orders (TMO) designed to answer a client’s specific needs.
Here are the available TMO order types:
- PTO, Program Trading Order: Executed on synthetic instruments.
- VTO, Volume Triggered Order: Executed when a certain volume has been reached on the different exchanges. Need to specify the amount of volume after which execution will start. Child order is an IGO.
- SLO, Stop Loss Order: Executed to limit the loss of a position until a specified value of an anchor price.
- TPO, Take Profit Order: Executed to capture the profit of a position when an anchor price reaches a specified value.
HTTP Request
POST /participants/:mpid/orders/tmo/pto
import requests
endpoint = '%s/participants/%s/orders/tmo/pto' % (base, mpid)
body = {
"token": "123456789",
"instrument": "BTC-USDC.SPOT",
"side": "buy",
"volume": "15",
"price": "11052.59"
}
response = requests.post(endpoint, headers=headers, json=body)
Body Parameters
| Parameter | Type | Description |
|---|---|---|
| token | string | A token provided by the participant to uniquely identify the order |
| instrument | string | Instrument Name as retrieved using the /instruments route |
| side | string | "buy" or "sell" |
| volume | string | Order size |
| price | string | Order price |
| virtualAccountId | number | optional Virtual account (main account if not provided) |
HTTP Request
POST /participants/:mpid/orders/tmo/vto
import requests
endpoint = '%s/participants/%s/orders/tmo/vto' % (base, mpid)
body = {
"token": "123456789",
"instrument": "BTC-USDT.SPOT",
"side": "buy",
"volume": "15",
"price": "11052.59",
"stop": "12003.4"
}
response = requests.post(endpoint, headers=headers, json=body)
Body Parameters
| Parameter | Type | Description |
|---|---|---|
| token | string | A token provided by the participant to uniquely identify the order |
| instrument | string | Instrument Name as retrieved using the /instruments route |
| side | string | "buy" or "sell" |
| volume | string | Order size |
| price | string | Order price |
| stop | string | Stop price for the order |
| virtualAccountId | number | optional Virtual account (main account if not provided) |
HTTP Request
POST /participants/:mpid/orders/tmo/slo
POST /participants/:mpid/orders/tmo/tpo
import requests
endpoint = '%s/participants/%s/orders/tmo/tpo' % (base, mpid)
body = {
"token": "123456789",
"instrument": "BTC-USDT.SPOT",
"side": "buy",
"volume": "15",
"price": "11052.59",
"stop": "12003.4",
"anchor": "mid"
}
response = requests.post(endpoint, headers=headers, json=body)
Body Parameters
| Parameter | Type | Description |
|---|---|---|
| token | string | A token provided by the participant to uniquely identify the order |
| instrument | string | Instrument Name as retrieved using the /instruments route |
| side | string | "buy" or "sell" |
| volume | string | Order size |
| price | string | Order price |
| stop | string | Stop price for the order |
| anchor | string | optional "mid", "bid", "offer" or "last" (defaults to "last"), see below for details |
| virtualAccountId | number | optional Virtual account (main account if not provided) |
The anchor specifies the condition to watch i.e. a price or a volume.
| Anchor | Descripton |
|---|---|
"bid" |
Watch best bid |
"last" |
Watch last execution |
"offer" |
Watch best offer |
"mid" |
Watch mid price |
Response
See Usage guidelines.
Modify an Order
This method allows market participants to change the price and/or the size of an existing order.
If the modification is only a reduction of the order size, the order keeps its place in the price-level queue. Otherwise, the order goes back to the end of the queue.
HTTP Request
PATCH /participants/:mpid/orders/:token/modify
import requests
endpoint = '%s/participants/%s/orders/%s/modify' % (base, mpid, token)
body = {
"volume": "9.5",
"price": "16540",
"instrument": "BTC-USDT.SPOT"
}
response = requests.patch(endpoint, headers=headers, json=body)
Body Parameters
| Parameter | Type | Description |
|---|---|---|
| instrument | string | Instrument Name as retrieved using the /instruments route |
| volume | string | Order size |
| price | string | Order price |
| virtualAccountId | number | optional Virtual account (main account if not provided) |
Response
See Usage guidelines.
Reduce an Order
This method allows market participants to reduce the volume of an order without it going back to the end of the queue.
This method requires you provide a volume lower than the current ordered one.
HTTP Request
PATCH /participants/:mpid/orders/:token/reduce
import requests
endpoint = '%s/participants/%s/orders/%s/reduce' % (base, mpid, token)
body = {
"volume": "9.5",
"instrument": "BTC-USDT.SPOT"
}
response = requests.patch(endpoint, headers=headers, json=body)
Body Parameters
| Parameter | Type | Description |
|---|---|---|
| instrument | string | Instrument Name as retrieved using the /instruments route |
| volume | string | Order size |
| virtualAccountId | number | optional Virtual account (main account if not provided) |
Response
See Usage guidelines.
Get Order Status
This method allows market participants to retrieve the status of a single order using its token and initialDay as identifier
HTTP Request
GET /participants/:mpid/orders/:token-:initialDay
Response fields
The response is an Order object.
Get Open Orders
This method allows market participants to retrieve all open orders for a Market Participant
HTTP Request
GET /participants/:mpid/orders/open
import requests
endpoint = '%s/participants/%s/orders/open' % (base, mpid)
response = requests.get(endpoint, headers=headers)
This route takes the following optional query parameters:
| Parameter | Type | Description |
|---|---|---|
| virtualAccountId | number | Virtual account (main account if not provided) |
Response fields
The response is a JSON array of Order objects
Get Orders
This method allows market participants to retrieve all orders for a Market Participant.
HTTP Request
GET /participants/:mpid/orders
import requests
endpoint = '%s/participants/%s/orders?page=%s&pageSize=%s&sort=%s|asc' % (base, mpid, page, pageSize, sortedColumn)
response = requests.get(endpoint, headers=headers)
This route takes the following optional query parameters:
| Parameter | Type | Description |
|---|---|---|
| start | number or string | inclusive Can be either a Unix timestamp (in seconds), a JS timestamp (in milliseconds), or a string date with format YYYY-MM-DD HH:mm:ss or YYYY-MM-DDTHH:mm:ss.SSSZ |
| end | number or string | inclusive Can be either a Unix timestamp (in seconds), a JS timestamp (in milliseconds), or a string date with format YYYY-MM-DD HH:mm:ss or YYYY-MM-DDTHH:mm:ss.SSSZ |
| instrument | string | Instrument Name as retrieved using the /instruments route |
| side | string | "B" or "S" |
| hideClosed | boolean | Filters out unfilled closed orders, accepts "true" or "false" (defaults) |
| openOnly | boolean | Filters out all closed orders, accepts "true" or "false" (defaults) |
| virtualAccountId | number | Virtual account (main account if not provided) |
Response fields
{
"data": [
/* Order object*/,
...
],
"count": 15
}
Where data is a JSON array of Order objects
Cancel an Order
This method allows market participants to cancel one order
HTTP Request
DELETE /participants/:mpid/orders/:token
import requests
endpoint = '%s/participants/%s/orders/%s' % (base, mpid, token)
response = requests.delete(endpoint, headers=headers)
Response
See Usage guidelines.
Mass Cancel Orders
This method mass cancels all open orders of a Market Participant.
HTTP Request
DELETE /participants/:mpid/orders
import requests
endpoint = '%s/participants/%s/orders' % (base, mpid )
response = requests.delete(endpoint, headers=headers)
Response fields
No response
Suspend Trading
This method suspends a Market Participant. Its following trading messages will be rejected by the gateway.
HTTP Request
POST /participants/:mpid/suspend
import requests
endpoint = '%s/participants/%s/suspend' % (base, mpid )
response = requests.post(endpoint, headers=headers)
Response fields
No response
Resume Trading
This method resumes a Market Participant. Its following trading messages will be accepted by the gateway.
HTTP Request
POST /participants/:mpid/resume
import requests
endpoint = '%s/participants/%s/resume' % (base, mpid )
response = requests.post(endpoint, headers=headers)
Response fields
No response
Get collateral locates requests
This method allows market participants to retrieve all collateral locates requests made during the current session.
HTTP Request
GET /participants/:mpid/orders/locates-requests
import requests
endpoint = '%s/participants/%s/orders/locates-requests' % (base, mpid)
response = requests.get(endpoint, headers=headers)
Response fields
Example of JSON structure returned by the request:
[
{
"price": "0.458391",
"volume": "2159728.2",
"mpid": "SONE",
"token": "2551884922255932",
"time": "1661330566574000000",
"iid": 19,
"instrument": "BTC-USDT.PERP-Binance-USDT",
"vid": 3,
"venue": "Binance",
"side": "S",
"virtualAccountId": null,
"userId": "ab35e86e-8ce1-446e-b0d7-d33b1bd6b0bc",
"day": "2022-08-24",
"status": "completed",
"id": "8f2752a0-4761-44b1-b3a0-da6affe06ffc",
"createdAt": "2022-08-24T08:42:46.576Z",
"updatedAt": "2022-08-24T12:26:15.008Z"
},
{
"price": "0.459049",
"volume": "214",
"mpid": "SONE",
"token": "2191275191699646",
"time": "1661349149740000000",
"iid": 19,
"instrument": "BTC-USDT.PERP-Binance-USDT",
"vid": 0,
"venue": null,
"side": "S",
"virtualAccountId": null,
"userId": "ab35e86e-8ce1-446e-b0d7-d33b1bd6b0bc",
"day": "2022-08-24",
"status": "pending",
"id": "5eb54c41-8717-4ea6-af2b-2174b63e674e",
"createdAt": "2022-08-24T13:52:29.743Z",
"updatedAt": "2022-08-24T13:52:29.743Z"
}
]
The response is an array of JSON objects with the following fields:
| Field | Type | Description |
|---|---|---|
| price | string | Price of the locates |
| volume | string | Volume of the locates |
| mpid | string | Market Participant Id |
| token | string | Unique token for this locates |
| time | string | Time of request (in nanoseconds) |
| iid | number | The Instrument ID for the given day |
| instrument | string | The Instrument name for the iid |
| vid | number | The Venue ID for the given day |
| venue | string | The Venue name for the vid (can be null for routed requests) |
| side | string | "B" or "S" |
| virtualAccountId | number | Virtual account id (can be null to indicate the "Main Account") |
| userId | string | Id of the User who made the request |
| day | string | Session of the locates |
| status | string | Status of the locates request, can be "pending", "processing", "completed", "rejected" or "error" |
| id | string | Unique Locates Id |
| createdAt | string | Date of creation |
| updatedAt | string | Date of last update |
Submit a high touch ticket
This method allows market participant to submit new high-touch tickets for our trading desk.
HTTP Request
POST /accounts/:accountId/high-touch-tickets
import requests
endpoint = '%s/accounts/%s/high-touch-tickets' % (base, accountId)
body = {
"stategy": "vwap",
"startinTime": "2025-06-05T08:00:00.000Z",
"endingTime": "null",
"inputs": [
{
"assetId": "BTC",
"quantity": "1.453"
},
{
"assetId": "ADA",
"quantity": "905"
}
],
"outputs": [
{
"assetId": "ETH",
"proportion": "50"
},
{
"assetId": "USDT",
"proportion": "35"
},
{
"assetId": "EURC",
"proportion": "15"
}
],
"targetParticipant": "SONE"
}
response = requests.post(endpoint, headers=headers, json=body)
Body Parameters
| Parameter | Type | Description |
|---|---|---|
| strategy | string | "implementationShortfall", "vwap", "twap", "inlinePov" |
| startingTime | number or string | Can be either a Unix timestamp (in seconds), a JS timestamp (in milliseconds), or a string date with format YYYY-MM-DD HH:mm:ss or YYYY-MM-DDTHH:mm:ss.SSSZ |
| endingTime | number or string | Can be either a Unix timestamp (in seconds), a JS timestamp (in milliseconds), or a string date with format YYYY-MM-DD HH:mm:ss or YYYY-MM-DDTHH:mm:ss.SSSZ.Required when strategy is either "vwap" or "twap", otherwise must be null |
| inputs | Array | Array of asset inputs (see below) |
| outputs | Array | Array of target outputs |
| participation | number | If strategy is inlinePov, must be a positive number with a maximum precision of 0.01 representing the market participation %. Otherwise, must be null |
| targetParticipant | string | The MPID where the outputs will be deposited on. |
| virtualAccountId | number | optional Virtual account id requesting the high-touch ticket (null indicates the "Main Account") |
inputs is a JSON array of object with the following properties
| Parameter | Type | Description |
|---|---|---|
| assetId | string | Asset Identifier |
| quantity | number | Quantity of asset |
| limitPrice | number | optional Limit price of the asset, can be null |
outputs is a JSON array of object with the following properties
| Parameter | Type | Description |
|---|---|---|
| assetId | string | Asset Identifier |
| proportion | number | Percentage of this asset in the resulting basket |
Please note that inputs will source your "funding" account balances.
If need be, you must first transfer your funds back to the "funding" sub-account before submitting your High Touch ticket.
Response fields
Example of JSON structure returned by the request:
{
"participation": "3",
"attachments": [],
"lowTouchOrderToken": null,
"lowTouchOrderInitialDay": null,
"id": "20250602A",
"clientAccountId": "45cabb50-42f7-4a69-a534-db030882f34c",
"clientVirtualAccountId": null,
"clientMpid": "SONE",
"mode": "preFunded",
"strategy": "inlinePov",
"status": "requested",
"startingTime": "2025-05-30T16:00:00.618Z",
"endingTime": null,
"inputs": [
{ "assetId": "BTC", "quantity": "1.453", "limitPrice": null },
{ "assetId": "ADA", "quantity": "600", "limitPrice": null }
],
"outputs": [
{ "assetId": "USDT", "proportion": "50" },
{ "assetId": "ETH", "proportion": "35" },
{ "assetId": "SOL", "proportion": "15" }
],
"acquisitionDay": "2025-06-02",
"publicNotes": null,
"createdAt": "2025-06-02T14:01:55.522Z"
}
Backoffice
Get MPID Balances
import requests
endpoint = '%s/participants/%s/balances' % (base, mpid)
response = requests.get(endpoint, headers=headers)
This route allows market participants to retrieve their balances.
HTTP Request
GET /participants/:MPID/balances
This route can take the following optional query parameters:
| Parameter | Type | Description |
|---|---|---|
| virtualAccountId | number | Specific virtual account to retrieve balances for |
Response fields
Example of JSON structure returned by the request:
[
{
"quantity": "100",
"borrowed": "0",
"committed": "0",
"collateralized": "0",
"accumulated": "0",
"cost": "4376041",
"positionCost": "4376041",
"entryPrice": "43760.41",
"assetId": "BTC",
"mpid": "SHLD",
"virtualAccountId": null,
"asset": {
"minWithdrawalAmount": "0.01",
"rate": "26933.62",
"strikePrice": null,
"id": "BTC",
"name": "BTC",
"description": "Bitcoin",
"classification": "S",
"logo": "btc.svg",
"availableProgramTrading": true,
"underlyingId": null,
"currencyId": null,
"issuingVenue": null,
"marginId": null,
"expiration": null,
"durationCode": null
}
}
]
The response is a JSON object of balances:
| Field | Type | Description |
|---|---|---|
| quantity | string | Balance |
| borrowed | string | Borrowed balance |
| committed | string | Committed balance |
| collateralized | string | Collateralized balance |
| accumulated | string | Accumulated transacted quantity of asset |
| cost | string | Money flow of asset |
| positionCost | string | Current position cost for this asset |
| entryPrice | string | PositionCost / quantity |
| assetId | string | Asset ASCII identifier |
| mpid | string | 4-character MPID |
| virtualAccountId | number | Virtual account id owning this balance (null indicates the "Main Account") |
| asset | object | Asset object (see the /assets route) |
Get User Information
import requests
endpoint = '%s/user' % base
response = requests.get(endpoint, headers=headers)
This route allows users to retrive the detailed user data such as which market participants and accounts they have access to.
HTTP Request
GET /user
This endpoint doesn't require any parameters.
Response fields
Example of JSON structure returned by the request:
{
"user": {
"id": "ec4c98ee-c83e-42cf-9d23-0bcb367b1ef3",
"email": "account@account.com",
"firstname": "First",
"lastname": "Last",
"fullname": "First Last",
"totpValidated": true,
"tosUpdatedAt": "2020-01-01 00:00:00+00",
"lastSigninAt": "2020-01-01 00:00:00+00",
"defaultAccountId": "35a6934e-94e5-4396-94e6-a2b86bce6bf9",
"defaultParticipantId": "SHLD",
"cancelDisconnect": false,
"createdAt": "2020-01-01 00:00:00+00"
},
"participants": {
"SHLD": {
"mpid": "SHLD",
"accountId": "35a6934e-94e5-4396-94e6-a2b86bce6bf9",
"maxRate": 1000,
"maxVol": 1000000,
"maxDailyVol": "1000000000",
"maxOpen": 10000,
"createdAt": "2020-01-01 00:00:00+00",
"isSuspended": false,
"isHalted": false
}
},
"accounts": {
"35a6934e-94e5-4396-94e6-a2b86bce6bf9": {
"id": "35a6934e-94e5-4396-94e6-a2b86bce6bf9",
"name": "Account1",
"country": "FR",
"role": {
"id": "a61da8a1-c8e2-55aa-9858-f8436f569f93",
"name": "admin",
"createdAt": "2020-01-01 00:00:00+00"
},
"permissions": ["u_orders", "u_otc", "u_trading"],
"assetUniverse": {
"BTC": { "isActive": true },
"ETH": { "isActive": true },
"SOL": { "isActive": true },
"EURC": { "isActive": true }
}
}
}
}
The user property has the following fields:
| Field | Type | Description |
|---|---|---|
| id | string | Account UUID |
| string | Account email | |
| firstname | string | First name |
| lastname | string | Last name |
| fullname | string | First name + Last name |
| totpValidated | boolean | Is the TOTP 2FA validated ? |
| tosUpdatedAt | string | Date of acceptance of terms of service |
| defaultAccountId | string | Default Account UUID |
| defaultParticipantId | string | Default MPID used by the account |
| cancelDisconnect | boolean | Do we automatically cancel open orders on disconnection ? |
| createdAt | string | Date of account creation |
The accounts property (indexed by account Ids) contains objects with the following fields:
| Field | Type | Description |
|---|---|---|
| id | string | Account UUID |
| name | string | Account name |
| country | string | Account country, used for IP geolocation security |
| permissions | Array | Account permissions, used internally by Aplo to restrict feature/API access |
| assetUniverse | Object | The list of assets the account has access to. |
The role property in each accounts contains the following fields:
| Field | Type | Description |
|---|---|---|
| id | string | Role UUID |
| name | string | Role name |
| createdAt | Date | Role creation date |
The participants property (indexed by MPIDs) contains objects with the following fields:
| Field | Type | Description |
|---|---|---|
| mpid | string | 4-character Market participant ID |
| accountId | string | Account UUID |
| maxRate | number | Maximum number of messages per second |
| maxVol | number | Maximum USD notional value of an order |
| maxOpen | number | Maximum USD notional value of open orders |
| maxDailyVol | string | Maximum USD notional value of all orders in a day |
| createdAt | string | Date of creation of the participant |
| isSuspended | boolean | Whether or not this participant has been suspended by the user or not (trading cannot be done while suspended) |
| isHalted | boolean | Similar to isSuspended but is managed by Aplo directly. (trading cannot be done if halted) |
Get Withdrawal Addresses
import requests
endpoint = '%s/accounts/%s/addresses' % (base, accountId)
response = requests.get(endpoint, headers=headers)
This route allow users to get all available withdrawal address for their account (or a specific virtual account if given).
HTTP Request
GET /accounts/:accountId/addresses
This route can take the following optional query parameters:
| Parameter | Type | Description |
|---|---|---|
| virtualAccountId | number | Specific virtual account to retrieve withdrawal addresses for |
Response fields
Example of JSON structure returned by the request:
[
{
"id": "314e8f4b-4948-4e2e-b4f0-12c7155c930f",
"accountId": "2e530764-9228-437e-a62f-a049e6400269",
"virtualAccountId": null,
"isArchived": false,
"isWhitelisted": true,
"whitelistedAt": "2023-11-24T13:35:26.151Z",
"whitelistValidators": [
{
"name": "User One",
"time": 1659083710297,
"userId": "ab35e86e-8ce1-446e-b0d7-d33b1bd6b0bc",
"approved": true
}
],
"name": "BTC deposit address",
"networkId": "BTC",
"isUniversal": true,
"value": "98Dj-NLC86jd:42Lxzk9ld:9228",
"memo": "0DJDZ",
"isSelfBeneficiary": true,
"beneficiaryLegalName": null,
"beneficiaryFirstName": null,
"beneficiaryLastName": null,
"beneficiaryContactEmail": null,
"beneficiaryCountry": null,
"beneficiaryAddress": null,
"beneficiaryActivity": null,
"beneficiaryTaxId": null,
"beneficiaryRelationship": null,
"paymentReason": null,
"isSelfHosted": true,
"providerName": null,
"providerWebsite": null,
"providerCountry": null,
"walletPurpose": "testing",
"description": null,
"createdAt": "2023-11-24T13:35:26.151Z",
"provider": null,
"assets": [...]
},
{
"id": "a14e8f4b-4948-4e2e-b4f0-1wc7155c9302",
"accountId": "2e530764-9228-437e-a62f-a049e6400269",
"virtualAccountId": 1138967864,
"isArchived": false,
"isWhitelisted": true,
"whitelistedAt": "2023-11-24T13:35:26.151Z",
"whitelistValidators": [
{
"name": "User One",
"time": 1659083710297,
"userId": "ab35e86e-8ce1-446e-b0d7-d33b1bd6b0bc",
"approved": true
}
],
"name": "BTC deposit address 1138967864",
"networkId": "BTC",
"isUniversal": false,
"value": "98Dj-NLc86JD:42l2329LDQ:5143",
"memo": null,
"isSelfBeneficiary": false,
"beneficiaryLegalName": "Company Name",
"beneficiaryFirstName": null,
"beneficiaryLastName": null,
"beneficiaryContactEmail": "contact@company.com",
"beneficiaryCountry": "FR",
"beneficiaryAddress": "19 rue des ailerons bleutés, Paris",
"beneficiaryActivity": null,
"beneficiaryTaxId": null,
"beneficiaryRelationship": "Intra-group transfer; Deposit",
"paymentReason": "Intra-group",
"isSelfHosted": false,
"providerName": null,
"providerWebsite": null,
"providerCountry": null,
"walletPurpose": null,
"description": "BTC deposit address 1138967864",
"createdAt": "2023-11-24T13:35:26.151Z",
"provider": null,
"assets": [...]
},
{
"id": "c32a0065-aa65-4041-9656-63835159b4e4",
"accountId": "2e530764-9228-437e-a62f-a049e6400269",
"virtualAccountId": null,
"isArchived": false,
"isWhitelisted": true,
"whitelistedAt": "2023-11-24T13:35:26.151Z",
"whitelistValidators": [
{
"name": "User One",
"time": 1659083710297,
"userId": "ab35e86e-8ce1-446e-b0d7-d33b1bd6b0bc",
"approved": true
}
],
"name": "Withdrawal address Bitcoint",
"networkId": "BTC",
"isUniversal": false,
"value": "98Dj-NLc86JD-42l2329LDQ25143",
"memo": null,
"isSelfBeneficiary": true,
"beneficiaryLegalName": null,
"beneficiaryFirstName": null,
"beneficiaryLastName": null,
"beneficiaryContactEmail": null,
"beneficiaryCountry": null,
"beneficiaryAddress": null,
"beneficiaryActivity": null,
"beneficiaryTaxId": null,
"beneficiaryRelationship": null,
"paymentReason": null,
"isSelfHosted": false,
"providerName": null,
"providerWebsite": null,
"providerCountry": null,
"walletPurpose": null,
"description": "BTC deposit address 1138967864",
"createdAt": "2023-11-24T13:35:26.151Z",
"provider": {
"id": "41d8fb99-8f54-4f12-bfae-10e6abbcf807",
"name": "Binance",
"website": "https://www.binance.com/",
"country": "SC"
},
"assets": [...]
},
]
The response is an array of JSON objects with the following fields:
| Header | Type | Description |
|---|---|---|
| id | string | Address unique ID |
| accountId | string | Address owner's account Id |
| virtualAccountId | number | The virtual account that this address is linked to (null indicates the "Main Account") |
| isArchived | boolean | Whether the address has been archived or not |
| isWhitelisted | boolean | Whether the address has been whitelisted. |
| whitelistedAt | Date | Date of whitelisting for this address, is null if the address is not whitelisted |
| whitelistValidators | array | Array of whitelisting validators (see below) |
| name | string | Human readable address alias |
| networkId | string | Network id (see /networks) |
| isUniversal | boolean | Whether the address is universal for all assets on the network |
| value | string | Actual address value |
| memo | string | Memo/Tag for this address (can be null) |
| isSelfBeneficiary | boolean | Whether this address's beneficiary is the account's owner or someone else |
| beneficiaryLegalName | string | The address beneficiary's legal name. Can be null if isSelfBeneficiary is true or if the beneficiary is a natural person. |
| beneficiaryFirstName | string | The address beneficiary's first name. Can be null if isSelfBeneficiary is true or if the beneficiary is a legal person. |
| beneficiaryLastName | string | The address beneficiary's last name. Can be null if isSelfBeneficiary is true or if the beneficiary is a legal person. |
| beneficiaryContactEmail | string | The address beneficiary's contact email (can be null) |
| beneficiaryCountry | string | The address beneficiary's country ISO code. Can be null if isSelfBeneficiary is true. |
| beneficiaryAddress | string | The address beneficiary's real life address (can be null) |
| beneficiaryActivity | string | The address beneficiary's business activity (can be null) |
| beneficiaryTaxId | string | The address beneficiary's tax ID (can be null) |
| beneficiaryRelationship | string | The address beneficiary's business relationship between the account's owner. Can be null if isSelfBeneficiary is true. |
| paymentReason | string | The reasons for payment to the address beneficiary. Can be null if isSelfBeneficiary is true. |
| isSelfHosted | boolean | Whether or not this address is self hosted or on a thrid party provider. |
| providerName | string | The address provider's name. Can be null if isSelfBeneficiary is false orisSelfHosted is true. |
| providerWebsite | string | The address provider's website. Can be null if isSelfBeneficiary is false orisSelfHosted is true. |
| providerCountry | string | The address provider's country ISO code. Can be null if isSelfBeneficiary is false orisSelfHosted is true. |
| walletPurpose | string | The purpose of the self hosted wallet. Can be null if isSelfBeneficiary is false or isSelfHosted is false. |
| description | string | Additional comments for this address (can be null) |
| createdAt | Date | Date of creation |
| provider | object | The address provider's information (see below). Can be null if isSelfBeneficiary is false or isSelfHosted is true |
| assets | array | List of supported assets that can be withdrawn on this address (see the /assets route) |
provider is a JSON oject with the following properties:
| Header | Type | Description |
|---|---|---|
| id | string | Address provider ID |
| name | string | The provider's name |
| website | string | The provider's website (can be null) |
| country | string | The provider's country ISO code (can be null) |
whitelistValidators is an array of JSON object with the following properties:
| Header | Type | Description |
|---|---|---|
| name | string | Full name of the validator |
| time | number | Timestamp of validation/rejection |
| userId | string | UUID of the validator |
| approved | boolean | Whether the validator approved the address' whitelisting or not |
Address whitelisting is an optional account wide parameter. The number of validators needed to whitelist an address can be modified by our operations team.
Add a new withdrawal address
import requests
endpoint = '%s/accounts/%s/addresses' % (base, accountId)
body = {
"virtualAccountId": null,
"name":"New Bitcoin withdrawal address",
"networkId":"BTC",
"isUniversal": false,
"assets": ["BTC"],
"value":"98Dj-NLC86jd:42Lxzk9ldq:5143",
"memo":"0DJDZ",
"isSelfBeneficiary": true,
"isProfessional": false,
"beneficiaryLegalName": null,
"beneficiaryFirstName": null,
"beneficiaryLastName": null,
"beneficiaryContactEmail": null,
"beneficiaryCountry": null,
"beneficiaryAddress": null,
"beneficiaryActivity": null,
"beneficiaryTaxId": null,
"beneficiaryRelationship": null,
"paymentReason": null,
"isSelfHosted": true,
"isOtherProvider": false,
"providerId": null,
"providerName": null,
"providerWebsite": null,
"providerCountry": null,
"purpose": "Transfer",
"description": "New Bitcoin withdrawal address",
}
response = requests.post(endpoint, headers=headers, json=body)
This route allow users to add a new withdrawal address.
HTTP Request
POST /accounts/:accountId/addresses
Body Parameters
| Parameter | Type | Description |
|---|---|---|
| virtualAccountId | number | optional Virtual account id this address is for (null indicates the "Main Account") |
| name | string | Address alias |
| networkId | string | Network ID or name |
| isUniversal | boolean | Whether the address is universal for all assets on the network |
| assets | array | List of assets to be withdrawn on this address. mandatory if isUniversal is false. |
| value | string | Actual address value on the specified network |
| memo | string | optional Address memo |
| isSelfBeneficiary | boolean | Whether this address's beneficiary is the account's owner or someone else |
| isProfessional | boolean | Whether this address's beneficiary is a legal person or natural person. optional if isSelfBeneficiary is true |
| beneficiaryLegalName | string | The address beneficiary's legal name. optional if isSelfBeneficiary is true or isProfessional is false. |
| beneficiaryFirstName | string | The address beneficiary's first name. optional if isSelfBeneficiary is true or isProfessional is true. |
| beneficiaryLastName | string | The address beneficiary's last name. optional if isSelfBeneficiary is true or isProfessional is true. |
| beneficiaryContactEmail | string | optional The address beneficiary's contact email |
| beneficiaryCountry | string | The address beneficiary's country ISO code. optional if isSelfBeneficiary is true. |
| beneficiaryAddress | string | optional The address beneficiary's real life address |
| beneficiaryActivity | string | optional The address beneficiary's business activity |
| beneficiaryTaxId | string | optional The address beneficiary's tax ID |
| beneficiaryRelationship | string | The address beneficiary's business relationship between the account's owner. optional if isSelfBeneficiary is true. |
| paymentReason | string | The reasons for payment to the address beneficiary. optional if isSelfBeneficiary is true. |
| isSelfHosted | boolean | Whether or not this address is self hosted or on a thrid party provider. optional if isSelfBeneficiary is false. |
| isOtherProvider | boolean | Whether or not you know your provider's ID. optional if isSelfBeneficiary is false or if isSelfHosted is true. |
| providerId | string | The address provider's ID. optional if isSelfBeneficiary is false or if isSelfHosted is true or if isOtherProvider is true. |
| providerName | string | The address provider's name. optional if isSelfBeneficiary is false or if isSelfHosted is true or if isOtherProvider is false. |
| providerWebsite | string | The address provider's website. optional if isSelfBeneficiary is false or if isSelfHosted is true or if isOtherProvider is false. |
| providerCountry | string | The address provider's country ISO code. optional if isSelfBeneficiary is false or if isSelfHosted is true or if isOtherProvider is false. |
| walletPurpose | string | The purpose of the self hosted wallet. optional if isSelfBeneficiary is false or if isSelfHosted is false. |
| description | string | optional Additional comments for this address |
Response fields
See this route for details about the returned body.
Approve the whitelisting of a withdrawal address
import requests
endpoint = '%s/accounts/%s/addresses/%s/approve' % (base, accountId, addressId)
body = {
"approved": true,
}
response = requests.patch(endpoint, headers=headers, json=body)
This route allow users to approve or reject the whitelisting process of a withdrawal address.
HTTP Request
PATCH /accounts/:accountId/addresses/:addressId/approve
Body Parameters
| Parameter | Type | Description |
|---|---|---|
| approve | boolean | Whether to approve or reject the whitelisting of the address |
Response fields
See this route for details about the returned body.
Modify a withdrawal address
import requests
endpoint = '%s/accounts/%s/addresses/%s' % (base, accountId, addressId)
body = {
"isArchived": false,
"name": "New Bitcoin withdrawal address",
"isUniversal": false,
"assets": ["BTC"],
"isSelfBeneficiary": true,
"isProfessional": false,
"beneficiaryLegalName": null,
"beneficiaryFirstName": null,
"beneficiaryLastName": null,
"beneficiaryContactEmail": null,
"beneficiaryCountry": null,
"beneficiaryAddress": null,
"beneficiaryActivity": null,
"beneficiaryTaxId": null,
"beneficiaryRelationship": null,
"paymentReason": null,
"isSelfHosted": true,
"isOtherProvider": false,
"providerId": null,
"providerName": null,
"providerWebsite": null,
"providerCountry": null,
"purpose": "Transfer",
"description": null,
}
response = requests.patch(endpoint, headers=headers, json=body)
This route allow users to modify a withdrawal address.
HTTP Request
PATCH /accounts/:accountId/addresses/:addressId
Body Parameters
| Parameter | Type | Description |
|---|---|---|
| isArchived | boolean | optional Whether or not the address is usable for withdrawals |
| name | string | optional Address alias |
| isUniversal | boolean | optional Whether the address is universal for all assets on the network |
| assets | array | List of assets to be withdrawn on this address. mandatory if isUniversal is false. |
| isSelfBeneficiary | boolean | optional Whether this address's beneficiary is the account's owner or someone else |
| isProfessional | boolean | Whether this address's beneficiary is a legal person or natural person. optional if isSelfBeneficiary is true |
| beneficiaryLegalName | string | The address beneficiary's legal name. optional if isSelfBeneficiary is true or isProfessional is false. |
| beneficiaryFirstName | string | The address beneficiary's first name. optional if isSelfBeneficiary is true or isProfessional is true. |
| beneficiaryLastName | string | The address beneficiary's last name. optional if isSelfBeneficiary is true or isProfessional is true. |
| beneficiaryContactEmail | string | optional The address beneficiary's contact email |
| beneficiaryCountry | string | The address beneficiary's country ISO code. optional if isSelfBeneficiary is true. |
| beneficiaryAddress | string | optional The address beneficiary's real life address |
| beneficiaryActivity | string | optional The address beneficiary's business activity |
| beneficiaryTaxId | string | optional The address beneficiary's tax ID |
| beneficiaryRelationship | string | The address beneficiary's business relationship between the account's owner. optional if isSelfBeneficiary is true. |
| paymentReason | string | The reasons for payment to the address beneficiary. optional if isSelfBeneficiary is true. |
| isSelfHosted | boolean | Whether or not this address is self hosted or on a thrid party provider. optional if isSelfBeneficiary is false. |
| isOtherProvider | boolean | Whether or not you know your provider's ID. optional if isSelfBeneficiary is false or if isSelfHosted is true. |
| providerId | string | The address provider's ID. optional if isSelfBeneficiary is false or if isSelfHosted is true or if isOtherProvider is true. |
| providerName | string | The address provider's name. optional if isSelfBeneficiary is false or if isSelfHosted is true or if isOtherProvider is false. |
| providerWebsite | string | The address provider's website. optional if isSelfBeneficiary is false or if isSelfHosted is true or if isOtherProvider is false. |
| providerCountry | string | The address provider's country ISO code. optional if isSelfBeneficiary is false or if isSelfHosted is true or if isOtherProvider is false. |
| walletPurpose | string | The purpose of the self hosted wallet. optional if isSelfBeneficiary is false or if isSelfHosted is false. |
| description | string | optional Additional comments for this address |
Response fields
See this route for details about the returned body.
Update assets for a withdrawal address
import requests
endpoint = '%s/accounts/%s/addresses/%s/assets' % (base, accountId, addressId)
body = {
"assets": [
"USDT",
"UDSC"
]
}
response = requests.put(endpoint, headers=headers, json=body)
This route allow users to update all assets for a withdrawal address, making it possible for the specified assets to be withdrawn on this address.
The provided assets must be available on the network the address uses.
HTTP Request
PUT /accounts/:accountId/addresses/:addressId/assets
Get Withdrawal Banks
import requests
endpoint = '%s/accounts/%s/banks' % (base, accountId)
response = requests.get(endpoint, headers=headers)
This route allow users to get all available withdrawal bank accounts for their account (or a specific virtual account if given).
HTTP Request
GET /accounts/:accountId/banks
This route can take the following optional query parameters:
| Parameter | Type | Description |
|---|---|---|
| virtualAccountId | number | Specific virtual account to retrieve withdrawal banks for |
Response fields
Example of JSON structure returned by the request:
[
{
"id": "a3c9c401-2b3e-4741-8d93-10075958fb2f",
"description": "Withdrawal Bank account SEPA",
"beneficiaryName": "SheeldTrading 1",
"beneficiaryAddress1": "15 rue des Allités",
"beneficiaryAddress2": null,
"beneficiaryPostcode": "75001",
"beneficiaryCity": "Paris",
"beneficiaryRegion": null,
"beneficiaryCountry": "FR",
"bankName": "NEO PAYMENT FACTORY S.L.",
"bankAddress1": "C. Comte Urgell 143",
"bankAddress2": null,
"bankPostcode": "31000",
"bankCity": "Toulouse",
"bankRegion": null,
"bankCountry": "FR",
"bankId": "ZH02CITI00001077181611",
"bankAccountNumber": "98DNQ",
"isIntermediary": false,
"intermediaryBankId": null,
"walletAddress": null,
"isApprovedByAplo": true,
"isWhitelisted": true,
"whitelistedAt": "2021-12-31T23:00:00.000Z",
"whitelistValidators": [
{
"name": "User One",
"time": 1659083710297,
"userId": "ab35e86e-8ce1-446e-b0d7-d33b1bd6b0bc",
"approved": true
}
],
"isArchived": false,
"isActive": true,
"networkId": "SEPA",
"accountId": "2e530764-9228-437e-a62f-a049e6400269",
"virtualAccountId": null,
"createdAt": "2022-12-15T10:38:02.575Z",
"assetId": "EUR"
},
{
"id": "c2d16c3d-3d05-4c8f-b8ef-5495f3eb506f",
"description": "Withdrawal Bank account BLINC",
"beneficiaryName": "SheeldTrading 1",
"beneficiaryAddress1": null,
"beneficiaryAddress2": null,
"beneficiaryPostcode": null,
"beneficiaryCity": null,
"beneficiaryRegion": null,
"beneficiaryCountry": null,
"bankName": null,
"bankAddress1": null,
"bankAddress2": null,
"bankPostcode": null,
"bankCity": null,
"bankRegion": null,
"bankCountry": null,
"bankId": null,
"bankAccountNumber": null,
"isIntermediary": false,
"intermediaryBankId": null,
"walletAddress": "97DNQZQOZDZQ",
"isApprovedByAplo": true,
"isWhitelisted": true,
"whitelistedAt": "2021-12-31T23:00:00.000Z",
"whitelistValidators": [
{
"name": "User One",
"time": 1659083710297,
"userId": "ab35e86e-8ce1-446e-b0d7-d33b1bd6b0bc",
"approved": true
}
],
"isArchived": false,
"isActive": true,
"networkId": "BLINC",
"accountId": "2e530764-9228-437e-a62f-a049e6400269",
"virtualAccountId": null,
"createdAt": "2022-12-15T10:38:02.575Z",
"assetId": "USD"
}
]
The response is an array of JSON objects with the following fields:
| Header | Type | Description |
|---|---|---|
| id | string | Aplo Bank unique ID |
| networkId | string | Network id (see /networks) |
| beneficiaryName | string | The name of the bank account's beneficiary (can be null depending on the network used) |
| beneficiaryAddress1 | string | The first address line of the bank account's beneficiary (can be null depending on the network used) |
| beneficiaryAddress2 | string | The second address line of the bank account's beneficiary (can be null depending on the network used) |
| beneficiaryPostcode | string | The postcode of the bank account's beneficiary (can be null depending on the network used) |
| beneficiaryCity | string | The city of the bank account's beneficiary (can be null depending on the network used) |
| beneficiaryRegion | string | The state/region of the bank account's beneficiary (can be null depending on the network used) |
| beneficiaryCountry | string | The country of the bank account's beneficiary (can be null depending on the network used) |
| bankName | string | The name of the bank account (can be null depending on the network used) |
| bankAddress1 | string | The first address line of the bank account (can be null depending on the network used) |
| bankAddress2 | string | The second address of the bank account (can be null depending on the network used) |
| bankPostcode | string | The postcode of the bank account (can be null depending on the network used) |
| bankCity | string | The city of the bank account (can be null depending on the network used) |
| bankRegion | string | The state/region of the bank account (can be null depending on the network used) |
| bankCountry | string | The country of the bank account (can be null depending on the network used) |
| bankId | string | BIC/SWIFT code for "SEPA" and "SWIFT" networks, ABA routing number for "WIRE" network (can be null depending on the network used) |
| bankAccountNumber | string | IBAN for "SEPA", "SWIFT" and "WIRE" networks, can also be Account number for "WIRE" (can be null depending on the network used) |
| isIntermediary | boolean | Whether this bank account uses an intermediary bank account |
| intermediaryBankId | string | The intermediary bank Id: BIC/SWIFT code for "SEPA" and "SWIFT" networks, ABA routing number for "WIRE" network (null if "isIntermediary" is false) |
| walletAddress | string | The wallet address of the bank account (only used for "BLINC" network) |
| description | string | Human readable name |
| accountId | string | The account Id |
| isActive | boolean | Whether the bank is active or not |
| isApprovedByAplo | boolean | Whether the bank has been verified with a mandatory test deposit |
| isArchived | boolean | Whether the bank has been archived or not |
| isWhitelisted | boolean | Whether the bank has been whitelisted. |
| virtualAccountId | number | The virtual account that this bank is linked to (null indicates the "Main Account") |
| whitelistedAt | Date | Date of whitelisting for this bank, is null if the bank is not whitelisted |
| whitelistValidators | array | Array of whitelisting validators (see below) |
| assetId | string | The asset that can be withdrawn on this bank account |
| createdAt | Date | Date of creation |
whitelistValidators is an array of JSON object with the following properties:
| Header | Type | Description |
|---|---|---|
| name | string | Full name of the validator |
| time | number | Timestamp of validation/rejection |
| userId | string | UUID of the validator |
| approved | boolean | Whether the validator approved the bank's whitelisting or not |
Bank whitelisting is an optional account wide parameter. The number of validators needed to witelist a bank can be modified by our operations team.
Add a new withdrawal bank
import requests
endpoint = '%s/accounts/%s/bank' % (base, accountId)
body = {
"networkId": "SWIFT",
"assetId": "EUR",
"description": "Swift EUR withdrawal bank cc",
"beneficiaryName": "SheeldTrading 1",
"beneficiaryAddress1": "15 rue des Allités",
"beneficiaryPostcode": "75001",
"beneficiaryCity": "Paris",
"beneficiaryCountry": "FR",
"bankName": "NEO PAYMENT FACTORY S.L.",
"bankAddress1": "C. Comte Urgell 143",
"bankPostcode": "31000",
"bankCity": "Toulouse",
"bankCountry": "FR",
"bankId": "ZH02CITI00001077181611",
"bankAccountNumber": "98DNQ",
"virtualAccountId": null,
}
response = requests.post(endpoint, headers=headers, json=body)
This route allow users to add a new withdrawal bank.
Allowed networks are: "SWIFT", "SEPA", "WIRE" and "BLINC".
"BLINC" banks only require two fields: "beneficiaryName" and "walletAddress".
"SWIFT", "SEPA" and "WIRE" required fields are described below. Second address lines and state/regions are optional for bank and beneficiary in addition to the whole intermediary info.
HTTP Request
POST /accounts/:accountId/banks
Body Parameters
| Parameter | Type | Description |
|---|---|---|
| networkId | string | Network ID or name |
| assetId | string | The asset to be withdrawn on this bank. |
| beneficiaryName | string | The name of the bank account's beneficiary (can be null depending on the network used) |
| beneficiaryAddress1 | string | The first address line of the bank account's beneficiary (can be null depending on the network used) |
| beneficiaryAddress2 | string | optional The second address line of the bank account's beneficiary (can be null depending on the network used) |
| beneficiaryPostcode | string | The postcode of the bank account's beneficiary (can be null depending on the network used) |
| beneficiaryCity | string | The city of the bank account's beneficiary (can be null depending on the network used) |
| beneficiaryRegion | string | optional The state/region of the bank account's beneficiary (can be null depending on the network used) |
| beneficiaryCountry | string | The country of the bank account's beneficiary (can be null depending on the network used) |
| bankName | string | The name of the bank account (can be null depending on the network used) |
| bankAddress1 | string | The first address line of the bank account (can be null depending on the network used) |
| bankAddress2 | string | optional The second address of the bank account (can be null depending on the network used) |
| bankPostcode | string | The postcode of the bank account (can be null depending on the network used) |
| bankCity | string | The city of the bank account (can be null depending on the network used) |
| bankRegion | string | optional The state/region of the bank account (can be null depending on the network used) |
| bankCountry | string | The country of the bank account (can be null depending on the network used) |
| bankId | string | BIC/SWIFT code for "SEPA" and "SWIFT" networks, ABA routing number for "WIRE" network (can be null depending on the network used) |
| bankAccountNumber | string | IBAN for "SEPA", "SWIFT" and "WIRE" networks, can also be Account number for "WIRE" (can be null depending on the network used) |
| isIntermediary | boolean | optional Whether this bank account uses an intermediary bank account |
| intermediaryBankId | string | optional The intermediary bank Id: BIC/SWIFT code for "SEPA" and "SWIFT" networks, ABA routing number for "WIRE" network (null if "isIntermediary" is false) |
| walletAddress | string | The wallet address of the bank account (only used for "BLINC" network) |
| description | string | Human readable name for this address |
| virtualAccountId | number | optional Virtual account id this address is for (null indicates the "Main Account") |
Response fields
See this route for details about the returned body.
Approve the whitelisting of a bank account
import requests
endpoint = '%s/accounts/%s/banks/%s/approve' % (base, accountId, bankId)
body = {
"approved": true,
}
response = requests.patch(endpoint, headers=headers, json=body)
This route allow users to approve or reject the whitelisting process of a bank account.
HTTP Request
PATCH /accounts/:accountId/banks/:bankId/approve
Body Parameters
| Parameter | Type | Description |
|---|---|---|
| approve | boolean | Whether to approve or reject the whitelisting of the bank |
Response fields
See this route for details about the returned body.
Modify a bank account
import requests
endpoint = '%s/accounts/%s/banks/%s' % (base, accountId, bankId)
body = {
"description": "Actually I prefer this name"
"isArchived": true,
}
response = requests.patch(endpoint, headers=headers)
This route allow users to modify a bank account.
HTTP Request
PATCH /accounts/:accountId/banks/:bankId
Body Parameters
| Parameter | Type | Description |
|---|---|---|
| description | string | optional Human readable name for this bank account |
| isArchived | boolean | optional Whether or not the bank is usable for withdrawals |
Response fields
See this route for details about the returned body.
Update assets for a bank account
import requests
endpoint = '%s/accounts/%s/banks/%s/assets' % (base, accountId, bankId)
body = {
"assets": [
"EUR",
]
}
response = requests.put(endpoint, headers=headers, json=body)
This route allow users to update all assets for a bank account, making it possible for the specified assets to be withdrawn on this bank account.
The provided assets must be available on the network the bank account uses.
HTTP Request
PUT /accounts/:accountId/banks/:bankId/assets
Get Internal Banks
import requests
endpoint = '%s/accounts/%s/banks/internal' % (base, accountId)
response = requests.get(endpoint, headers=headers)
This route allows users to retrieve Aplo's bank accounts for FIAT deposit and withdrawal purposes.
HTTP Request
GET /accounts/:accountId/banks/internal
Response fields
Example of JSON structure returned by the request:
[
{
"depositFixedFee": "5",
"depositVariableFeeRate": "0.005",
"depositMinFee": "10",
"depositMaxFee": "40",
"withdrawalFixedFee": "10",
"withdrawalVariableFeeRate": "0.01",
"withdrawalMinFee": "20",
"withdrawalMaxFee": "100",
"id": "2c83d612-38cf-457d-8a59-f3a56e108986",
"description": "Deposit Bank account SEPA",
"priority": 1,
"countryWhitelist": "FR GB US",
"countryBlacklist": null,
"beneficiaryName": "Aplo",
"beneficiaryAddress1": "15 rue des Allités",
"beneficiaryAddress2": null,
"beneficiaryPostcode": "75001",
"beneficiaryCity": "Paris",
"beneficiaryRegion": null,
"beneficiaryCountry": "FR",
"bankName": "NEO PAYMENT FACTORY S.L.",
"bankAddress1": "C. Comte Urgell 143",
"bankAddress2": null,
"bankPostcode": "31000",
"bankCity": "Toulouse",
"bankRegion": null,
"bankCountry": "FR",
"bankId": "ZH02CITI00001077181611",
"bankAccountNumber": "98DNQ",
"isIntermediary": false,
"intermediaryBankId": null,
"walletAddress": null,
"isActive": true,
"networkId": "SEPA",
"createdAt": "2025-07-02T22:00:00.000Z",
"expectedTime": "2 days",
"assetId": "EUR"
}
]
The response is a JSON object with the following fields:
| Field | Type | Description |
|---|---|---|
| depositFixedFee | number | Deposit fixed fee. Can be null. |
| depositVariableFeeRate | number | Deposit variable fee. Can be null. |
| depositMinFee | number | Deposit minimum fee. Can be null. |
| depositMaxFee | number | Deposit maximum fee. Can be null. |
| withdrawalFixedFee | number | Withdrawal fixed fee. Can be null. |
| withdrawalVariableFeeRate | number | Withdrawal variable fee. Can be null. |
| withdrawalMinFee | number | Withdrawal minimum fee. Can be null. |
| withdrawalMaxFee | number | Withdrawal maximum fee. Can be null. |
| id | string | UUID, used for FIAT withdrawals' sourceBankId |
| description | string | Human readable description. |
| priority | number | Used to determine which bank to use when sourceBankId is null for FIAT withdrawals. |
| countryWhitelist | string | Bank account country whitelist (overrides blacklist if set). Can be null. |
| countryBlacklist | string | Bank account country blacklist. Can be null. |
| beneficiaryName | string | The name of the bank account's beneficiary (can be null depending on the network used) |
| beneficiaryAddress1 | string | The first address line of the bank account's beneficiary (can be null depending on the network used) |
| beneficiaryAddress2 | string | The second address line of the bank account's beneficiary (can be null depending on the network used) |
| beneficiaryPostcode | string | The postcode of the bank account's beneficiary (can be null depending on the network used) |
| beneficiaryCity | string | The city of the bank account's beneficiary (can be null depending on the network used) |
| beneficiaryRegion | string | The state/region of the bank account's beneficiary (can be null depending on the network used) |
| beneficiaryCountry | string | The country of the bank account's beneficiary (can be null depending on the network used) |
| bankName | string | The name of the bank account (can be null depending on the network used) |
| bankAddress1 | string | The first address line of the bank account (can be null depending on the network used) |
| bankAddress2 | string | The second address of the bank account (can be null depending on the network used) |
| bankPostcode | string | The postcode of the bank account (can be null depending on the network used) |
| bankCity | string | The city of the bank account (can be null depending on the network used) |
| bankRegion | string | The state/region of the bank account (can be null depending on the network used) |
| bankCountry | string | The country of the bank account (can be null depending on the network used) |
| bankId | string | BIC/SWIFT code for "SEPA" and "SWIFT" networks, ABA routing number for "WIRE" network (can be null depending on the network used) |
| bankAccountNumber | string | IBAN for "SEPA", "SWIFT" and "WIRE" networks, can also be Account number for "WIRE" (can be null depending on the network used) |
| isIntermediary | boolean | Whether this bank account uses an intermediary bank account |
| intermediaryBankId | string | The intermediary bank Id: BIC/SWIFT code for "SEPA" and "SWIFT" networks, ABA routing number for "WIRE" network (null if "isIntermediary" is false) |
| walletAddress | string | The wallet address of the bank account (only used for "BLINC" network) |
| networkId | string | The bank network |
| createdAt | date | Creation date |
| expectedTime | string | Expected duration of payment processing |
| assetId | string | Asset supported by this bank |
Get Deposit Destinations
import requests
endpoint = '%s/accounts/%s/deposit-destinations' % (base, accountId)
response = requests.get(endpoint, headers=headers)
This route allows market participants to retrieve the deposit addresses for the specified Account.
HTTP Request
GET /accounts/:accountId/deposit-destinations
This route can take the following optional query parameters:
| Parameter | Type | Description |
|---|---|---|
| virtualAccountId | number | Specific virtual account to retrieve destinations for |
Response fields
Example of JSON structure returned by the request:
[
{
"minWithdrawalAmount": "1000",
"rate": "0.3636",
"strikePrice": null,
"id": "XRP",
"name": "XRP",
"description": "Ripple",
"classification": "S",
"logo": "xrp.svg",
"availableProgramTrading": true,
"underlyingId": null,
"currencyId": null,
"issuingVenue": null,
"marginId": null,
"expiration": null,
"durationCode": null,
"internalAddresses": [
{
"id": "016bc6ed-20ef-4f89-9261-929c6204beab",
"value": "fdb018e9-39c1-49b9-bbef-35029f67ea3c",
"memo": null,
"description": "Deposit address Ripple",
"accountId": "2e530764-9228-437e-a62f-a049e6400269",
"isActive": true,
"virtualAccountId": null,
"createdAt": "2022-12-15T10:38:02.575Z",
"networkId": "XRP"
}
]
}
]
The response is a JSON object with the following fields:
| Field | Type | Description |
|---|---|---|
| minWithdrawalAmount | string | Minimum amount to withdraw |
| rate | string | Current USD price of the asset |
| id | string | Asset ASCII id identifier |
| name | string | Asset ASCII name identifier |
| internalAddresses | array | Array of internal address object (see content below) |
The internalAddresses, are an array of internal address object with the following properties:
| Field | Type | Description |
|---|---|---|
| id | string | Internal address unique ID |
| networkId | string | Network id (see /networks) |
| value | string | Actual address value |
| description | string | Human readable name |
| memo | string | Memo/Tag for this address (can be null) |
| accountId | string | Address owner's account Id |
| virtualAccountId | number | The virtual account that this address is linked to (null indicates the "Main Account") |
| createdAt | Date | Date of creation |
To get deposit info for FIAT assets, please refer to the GET internal banks route
Get Withdrawal Destinations
import requests
endpoint = '%s/accounts/%s/withdrawal-destinations' % (base, accountId)
response = requests.get(endpoint, headers=headers)
This route allows market participants to retrieve the withdrawal addresses for the specified Account.
HTTP Request
GET /accounts/:accountId/withdrawal-destinations
This route can take the following optional query parameters:
| Parameter | Type | Description |
|---|---|---|
| virtualAccountId | number | Specific virtual account to retrieve destinations for |
Response fields
Example of JSON structure returned by the request:
[
{
"minWithdrawalAmount": "1000",
"rate": "0.3636",
"strikePrice": null,
"id": "XRP",
"name": "XRP",
"description": "Ripple",
"classification": "S",
"logo": "xrp.svg",
"availableProgramTrading": true,
"underlyingId": null,
"currencyId": null,
"issuingVenue": null,
"marginId": null,
"expiration": null,
"durationCode": null,
"externalAddresses": [
{
"id": "ab34bc89-fdd8-42d2-8f11-d20f3c0e8762",
"value": "e8309358-483d-4d03-a953-c66318679add",
"memo": null,
"description": "Withdrawal address Bitcoin",
"accountId": "2e530764-9228-437e-a62f-a049e6400269",
"isActive": true,
"isWhitelisted": true,
"isArchived": false,
"virtualAccountId": null,
"whitelistedAt": "2022-07-29T08:35:10.297Z",
"whitelistValidators": [
{
"name": "User One",
"time": 1659083710297,
"userId": "ab35e86e-8ce1-446e-b0d7-d33b1bd6b0bc",
"approved": true
}
],
"isSelfBeneficiary": false,
"isSelfHosted": false,
"beneficiaryCountry": null,
"beneficiaryAddress": null,
"beneficiaryRelationship": null,
"walletPurpose": null,
"providerName": null,
"providerWebsite": null,
"providerCountry": null,
"createdAt": "2023-11-24T13:30:55.261Z",
"whitelistingPolicy": null,
"userId": null,
"name": "Withdrawal address Bitcoin",
"isUniversal": false,
"providerId": null,
"beneficiaryLegalName": null,
"beneficiaryFirstName": null,
"beneficiaryLastName": null,
"paymentReason": null,
"beneficiaryActivity": null,
"beneficiaryTaxId": null,
"beneficiaryContactEmail": null,
"networkId": "BTC"
}
],
"externalBanks": [],
},
{
"minWithdrawalAmount": "100",
"rate": "1",
"id": "USD",
"name": "US Dollar",
"externalAddresses": [],
"externalBanks": [
{
"id": "17ddab40-34c1-451b-81bb-08fcadda384d",
"description": "Withdrawal Bank account SWIFT",
"beneficiaryName": "SheeldTrading 1",
"beneficiaryAddress1": "15 rue des Allités",
"beneficiaryAddress2": null,
"beneficiaryPostcode": "75001",
"beneficiaryCity": "Paris",
"beneficiaryRegion": null,
"beneficiaryCountry": "FR",
"bankName": "NEO PAYMENT FACTORY S.L.",
"bankAddress1": "C. Comte Urgell 143",
"bankAddress2": null,
"bankPostcode": "31000",
"bankCity": "Toulouse",
"bankRegion": null,
"networkId": "SWIFT"
...
},
{
"id": "c2d16c3d-3d05-4c8f-b8ef-5495f3eb506f",
"description": "Withdrawal Bank account BLINC",
"beneficiaryName": "SheeldTrading 1",
"walletAddress": "97DNQZQOZDZQ",
"networkId": "BLINC"
...
}
],
}
]
The response is a JSON object with the following fields:
| Field | Type | Description |
|---|---|---|
| minWithdrawalAmount | string | Minimum amount to withdraw |
| rate | string | Current USD price of the asset |
| id | string | Asset ASCII id identifier |
| name | string | Asset ASCII name identifier |
| externalAddresses | array | Array of external address (see content below) |
| externalBanks | array | Array of withdrawal banks (see content below) |
For externalAddresses, see the GET /address route.
For externalBanks, see the GET /banks route.
Request a crypto withdrawal
import requests
endpoint = '%s/accounts/%s/withdrawal-requests' % (base, accountId)
body = {
"assetId": "BTC",
"address": "98Dj-NLC86jd:42Lxzk9ldq:5143",
"quantity": "100",
"description": "My note",
"includeFees": true,
"virtualAccountId": null
}
response = requests.post(endpoint, headers=headers, json=body)
This route allows market participants to request a crypto withdrawal. The assets will be withdrawn from the Funding Account towards an external address.
HTTP Request
POST /accounts/:accountId/withdrawal-requests
Body Parameters
| Parameter | Type | Description |
|---|---|---|
| assetId | string | Asset ID |
| address | string | Destination address value |
| memo | string | optional Destination address memo |
| networkId | string | optional Destination address network id |
| quantity | string | Withdrawal request volume |
| description | string | Custom note |
| includeFees | boolean | Whether fees are included in the quantity withdrawn or not |
| virtualAccountId | number | optional Virtual account id requesting the withdrawal (null indicates the "Main Account") |
Response fields
Example of JSON structure returned by the request:
{
"id": "87ccf43f-225e-4c77-9e32-7fdc4693e870",
"assetId": "BTC",
"destinationId": "732bb9f6-cca7-45da-aaef-225e5d79b81e",
"destinationType": "externalAddress",
"quantity": "100",
"description": "My note",
"accountId": "45cabb50-42f7-4a69-a534-db030882f34c",
"status": "requested",
"includeFees": true,
"networkFeesQuantity": "0.1",
"serviceFeesQuantity": "0.01",
"networkFeesNotional": "5000",
"serviceFeesNotional": "500",
"virtualAccountId": null
}
The response is a JSON object with the following fields:
| Field | Type | Description |
|---|---|---|
| id | string | Withdrawal request UUID |
| assetId | string | Asset ID |
| destinationId | string | Destination address UUID |
| destinationType | string | "extenralAddress" |
| quantity | string | Withdrawal request volume |
| description | string | Custom note |
| accountId | string | Account UUID |
| status | string | Withdrawal request status |
| virtualAccountId | number | Virtual account id (null indicates the "Main Account") |
| includeFees | boolean | Whether fees are included in the amount withdrawn |
| networkFeesQuantity | number | Network fees in kind. (used if includeFees is "true") |
| networkFeesNotional | number | Network fees in valuation currency. (used if includeFees is "false") |
| serviceFeesQuantity | number | Service fees in kind. (used if includeFees is "true") |
| serviceFeesNotional | number | Service fees in valuation currency. (used if includeFees is "false") |
Request a BLINC withdrawal
import requests
endpoint = '%s/accounts/%s/withdrawal-requests' % (base, accountId)
body = {
"assetId": "USD",
"networkId": "BLINC",
"walletAddress": "978DNQZ3FQKJ",
"sourceBankId": "f48e40c0-aa41-56c4-9218-7f55b5ad6a11",
"quantity": "100",
"description": "My note",
"includeFees": true,
"virtualAccountId": null
}
response = requests.post(endpoint, headers=headers, json=body)
This route allows market participants to request a withdrawal on a "BLINC" bank account.
The assets will be withdrawn from the Funding Account towards an external bank account.
It is possible to use your EURC balance to withdraw to a bank account that handles EUR. Same logic applies to USDC and USD.
HTTP Request
POST /accounts/:accountId/withdrawal-requests
Body Parameters
| Parameter | Type | Description |
|---|---|---|
| assetId | string | Asset ID |
| walletAddress | string | Destination bank account walletAddress (as retrieved in the /banks route) |
| sourceBankId | string | Aplo's bank account the funds will be sent from. (as retrieved in the GET /banks/internal route). Can be null in which case, the source bank is chosen automatically. |
| networkId | string | "BLINC" |
| quantity | string | Withdrawal request volume |
| description | string | Custom note |
| includeFees | boolean | Whether fees are included in the quantity withdrawn or not |
| virtualAccountId | number | optional Virtual account id requesting the withdrawal (null indicates the "Main Account") |
Response fields
Example of JSON structure returned by the request:
{
"id": "a101e8b5-388f-56b3-ada7-29e2b2b464eb",
"assetId": "USD",
"conversionAssetId": "USDC",
"destinationId": "3534296d-356b-5863-8db1-90ae3d833c3c",
"destinationType": "externalBank",
"quantity": "100000",
"description": "My note",
"accountId": "45cabb50-42f7-4a69-a534-db030882f34c",
"status": "requested",
"includeFees": true,
"networkFeesQuantity": "0.1",
"serviceFeesQuantity": "0.01",
"networkFeesNotional": "5000",
"serviceFeesNotional": "500",
"virtualAccountId": null
}
The response is a JSON object with the following fields:
| Field | Type | Description |
|---|---|---|
| id | string | Withdrawal request UUID |
| assetId | string | Asset ID |
| conversionAssetId | string | The asset that was converted to assetId before being withdrawn (USDC -> USD and EURC -> EUR), will be null for withdrawals without conversion |
| destinationId | string | Destination bank UUID |
| destinationType | string | "externalBank" |
| quantity | string | Withdrawal request volume |
| description | string | Custom note |
| accountId | string | Account UUID |
| status | string | Withdrawal request status |
| virtualAccountId | number | Virtual account id (null indicates the "Main Account") |
| includeFees | boolean | Whether fees are included in the amount withdrawn |
| networkFeesQuantity | number | Network fees in kind. (used if includeFees is "true") |
| networkFeesNotional | number | Network fees in valuation currency. (used if includeFees is "false") |
| serviceFeesQuantity | number | Service fees in kind. (used if includeFees is "true") |
| serviceFeesNotional | number | Service fees in valuation currency. (used if includeFees is "false") |
Request a SWIFT, SEPA or WIRE withdrawal
import requests
endpoint = '%s/accounts/%s/withdrawal-requests' % (base, accountId)
body = {
"assetId": "EUR",
"networkId": "SEPA",
"sourceBankId": "972b1d9c-4261-50f2-9e09-20b87ef18226",
"bankId": "978DNQZ3FQKJ",
"bankAccountNumber": "IK877DY1NJDQZ",
"quantity": "100",
"description": "My note",
"includeFees": true,
"virtualAccountId": null
}
response = requests.post(endpoint, headers=headers, json=body)
This route allows market participants to request a withdrawal on a "SWIFT", "SEPA" or "WIRE" bank account.
The assets will be withdrawn from the Funding Account towards an external bank account.
It is possible to use your EURC balance to withdraw to a bank account that handles EUR. Same logic applies to USDC and USD.
HTTP Request
POST /accounts/:accountId/withdrawal-requests
Body Parameters
| Parameter | Type | Description |
|---|---|---|
| assetId | string | Asset ID |
| bankId | string | Destination bank account bankId (BIC/SWIFT/ABA Routing Number) (as retrieved in the /banks route or /withdrawal-destinations route) |
| bankAccountNumber | string | Destination bank account bankAccountNumber (IBAN/Account number) (as retrieved in the /banks route or /withdrawal-destinations route) |
| sourceBankId | string | Aplo's bank account the funds will be sent from. (as retrieved in the GET /banks/internal route). Can be null in which case, the source bank is chosen automatically. |
| networkId | string | "SWIFT", "SEPA" or "WIRE" |
| quantity | string | Withdrawal request volume |
| description | string | Custom note |
| includeFees | boolean | Whether fees are included in the quantity withdrawn or not |
| virtualAccountId | number | optional Virtual account id requesting the withdrawal (null indicates the "Main Account") |
Response fields
Example of JSON structure returned by the request:
{
"id": "22e87620-e1a1-58bf-8c14-f4c0cc8695c4",
"assetId": "EUR",
"conversionAssetId": null,
"destinationId": "12a79474-586f-5db4-8d36-4b123155746a",
"destinationType": "externalBank",
"quantity": "100",
"description": "My note",
"accountId": "45cabb50-42f7-4a69-a534-db030882f34c",
"status": "requested",
"includeFees": true,
"networkFeesQuantity": "0.1",
"serviceFeesQuantity": "0.01",
"networkFeesNotional": "5000",
"serviceFeesNotional": "500",
"virtualAccountId": null
}
The response is a JSON object with the following fields:
| Field | Type | Description |
|---|---|---|
| id | string | Withdrawal request UUID |
| assetId | string | Asset ID |
| conversionAssetId | string | The asset that was converted to assetId before being withdrawn (USDC -> USD and EURC -> EUR), will be null for withdrawals without conversion |
| destinationId | string | Destination bank UUID |
| destinationType | string | "externalBank" |
| quantity | string | Withdrawal request volume |
| description | string | Custom note |
| accountId | string | Account UUID |
| status | string | Withdrawal request status |
| virtualAccountId | number | Virtual account id (null indicates the "Main Account") |
| includeFees | boolean | Whether fees are included in the amount withdrawn |
| networkFeesQuantity | number | Network fees in kind. (used if includeFees is "true") |
| networkFeesNotional | number | Network fees in valuation currency. (used if includeFees is "false") |
| serviceFeesQuantity | number | Service fees in kind. (used if includeFees is "true") |
| serviceFeesNotional | number | Service fees in valuation currency. (used if includeFees is "false") |
Get Withdrawal Requests
import requests
endpoint = '%s/accounts/%s/fund-history?type=withdrawal&status=requested&page=%s&pageSize=%d' % (base, accountId, page, pageSize)
response = requests.get(endpoint, headers=headers)
This route allows accounts to retrieve their withdrawal requests history.
HTTP Request
GET /accounts/:accountId/fund-history?type=withdrawal&status=requested
This route can take the following optional query parameters:
| Parameter | Type | Description |
|---|---|---|
| virtualAccountId | number | Specific virtual account to retrieve withdrawal requests for |
| start | number or string | inclusive Can be either a Unix timestamp (in seconds), a JS timestamp (in milliseconds), or a string date with format YYYY-MM-DD HH:mm:ss or YYYY-MM-DDTHH:mm:ss.SSSZ |
| end | number or string | inclusive Can be either a Unix timestamp (in seconds), a JS timestamp (in milliseconds), or a string date with format YYYY-MM-DD HH:mm:ss or YYYY-MM-DDTHH:mm:ss.SSSZ |
| status | string | Status filter: must be "complete", "processing", "canceled" or "frozen" |
| assetId | string | Asset filter |
Response fields
Example of JSON structure returned by the request:
{
"data": [
{
"quantity": "1.868565",
"sourceVirtualAccountId": "1138967864",
"destinationVirtualAccountId": "1138967864",
"id": "5a83f67f-aebd-4e56-99df-de5237645b41",
"assetId": "BTC",
"type": "withdrawal",
"accountId": "2e530764-9228-437e-a62f-a049e6400269",
"description": null,
"status": "requested",
"createdAt": "2022-07-29T08:52:15.887Z",
"details": {
"networkFeesQuantity": "0.0005",
"serviceFeesQuantity": "0.000935",
"networkFeesNotional": "11.97",
"serviceFeesNotional": "22.39",
"userId": "ab35e86e-8ce1-446e-b0d7-d33b1bd6b0bc",
"destinationId": "3cc28095-4f3b-4203-a4b2-afd1c731a790",
"destinationType": "externalAddress",
"paymentId": null,
"status": "requested",
"selectedPolicy": null,
"validatedBy": [],
"includeFees": true,
"chainalysisId": null,
"kytValidated": null,
"virtualAccountId": 1138967864,
"networkId": "BTC",
"payment": null,
"externalAddress": {
"id": "ab34bc89-fdd8-42d2-8f11-d20f3c0e8762",
"value": "e8309358-483d-4d03-a953-c66318679add",
"memo": null,
"description": "Withdrawal address Bitcoin",
"accountId": "2e530764-9228-437e-a62f-a049e6400269",
"isActive": true,
"isWhitelisted": true,
"isArchived": false,
"virtualAccountId": null,
"whitelistedAt": "2022-07-29T08:35:10.297Z",
"whitelistValidators": [
{
"name": "User One",
"time": 1659083710297,
"userId": "ab35e86e-8ce1-446e-b0d7-d33b1bd6b0bc",
"approved": true
}
],
"isSelfBeneficiary": false,
"isSelfHosted": false,
"beneficiaryCountry": null,
"beneficiaryAddress": null,
"beneficiaryRelationship": null,
"walletPurpose": null,
"providerName": null,
"providerWebsite": null,
"providerCountry": null,
"createdAt": "2023-11-24T13:30:55.261Z",
"whitelistingPolicy": null,
"userId": null,
"name": "Withdrawal address Bitcoin",
"isUniversal": false,
"providerId": null,
"beneficiaryLegalName": null,
"beneficiaryFirstName": null,
"beneficiaryLastName": null,
"paymentReason": null,
"beneficiaryActivity": null,
"beneficiaryTaxId": null,
"beneficiaryContactEmail": null,
"networkId": "BTC"
},
"externalBank": null,
"createdBy": {
"id": "ab35e86e-8ce1-446e-b0d7-d33b1bd6b0bc",
"firstname": "User",
"lastname": "One",
"email": "usr1@sheeldmarket.com"
}
}
}
...
],
"count": 3
}
Where data is a JSON array of object with the following fields:
| Field | Type | Description |
|---|---|---|
| id | string | Withdrawal request UUID |
| quantity | string | Withdrawal volume |
| sourceVirtualAccountId | number | Virtual account id (null indicates the "Main Account") |
| destinationVirtualAccountId | number | Same as the sourceVirtualAccountId |
| assetId | string | Asset identifier |
| type | string | "withdrawal" |
| accountId | string | Account UUID |
| description | string | null |
| status | string | Status of the withdrawal, can be "pendingPeerApproval", "rejectedPolicy", "rejectedPeer", "requested", "processing", "approved", "frozen", "rejectedSys" or "pendingInvestigation", |
| createdAt | date | Date of withdrawal |
| details | object | Withdrawal details (see below) |
details is an object with the following properties:
| Field | Type | Description |
|---|---|---|
| networkFeesQuantity | string | Network fees (in kind) |
| serviceFeesQuantity | string | Service fees (in kind) |
| networkFeesNotional | string | Network fees (in USD) |
| serviceFeesNotional | string | Service fees (in USD) |
| userId | string | UUID of the User that made this request (most likely: you) |
| destinationId | string | Either the UUID of the destination address or the destination bank (depending on the value of destinationType) for this withdrawal |
| destinationType | string | "externalAddress" or "externalBank" |
| paymentId | string | null since this withdrawal has yet to be completed |
| status | string | Status of the withdrawal, can be "pendingPeerApproval", "rejectedPolicy", "rejectedPeer", "requested", "processing", "approved", "frozen", "rejectedSys" or "pendingInvestigation", |
| selectedPolicy | object | The selected policy regarding governed withdrawal requests or null if none were used |
| validatedBy | array | List of users who have approved/rejected this withdrawal request (only used for governed withdrawal requests) |
| includeFees | boolean | Whether or not fees were included in the withdrawn amount (false means notional fees will be charged on the account's "billing" sub account, true means the fees are directly deducted from the quantity withdrawn) |
| chainalysisId | string | Potential Chainalysis Id (can be null) |
| kytValidated | boolean | Whether Aplo's KYT has been validated for this request |
| virtualAccountId | number | Virtual account Id (null indicates the "Main Account") |
| networkId | string | Network id (see /networks) |
| payment | object | null since this request has yet to be completed |
| externalAddress | object | The details about the address used to make the withdrawal (see this route for the complete properties) (null for fiat withdrawals) |
| externalBank | object | The details about the bank used to make the withdrawal (see this route for the complete properties) (null for non-fiat withdrawals) |
| createdBy | object | Details about the user that requested this request |
Get Deposits History
import requests
endpoint = '%s/accounts/%s/fund-history?type=deposit&page=%s&pageSize=%d' % (base, accountId, page, pageSize)
response = requests.get(endpoint, headers=headers)
This route allows accounts to retrieve theirs deposit history.
HTTP Request
GET /accounts/:accountId/fund-history?type=deposit
This route can take the following optional query parameters:
| Parameter | Type | Description |
|---|---|---|
| virtualAccountId | number | Specific virtual account to retrieve withdrawal requests for |
| start | number or string | inclusive Can be either a Unix timestamp (in seconds), a JS timestamp (in milliseconds), or a string date with format YYYY-MM-DD HH:mm:ss or YYYY-MM-DDTHH:mm:ss.SSSZ |
| end | number or string | inclusive Can be either a Unix timestamp (in seconds), a JS timestamp (in milliseconds), or a string date with format YYYY-MM-DD HH:mm:ss or YYYY-MM-DDTHH:mm:ss.SSSZ |
| status | string | Status filter: must be "complete", "processing", "canceled" or "frozen" |
| assetId | string | Asset filter |
Response fields
Example of JSON structure returned by the request:
{
"data": [
{
"quantity": "50",
"sourceVirtualAccountId": "1138967864",
"destinationVirtualAccountId": "1138967864",
"id": "3ee6e336-b557-446d-afb2-a3fc6240e625",
"assetId": "ETH",
"type": "deposit",
"accountId": "2e530764-9228-437e-a62f-a049e6400269",
"description": "ETH deposit",
"status": "complete",
"createdAt": "2022-07-25T14:54:28.512Z",
"details": {
"networkFees": "0",
"serviceFees": "0",
"txId": "b5993828-eb81-49dd-adcc-f51feeb539a2",
"type": "deposit",
"status": "complete",
"chainalysisId": null,
"kytValidated": null,
"virtualAccountId": 1138967864,
"networkId": "ETH",
"notional": "25000",
"sourceType": "externalAddress",
"sourceId": null,
"destinationType": "internalAddress",
"destinationId": "d6182c41-fdc6-4f0b-a2df-c98adcf5d849",
"feesAssetId": "ETH",
"sourceExternalAddress": null,
"destinationInternalAddress": {
"id": "d6182c41-fdc6-4f0b-a2df-c98adcf5d849",
"value": "cad245f5-8ed1-4d27-bcd3-c416e87c923f",
"memo": null,
"description": "Virtual Withdrawal address Ethereum usr1+virtual1@sheeldmarket.com",
"accountId": "2e530764-9228-437e-a62f-a049e6400269",
"virtualAccountId": 1138967864,
"createdAt": "2022-07-25T14:54:28.512Z",
"networkId": "ETH"
},
"sourceExternalBank": null,
"destinationInternalBank": null
}
}
...
],
"count": 3
}
Where data is a JSON array of object with the following fields:
| Field | Type | Description |
|---|---|---|
| quantity | string | Withdrawal volume |
| sourceVirtualAccountId | number | Virtual account id (null indicates the "Main Account") |
| destinationVirtualAccountId | number | Same as the sourceVirtualAccountId |
| id | string | UUID of the deposit payment |
| assetId | string | Asset identifier |
| type | string | "deposit" |
| accountId | string | Account UUID |
| description | string | Human readable description |
| status | string | "sent","error","processing","pendingInvestigation","pendingReview","complete" or "frozen" |
| createdAt | date | Date of deposit |
| details | object | Deposit details (see below) |
details is an object with the following properties:
| Field | Type | Description |
|---|---|---|
| networkFees | string | Amount of network fees |
| serviceFees | string | Amount of service fees |
| feesAssetId | string | Asset of the fees (either in kind or USD) |
| id | string | UUID of the payment linked to the deposit |
| txId | string | Transaction Hash |
| description | string | Human readable description |
| type | string | "deposit" |
| status | string | "sent","error","processing","pendingInvestigation","pendingReview","complete" or "frozen" |
| chainalysisId | string | The optional Chainalysis Id |
| kytValidated | boolean | Whether the KYT has been validated on Aplo's end |
| virtualAccountId | number | Virtual account Id (null indicates the "Main Account") |
| networkId | string | Network id (see /networks) |
| notional | string | Notional value of the deposit payment |
| sourceType | string | "externalAddress" for crypto deposits, or "externalBank" for fiat deposits |
| sourceId | string | Source Id (can be null) |
| destinationType | string | "internalAddress" for crypto deposits, or "internalBank" for fiat deposits |
| destinationId | string | Destination Id |
| sourceExternalAddress | object | If sourceType = "externalAddress", the destination external address, otherwise null (see this route for the complete properties) |
| destinationInternalAddress | object | If destinationType = "internalAddress", the destination internal address, otherwise null (see this route for the complete properties) |
| sourceExternalBank | object | If sourceType = "externalBank", the source external bank, otherwise null (see this route for the complete properties) |
| destinationInternalBank | object | If destinationType = "internalBank", the destination internal bank, otherwise null (see this route for the complete properties) |
Fiat deposits have an sourceExternalBank and a destinationInternalBank.
Get Withdrawals History
import requests
endpoint = '%s/accounts/%s/fund-history?type=withdrawal&status=complete&page=%s&pageSize=%d' % (base, accountId, page, pageSize)
response = requests.get(endpoint, headers=headers)
This route allows accounts to retrieve their withdrawal history (not to be confused with the history for withdrawal requests).
HTTP Request
GET /accounts/:accountId/fund-history?type=withdrawal&status=complete
This route can take the following optional query parameters:
| Parameter | Type | Description |
|---|---|---|
| virtualAccountId | number | Specific virtual account to retrieve withdrawal requests for |
| start | number or string | inclusive Can be either a Unix timestamp (in seconds), a JS timestamp (in milliseconds), or a string date with format YYYY-MM-DD HH:mm:ss or YYYY-MM-DDTHH:mm:ss.SSSZ |
| end | number or string | inclusive Can be either a Unix timestamp (in seconds), a JS timestamp (in milliseconds), or a string date with format YYYY-MM-DD HH:mm:ss or YYYY-MM-DDTHH:mm:ss.SSSZ |
| status | string | Status filter: must be "complete", "processing", "canceled" or "frozen" |
| assetId | string | Asset filter |
Response fields
Example of JSON structure returned by the request:
{
"data": [
{
"quantity": "1.868565",
"sourceVirtualAccountId": "1138967864",
"destinationVirtualAccountId": "1138967864",
"id": "65cc1c14-5e1b-4b54-82fa-f47460479718",
"assetId": "BTC",
"type": "withdrawal",
"accountId": "2e530764-9228-437e-a62f-a049e6400269",
"description": null,
"status": "approved",
"createdAt": "2022-08-01T09:14:59.536Z",
"details": {
"networkFeesQuantity": "0.0005",
"serviceFeesQuantity": "0.000935",
"networkFeesNotional": "11.66",
"serviceFeesNotional": "21.81",
"userId": "ab35e86e-8ce1-446e-b0d7-d33b1bd6b0bc",
"destinationId": "3cc28095-4f3b-4203-a4b2-afd1c731a790",
"destinationType": "externalAddress",
"paymentId": "56e2b7bf-2740-4397-9c69-21f190338629",
"status": "approved",
"selectedPolicy": null,
"validatedBy": [],
"includeFees": true,
"chainalysisId": null,
"kytValidated": null,
"virtualAccountId": 1138967864,
"networkId": "BTC",
"payment": {
"quantity": "1.868565",
"networkFees": "0.0005",
"serviceFees": "0.000935",
"id": "56e2b7bf-2740-4397-9c69-21f190338629",
"txId": "9787YJLPDGUIQD09",
"description": "Monthly withdrawal",
"destinationType": "externalAddress",
"type": "withdrawal",
"status": "complete",
"chainalysisId": null,
"kytValidated": null,
"virtualAccountId": 1138967864,
"networkId": "BTC",
"notional": "32082.42",
"createdAt": "2022-08-01T09:15:21.374Z",
"sourceId": "5a58dfbb-f686-4665-a7b8-f0e344bedd1c",
"destinationId": "3cc28095-4f3b-4203-a4b2-afd1c731a790",
"assetId": "BTC",
"feesAssetId": "BTC",
"accountId": "2e530764-9228-437e-a62f-a049e6400269",
"destinationExternalAddress": {
"id": "ab34bc89-fdd8-42d2-8f11-d20f3c0e8762",
"value": "e8309358-483d-4d03-a953-c66318679add",
"memo": null,
"description": "Withdrawal address Bitcoin",
"accountId": "2e530764-9228-437e-a62f-a049e6400269",
"isActive": true,
"isWhitelisted": true,
"isArchived": false,
"virtualAccountId": null,
"whitelistedAt": "2022-07-29T08:35:10.297Z",
"whitelistValidators": [
{
"name": "User One",
"time": 1659083710297,
"userId": "ab35e86e-8ce1-446e-b0d7-d33b1bd6b0bc",
"approved": true
}
],
"isSelfBeneficiary": false,
"isSelfHosted": false,
"beneficiaryCountry": null,
"beneficiaryAddress": null,
"beneficiaryRelationship": null,
"walletPurpose": null,
"providerName": null,
"providerWebsite": null,
"providerCountry": null,
"createdAt": "2023-11-24T13:30:55.261Z",
"whitelistingPolicy": null,
"userId": null,
"name": "Withdrawal address Bitcoin",
"isUniversal": false,
"providerId": null,
"beneficiaryLegalName": null,
"beneficiaryFirstName": null,
"beneficiaryLastName": null,
"paymentReason": null,
"beneficiaryActivity": null,
"beneficiaryTaxId": null,
"beneficiaryContactEmail": null,
"networkId": "BTC"
},
"sourceInternalAddress": null,
"destinationExternalBank": null,
"sourceInternalBank": null
},
"externalAddress": {
"id": "ab34bc89-fdd8-42d2-8f11-d20f3c0e8762",
"value": "e8309358-483d-4d03-a953-c66318679add",
"memo": null,
"description": "Withdrawal address Bitcoin",
"accountId": "2e530764-9228-437e-a62f-a049e6400269",
"isActive": true,
"isWhitelisted": true,
"isArchived": false,
"virtualAccountId": null,
"whitelistedAt": "2022-07-29T08:35:10.297Z",
"whitelistValidators": [
{
"name": "User One",
"time": 1659083710297,
"userId": "ab35e86e-8ce1-446e-b0d7-d33b1bd6b0bc",
"approved": true
}
],
"isSelfBeneficiary": false,
"isSelfHosted": false,
"beneficiaryCountry": null,
"beneficiaryAddress": null,
"beneficiaryRelationship": null,
"walletPurpose": null,
"providerName": null,
"providerWebsite": null,
"providerCountry": null,
"createdAt": "2023-11-24T13:30:55.261Z",
"whitelistingPolicy": null,
"userId": null,
"name": "Withdrawal address Bitcoin",
"isUniversal": false,
"providerId": null,
"beneficiaryLegalName": null,
"beneficiaryFirstName": null,
"beneficiaryLastName": null,
"paymentReason": null,
"beneficiaryActivity": null,
"beneficiaryTaxId": null,
"beneficiaryContactEmail": null,
"networkId": "BTC"
},
"externalBank": null,
"createdBy": {
"id": "ab35e86e-8ce1-446e-b0d7-d33b1bd6b0bc",
"firstname": "User",
"lastname": "One",
"email": "usr1@sheeldmarket.com"
}
}
}
...
],
"count": 3
}
The returned body is the same as the withdrawal request history route except details.payment has the following additional properties instead of being null:
| Field | Type | Description |
|---|---|---|
| quantity | string | The quantity withdrawn |
| assetId | string | Asset being withdrawn |
| networkFees | string | Amount of network fees |
| serviceFees | string | Amount of service fees |
| feesAssetId | string | Asset of the fees (either in kind or USD) |
| id | string | UUID of the payment linked to the withdrawal |
| txId | string | Transaction Hash |
| description | string | Human readable description |
| sourceType | string | null for crypto withdrawals, or "internalBank" for fiat withdrawals |
| destinationType | string | Either "externalAddress" or "externalBank" depending on the asset being withdrawn |
| type | string | "withdrawal" |
| status | string | "sent","error","processing","pendingInvestigation","pendingReview","complete" or "frozen" |
| chainalysisId | string | The optional Chainalysis Id |
| kytValidated | boolean | Whether the KYT has been validated on Aplo's end |
| virtualAccountId | number | Virtual account Id (null indicates the "Main Account") |
| networkId | string | Network id (see /networks) |
| notional | string | Notional value of the deposit payment |
| createdAt | date | Date of payment creation (ie. validation of withdrawal) |
| sourceId | string | Source Id |
| destinationId | string | Destination Id |
| accountId | string | Account UUID |
| destinationExternalAddress | object | If destinationType = "externalAddress", the destination external address, otherwise null (see this route for the complete properties) |
| sourceInternalAddress | object | If sourceType = "internalAddress", the source internal address, otherwise null (see this route for the complete properties) |
| destinationExternalBank | object | If destinationType = "externalBank", the destination external bank, otherwise null (see this route for the complete properties) |
| sourceInternalBank | object | If sourceType = "internalBank", the source internal bank, otherwise null (see this route for the complete properties) |
Fiat withdrawals have an sourceInternalBank and a destinationExternalBank.
Request a Transfer
import requests
endpoint = '%s/accounts/%s/transfer-requests' % (base, accountId)
body = {
"assetId": "BTC",
"sourceId": "DEM1",
"destinationId": "funding",
"sourceVirtualAccountId": null,
"destinationVirtualAccountId": 8742238924,
"quantity": "100",
"description": "My note",
}
response = requests.post(endpoint, headers=headers, json=body)
This route allows market participants to request a transfer.
You can transfer funds between different virtual accounts.
The assets will be withdrawn from source towards the destination.
HTTP Request
POST /accounts/:accountId/transfer-requests
Body Parameters
| Parameter | Type | Description |
|---|---|---|
| assetId | string | Asset ID |
| sourceId | string | Source Id: can be an MPID or "funding" |
| destinationId | string | Destination Id: can be an MPID or "funding" |
| quantity | string | Transfer request volume |
| description | string | Custom note |
| sourceVirtualAccountId | number | optional Source Virtual account id (null indicates the "Main Account") |
| destinationVirtualAccountId | number | optional Destination Virtual account id (null indicates the "Main Account") |
Response fields
Example of JSON structure returned by the request:
{
"id": "107849b8-40d2-5d56-82e1-9dee587b16a6",
"sourceId": "DEM1",
"destinationId": "funding",
"description": "My note",
"userId": "cf933712-4608-5989-bcc2-d71b15c7bad4",
"accountId": "d940fbba-13a1-5d30-9a1b-f59f4918122c",
"sourceVirtualAccountId": null,
"destinationVirtualAccountId": 8742238924,
"assetId": "BTC",
"status": "processing",
"quantity": "100",
"createdAt": "2020-08-07T15:42:47.138Z"
}
The response is a JSON object with the following fields:
| Field | Type | Description |
|---|---|---|
| id | string | Transfer request UUID |
| sourceId | string | Id of the source of the transfer (can be an MPID or "funding") |
| destinationId | string | Id of the destination of the transfer (can be an MPID or "funding") |
| userId | string | UUID of the User that made this request (most likely, you) |
| accountId | string | Account UUID |
| sourceVirtualAccountId | number | Source Virtual account id (null indicates the "Main Account") |
| destinationVirtualAccountId | number | Source Virtual account id (null indicates the "Main Account") |
| assetId | string | Asset identifier |
| status | string | Status of the transfer, can be "requested", "processing", "approved" or "rejected" |
| quantity | string | Transfer volume |
| createdAt | date | Date of transfer |
Get Transfers History
import requests
endpoint = '%s/accounts/%s/fund-history?type=transfer&page=%s&pageSize=%d' % (base, accountId, page, pageSize)
response = requests.get(endpoint, headers=headers)
This route allows accounts to retrieve theirs transfer requests history.
HTTP Request
GET /accounts/:accountId/fund-history?type=transfer
This route can take the following optional query parameters:
| Parameter | Type | Description |
|---|---|---|
| virtualAccountId | number | Specific virtual account to retrieve withdrawal requests for |
| start | number or string | inclusive Can be either a Unix timestamp (in seconds), a JS timestamp (in milliseconds), or a string date with format YYYY-MM-DD HH:mm:ss or YYYY-MM-DDTHH:mm:ss.SSSZ |
| end | number or string | inclusive Can be either a Unix timestamp (in seconds), a JS timestamp (in milliseconds), or a string date with format YYYY-MM-DD HH:mm:ss or YYYY-MM-DDTHH:mm:ss.SSSZ |
| status | string | Status filter: must be "complete", "processing", "canceled" or "frozen" |
| assetId | string | Asset filter |
Response fields
Example of JSON structure returned by the request:
{
"data": [
{
"id": "41e6a0cb-33e9-46ac-8c36-9cb6cb91ce9d",
"quantity": "8.234",
"sourceVirtualAccountId": null,
"destinationVirtualAccountId": 97842452,
"assetId": "ETH",
"type": "transfer",
"accountId": "2e530764-9228-437e-a62f-a049e6400269",
"description": null,
"status": "requested",
"createdAt": "2022-08-01T09:03:25.125Z",
"details": {
"destinationId": "SONE",
"destinationVirtualAccountId": 97842452,
"sourceId": "funding",
"sourceVirtualAccountId": null,
"userId": "ab35e86e-8ce1-446e-b0d7-d33b1bd6b0bc",
"status": "requested",
"createdBy": {
"id": "ab35e86e-8ce1-446e-b0d7-d33b1bd6b0bc",
"firstname": "User",
"lastname": "One",
"email": "usr1@sheeldmarket.com"
},
"sourceVirtualAccount": null,
"destinationVirtualAccount": {
"id": 97842452,
"email": "larry.het@aplo.io",
"name": "Larry Het"
}
}
}
],
"count": 3
}
Where data is a JSON array of object with the following fields:
| Field | Type | Description |
|---|---|---|
| id | string | Transfer request UUID |
| quantity | string | Transfer volume |
| sourceVirtualAccountId | number | Source Virtual account id (null indicates the "Main Account") |
| destinationVirtualAccountId | number | Destination Virtual account id (null indicates the "Main Account") |
| assetId | string | Asset identifier |
| type | string | "transfer" |
| accountId | string | Account UUID |
| description | string | null |
| status | string | Status of the transfer, can be "requested", "processing", "approved" or "rejected" |
| createdAt | string | Date of transfer |
| details | object | Transfer details (see below) |
details is an object with the following properties:
| Field | Type | Description |
|---|---|---|
| sourceId | string | Id of the source of the transfer (can be an MPID or "funding") |
| sourceVirtualAccountId | number | Virtual account id for the source of the transfer (null indicates the "Main Account") |
| destinationId | string | Id of the destination of the transfer (can be an MPID or "funding") |
| destinationVirtualAccountId | number | Virtual account id for the destination of the transfer (null indicates the "Main Account") |
| userId | string | UUID of the User that made this request (most likely: you) |
| createdBy | object | Details about the user that requested this request |
| sourceVirtualAccount | object | Details about the source virtual account (null indicates the "Main account") |
| destinationVirtualAccount | object | Details about the destination virtual account (null indicates the "Main account") |
Get billing balance
import requests
endpoint = '%s/accounts/%s/billing/balance' % (base, accountId)
response = requests.get(endpoint, headers=headers)
This route allows accounts to retrieve the current billing balance of a virtual account ("Main Account" by default).
HTTP Request
GET /accounts/:accountId/billing/balance
This route can take the following optional query parameters:
| Parameter | Type | Description |
|---|---|---|
| virtualAccountId | number | Specific virtual account to retrieve the billing balance for (null indicates the "Main Account") |
Response fields
Example of JSON structure returned by the request:
{
"USD": {
"outstandingInvoices": "21500",
"refundBalance": "549.83"
},
"EUR": {
"outstandingInvoices": "1000",
"refundBalance": "0"
}
}
This route returns both the total outstanding invoice amount and refund balance for both USD and EUR.
To get the details of the due invoices, you can use the GET invoices route and filter on the status.
Get invoices
import requests
endpoint = '%s/accounts/%s/billing/invoices' % (base, accountId)
response = requests.get(endpoint, headers=headers)
This route allows accounts to retrieve all their invoices.
HTTP Request
GET /accounts/:accountId/billing/invoices
This route can take the following optional query parameters:
| Parameter | Type | Description |
|---|---|---|
| virtualAccountId | number | Specific virtual account to retrieve the billing balance for (null indicates the "Main Account") |
| status | string | Filter on invoice status, can be "paid" or "due" |
| invoiceNumber | string | Text filter on invoice number (can be partial match) |
| issueDateStart | number or string | Filter on issue date. Can be either a Unix timestamp (in seconds), a JS timestamp (in milliseconds), or a string date with format YYYY-MM-DD HH:mm:ss or YYYY-MM-DDTHH:mm:ss.SSSZ |
| issueDateEnd | number or string | Filter on issue date. Can be either a Unix timestamp (in seconds), a JS timestamp (in milliseconds), or a string date with format YYYY-MM-DD HH:mm:ss or YYYY-MM-DDTHH:mm:ss.SSSZ |
| dueDateStart | number or string | Filter on due date. Can be either a Unix timestamp (in seconds), a JS timestamp (in milliseconds), or a string date with format YYYY-MM-DD HH:mm:ss or YYYY-MM-DDTHH:mm:ss.SSSZ |
| dueDateEnd | number or string | Filter on due date. Can be either a Unix timestamp (in seconds), a JS timestamp (in milliseconds), or a string date with format YYYY-MM-DD HH:mm:ss or YYYY-MM-DDTHH:mm:ss.SSSZ |
| isCanceled | boolean | Whether or not to filter out canceled invoices |
Response fields
Example of JSON structure returned by the request:
{
"data": [
{
"id": "93a713a3-aa98-508a-b10d-0f179fce8fe6",
"invoiceNumber": "F-2024-23082",
"invoiceNumberSequence": "23082",
"isSubjectToVat": false,
"issueDate": "2024-01-01",
"dueDate": "2024-02-01",
"fileName": "aplo-invoice-F-2024-23082.pdf",
"status": "paid",
"totalAmount": "12400",
"currencyId": "USD",
"accountId": "4132f517-d453-5615-80f1-6ee043db4ad5",
"virtualAccountId": null,
"creditNoteId": null,
"settlements": [
{
"id": "4",
"createdAt": "2024-01-10 10:12:33+00",
"createdBy": "Test User (test.user@email.com)",
"accountId": "4132f517-d453-5615-80f1-6ee043db4ad5",
"virtualAccountId": null,
"sourceSubAccount": "funding",
"rate": "1",
"quantity": "12400",
"totalQuantity": "12400",
"currencyId": "USD",
"refundQuantity": "0",
"refundCurrencyId": "USD",
"totalRefundQuantity": "0",
"totalSettled": "12400",
"targetCurrencyId": "USD",
"description": "Settlement for invoice F-2024-23082"
}
],
"billingItems": [
{
"id": "8",
"accountId": "4132f517-d453-5615-80f1-6ee043db4ad5",
"virtualAccountId": null,
"status": "paid",
"name": "Charge example",
"description": "Here is a nice description",
"category": "Fee",
"subCategory": null,
"currencyId": "USD",
"vatRate": "0",
"isSubjectToVat": false,
"type": "charge",
"eventTime": "2024-01-01 08:00:00+00",
"unitPrice": "10000",
"quantity": "1",
"value": "10000",
"vatValue": "0",
"totalValue": "10000",
"invoiceNumber": "23082",
"creditNoteId": null,
"billingSettlementId": null,
"settlement": null
},
{
"id": "12",
"accountId": "4132f517-d453-5615-80f1-6ee043db4ad5",
"virtualAccountId": null,
"status": "paid",
"name": "Another one",
"description": null,
"category": "Fee",
"subCategory": "Trading fee",
"currencyId": "USD",
"vatRate": "0",
"isSubjectToVat": false,
"type": "charge",
"eventTime": "2024-01-01 08:10:00+00",
"unitPrice": "400",
"quantity": "1",
"value": "400",
"vatValue": "0",
"totalValue": "400",
"invoiceNumber": "23082",
"creditNoteId": null,
"billingSettlementId": null,
"settlement": null
}
]
},
{
"id": "bb868349-fc86-5902-bf38-f7a38d6d8a84",
"invoiceNumber": "F-2024-23098",
"invoiceNumberSequence": "23098",
"isSubjectToVat": true,
"issueDate": "2024-02-01",
"dueDate": "2024-03-01",
"fileName": "aplo-invoice-F-2024-23098.pdf",
"status": "due",
"totalAmount": "6000",
"currencyId": "EUR",
"accountId": "4132f517-d453-5615-80f1-6ee043db4ad5",
"virtualAccountId": null,
"creditNoteId": null,
"settlements": [],
"billingItems": [
{
"id": "28",
"accountId": "4132f517-d453-5615-80f1-6ee043db4ad5",
"virtualAccountId": null,
"status": "due",
"name": "Banking Fee",
"description": null,
"category": null,
"subCategory": null,
"currencyId": "EUR",
"vatRate": "0.2",
"isSubjectToVat": true,
"type": "charge",
"eventTime": "2024-02-04 12:13:00+00",
"unitPrice": "5000",
"quantity": "1",
"value": "5000",
"vatValue": "1000",
"totalValue": "6000",
"invoiceNumber": "23098",
"creditNoteId": null,
"billingSettlementId": null,
"settlement": null
},
]
}, ...
],
"count": 12
}
Where data is a JSON array of objects with the following properties:
| Header | Type | Description |
|---|---|---|
| id | string | UUID of the invoice |
| invoiceNumber | string | Human readable number of the invoice |
| invoiceNumberSequence | string | Sequence number of the invoice number |
| isSubjectToVat | boolean | Whether this invoice is subject to VAT or not |
| issueDate | string | Date of issuance |
| dueDate | string | Due date for the invoice |
| fileName | string | Name of the PDF file for this invoice |
| status | string | "due" or "paid" |
| totalAmount | string | The total amount of currency this invoice is for |
| currencyId | string | "USD" or "EUR" |
| accountId | string | Account identifier |
| virtualAccountId | number | Virtual account id (null indicates the "Main Account") |
| creditNoteId | string | Credit note in case the invoice has been canceled. null otherwise |
| settlements | Array | Array of settlements, for now partial settlement of invoices is not supported, so only the first element will be present or not |
| billingItems | Array | The details of the invoice items |
billingItems is a sub-array of JSON objects with the following properties
| Header | Type | Description |
|---|---|---|
| id | string | Unique billing item id |
| accountId | string | Account identifier |
| virtualAccountId | number | Virtual account id (null indicates the "Main Account") |
| status | string | "due" or "paid" |
| name | string | Name of the billing item |
| description | string | Potential description for this item (can be null) |
| category | string | Potential category for this item (can be null) |
| subCategory | string | Potential sub category for this item (can be null) |
| currencyId | string | "USD" or "EUR" |
| vatRate | string | The VAT rate for this item ("0") if not subject to VAT |
| isSubjectToVat | boolean | Whether this item is subject to VAT or not |
| type | string | "charge" or "rebate" (rebates will have negative values for unitPrice and other number columns) |
| eventTime | string | Time of item billing |
| unitPrice | string | The unit price for this item |
| quantity | string | Number of item |
| value | string | unitPrice * quantity |
| vatValue | string | unitPrice * quantity * vatRate |
| totalValue | string | value + vatValue (should be identical to value for item not subject to VAT) |
| invoiceNumber | string | Number of the invoice this item belongs to |
| creditNoteId | string | Credit note in case the invoice has been canceled. null otherwise |
| billingSettlementId | string | Only used for pre-settled billing items, null otherwise |
| settlement | Object | Only used for pre-settled billing items, null otherwise |
settlements and billingItems.settlement are JSON objects with the following properties
| Header | Type | Description |
|---|---|---|
| id | string | Unique settlement id |
| accountId | string | Account identifier |
| virtualAccountId | number | Virtual account id (null indicates the "Main Account") |
| createdAt | string | Date of settlement |
| createdBy | string | Which user settled this invoice/item |
| sourceSubAccount | string | Source of funds for the settlement "funding" for invoice settlements, "outgoingFunding" for automatic withdrawal fees settlement |
| rate | string | Rate of conversion if needed. |
| quantity | string | Quantity settled (in currencyId) |
| totalQuantity | string | quantity * rate (in targetCurrencyId) |
| currencyId | string | The currency used for this settlement |
| refundQuantity | boolean | The billingRefund quantity being used to settle (in refundCurrencyId) |
| refundCurrencyId | string | "USD" or "EUR", the currency of the billingRefund being used |
| totalRefundQuantity | string | refundQuantity * rate (in targetCurrencyId) |
| totalSettled | string | totalQuantity + totalRefundQuantity (in targetCurrencyId) |
| targetCurrencyId | string | "USD" or "EUR", the currency of the invoice/billingItem being settled |
| description | string | Human readable description |
Settle invoice
import requests
endpoint = '%s/accounts/%s/billing/invoices/%s/settle' % (base, accountId, invoiceId)
body = {
"assetId": "USDT",
}
response = requests.post(endpoint, headers=headers, json=body)
Allow users to settle a "due" invoice.
Note: you shouldn't use invoiceNumber here but the id property of an invoice (which is a UUID).
You can settle a "USD" invoice using "USDT", "USDC" or "USD" without the use of a conversion.
You can only settle a "EUR" invoice using "EUR" without the use of a conversion.
You can settle a "USD" invoice using "EUR" or a "EUR" invoice using "USDT", "USDC" or "USD" by providing an optional token property in the body.
To get this token, please use the GET invoice token route.
HTTP Request
POST /accounts/:accountId/billing/invoices/:invoiceId/settle
Body Parameters
| Parameter | Type | Description |
|---|---|---|
| assetId | string | "USDT", "USDC", "USD" or "EUR" |
| token | string | optional The conversion JWT (see above) |
Response fields
This route returns the settlement object, see the GET invoices route for the settlement properties
Get invoice token
import requests
endpoint = '%s/accounts/%s/billing/invoice/%s/token' % (base, accountId, invoiceId)
response = requests.get(endpoint, headers=headers)
This route allows accounts to generate a JWT guaranteeing a fixed rate of conversion between "USD" and "EUR" for a period of 10 minutes.
Note: you shouldn't use invoiceNumber here but the id property of an invoice (which is a UUID).
You can then use this token to settle an invoice using a different currency than the invoice (see the settle invoice route for more information)
HTTP Request
GET /accounts/:accountId/billing/invoices/:invoiceId/token
Response fields
Example of JSON structure returned by the request:
{
"token": "DOI3JR3IDJQ034.FQZJDO12OR89F8DF.234523"
}
Request staking an asset
import requests
endpoint = '%s/accounts/%s/stake' % (base, accountId)
body = {
"assetId": "SOL",
"quantity": "1000",
"virtualAccountId": null
}
response = requests.post(endpoint, headers=headers, json=body)
This route allows market participants to request staking an asset.
The asset will be transferred from the Funding Account towards the Staking Sub Account.
HTTP Request
POST /accounts/:accountId/stake
Body Parameters
| Parameter | Type | Description |
|---|---|---|
| assetId | string | Asset ID |
| quantity | string | Staking volume |
| virtualAccountId | number | optional Virtual account id requesting the stake request (null indicates the "Main Account") |
Response fields
Example of JSON structure returned by the request:
{
"id": "87ccf43f-225e-4c77-9e32-7fdc4693e870",
"assetId": "SOL",
"accountId": "45cabb50-42f7-4a69-a534-db030882f34c",
"virtualAccountId": null,
"status": "pending",
"type": "stake",
"quantity": "1000",
"day": "2025-11-13",
"notional": "153000",
"createdAt": "2025-11-13T14:47:10.334Z",
"requestedBy": "User One (usr1@aplo.io)"
}
The response is a JSON object with the following fields:
| Field | Type | Description |
|---|---|---|
| id | string | Stake request UUID |
| assetId | string | Asset ID |
| accountId | string | Account UUID |
| virtualAccountId | number | Virtual account id (null indicates the "Main Account") |
| status | string | Stake request status |
| type | string | Stake request type (stake or unstake) |
| quantity | string | Stake request volume |
| day | string | The session of the request |
| notional | string | The notional value of the request |
| createdAt | string | ISO date of the request |
| requestedBy | string | The initiator of the request |
Request unstaking an asset
import requests
endpoint = '%s/accounts/%s/unstake' % (base, accountId)
body = {
"assetId": "SOL",
"quantity": "1000",
"virtualAccountId": null
}
response = requests.post(endpoint, headers=headers, json=body)
This route allows market participants to request unstaking an asset.
The asset will be transferred from the Staking Sub Account towards the Funding Account.
HTTP Request
POST /accounts/:accountId/unstake
Body Parameters
| Parameter | Type | Description |
|---|---|---|
| assetId | string | Asset ID |
| quantity | string | Unstaking volume |
| virtualAccountId | number | optional Virtual account id requesting the unstake request (null indicates the "Main Account") |
Response fields
Example of JSON structure returned by the request:
{
"id": "87ccf43f-225e-4c77-9e32-7fdc4693e870",
"assetId": "SOL",
"accountId": "45cabb50-42f7-4a69-a534-db030882f34c",
"virtualAccountId": null,
"status": "pending",
"type": "unstake",
"quantity": "1000",
"day": "2025-11-13",
"notional": "153000",
"createdAt": "2025-11-13T14:47:10.334Z",
"requestedBy": "User One (usr1@aplo.io)"
}
The response is a JSON object with the following fields:
| Field | Type | Description |
|---|---|---|
| id | string | Request UUID |
| assetId | string | Asset ID |
| accountId | string | Account UUID |
| virtualAccountId | number | Virtual account id (null indicates the "Main Account") |
| status | string | Request status |
| type | string | Request type (stake or unstake) |
| quantity | string | Unstaking volume |
| day | string | The session of the request |
| notional | string | The notional value of the request |
| createdAt | string | ISO date of the request |
| requestedBy | string | The initiator of the request |
Fund Administration
Get activity
import requests
endpoint = '%s/user/activity?start=%s&asset=%s&subAccount=%s' % (base, start, asset, subAccount)
response = requests.get(endpoint, headers=headers)
This route allows users to retrieve their account and subAccount activity.
This route takes the following optional query parameters:
| Parameter | Type | Description |
|---|---|---|
| asset | string | Asset filter |
| accountId | string | Account UUID that the user has access to (can be retrieved with the /user route) |
| subAccount | string | "billing" (for billing account activity) or a 4-character Market participant ID that the user has access to (can be retrieved with the /user route) |
| start | number or string | inclusive Can be either a Unix timestamp (in seconds), a JS timestamp (in milliseconds), or a string date with format YYYY-MM-DD HH:mm:ss or YYYY-MM-DDTHH:mm:ss.SSSZ |
| end | number or string | inclusive Can be either a Unix timestamp (in seconds), a JS timestamp (in milliseconds), or a string date with format YYYY-MM-DD HH:mm:ss or YYYY-MM-DDTHH:mm:ss.SSSZ |
| order | string | The sort order (by time), can be "asc" or "desc" (defaults to "asc") |
| virtualAccountId | number | Specific virtual account to retrieve activity for |
One of start and end MUST be provided (both can be provided at once).
HTTP Request
GET /user/activity
If pagination is not used, results will be streamed as chunks of JSON objects (using the transfer-encoding header)
This is the prefered way of getting large amount of records at once, but be aware that this may present long wait before the query fully finishes.
Response fields
Example of JSON structure returned by the request:
{
"data": [
{
"time": "2021-03-10T09:27:36.088Z",
"type": "deposit",
"asset": "BTC",
"quantity": "20",
"updatedBalance": "20",
"subAccount": "SHLD",
"activityId": "Ooi9873jBDX423237Osg450lnc0985hyD3fSSA",
"activityNote": "4bd70033-6266-4677-b5f2-5df9d7e05c2b",
"activityTime": "2021-03-09T10:00:00.000Z",
"virtualAccountId": null,
"assetName": "BTC"
}
],
"count": 329
}
Where data is a JSON array of objects with the following properties:
| Header | Type | Description |
|---|---|---|
| time | Date | ISO date of when the activity was entered in our system's books |
| type | string | Type of activity, can be "deposit", "withdrawal", "transfer", "trade", "rebate", "charge", "conversion" or "settlement" |
| asset | string | Aplo Asset identifier |
| quantity | string | Volume added or removed from the balance (can be negative) |
| updatedBalance | string | Balance of the account or subAccount for this asset after this activity |
| subAccount | string | Sub account impacted by this line. |
| activityId | string | Relevant ID based on the activity type (see below) |
| activityNote | string | Additional information based on the activity type (see below) |
| activityTime | string | ISO date of the event that lead to this line (Examples: for airdrops, activityTime is the actual reception time, and time is the time we notified the airdrop.) |
| virtualAccountId | number | Virtual account id (null indicates the "Main Account") |
| assetName | string | Human readable asset name (for SPOT assets, it is identical to the asset property) |
activityId value based on type:
| Type | ID description |
|---|---|
| deposit | Transaction ID |
| withdrawal | Transaction ID |
| transfer | none |
| trade | Match ID (mid) |
| rebate | Billing ID |
| charge | Billing ID |
| conversion | none |
| settlement | Settlement ID |
activityNote value based on type:
| Type | Note |
|---|---|
| deposit | Source address |
| withdrawal | Destination address |
| transfer | none |
| trade | Order token |
| rebate | Custom note |
| charge | Custom note |
| conversion | none |
| settlement | Settlement name |
Get activity report
import requests
endpoint = '%s/user/activity/report?start=%s&asset=%s&subAccount=%s' % (base, start, asset, subAccount)
response = requests.get(endpoint, headers=headers)
This route allows users to retrieve their account and subAccount activity as a CSV file.
This route takes the same optional query parameters as the JSON route except it doesn't support pagination.
HTTP Request
GET /user/activity/report
Response fields
Example of CSV file returned by the request:
Time, Type, Asset, Quantity, Updated balance, Sub Account, Activity ID, Activity Note, Activity Time, Virtual Account Id, Asset Name
2021-03-10T09:27:36.088Z, deposit, BTC, 20, 20, SHLD, Ooi9873jBDX423237Osg450lnc0985hyD3fSSA, 2021-03-09T10:00:00.000Z, 4bd70033-6266-4677-b5f2-5df9d7e05c2b,,BTC
For information about the CSV columns see the JSON route
Get balances
import requests
endpoint = '%s/user/balances?date=%s&asset=%s' % (base, start, asset)
response = requests.get(endpoint, headers=headers)
This route allows users to retrieve their account and subAccount balances. This route takes the following optional query parameters:
| Parameter | Type | Description |
|---|---|---|
| format | string | Whether asset keys are ids or names. Can be "id" or "name", Default: "name" |
| accountId | string | Account UUID that the user has access to (can be retrieved with the /user route) |
| asset | string | Asset identifier |
| date | number or string | inclusive Can be either a Unix timestamp (in seconds), a JS timestamp (in milliseconds), or a string date with format YYYY-MM-DD HH:mm:ss or YYYY-MM-DDTHH:mm:ss.SSSZ |
| virtualAccountId | number | Specific virtual account to retrieve balances for |
HTTP Request
GET /user/balances
Response fields
Example of JSON structure returned by the request:
{
"date": "2022-03-04T09:36:35.592Z",
"total": {
"BTC": "5",
"ETH": "50",
"USDT": "10000"
},
"subAccounts": {
"funding": {
"BTC": "2",
"ETH": "10"
},
"SONE": {
"USDT": "5000"
},
"earning": {
"USDT": "4000"
},
"incomingEarn": {
"SONE": {
"USDT": "1000"
}
},
"outgoingEarn": {
"BTC": "2",
"ETH": "40"
}
},
"virtualAccountId": null
}
date is the date of the balances. Defaults to the current time or the date query param, if provided.
The subAccounts property is a JSON object with sub accounts for keys with respective balances as values.
The virtualAccountId property is the virtual account requested (null by default).
Depending on the format parameter, asset keys will either be our internal asset id or asset names. This is only relevant for non-SPOT assets
For example:
| Id | Name |
|---|---|
| BTC | BTC |
| wBTC_A | BTC-USDT.PERP-USDT-Binance |
Orders History
import requests
endpoint = '%s/user/orders?start=%s&end=%s' % (base, start, end)
response = requests.get(endpoint, headers=headers)
This route allows users to retrieve their order activity This route takes the following optional query parameters:
| Parameter | Type | Description |
|---|---|---|
| asset | string | Asset filter |
| accountId | string | Account UUID that the user has access to (can be retrieved with the /user route) |
| mpid | string | 4-character Market participant ID that the user has access to (can be retrieved with the /user route). If supplied, overwrites the account parameter. |
| start | number or string | inclusive Can be either a Unix timestamp (in seconds), a JS timestamp (in milliseconds), or a string date with format YYYY-MM-DD HH:mm:ss or YYYY-MM-DDTHH:mm:ss.SSSZ |
| end | number or string | inclusive Can be either a Unix timestamp (in seconds), a JS timestamp (in milliseconds), or a string date with format YYYY-MM-DD HH:mm:ss or YYYY-MM-DDTHH:mm:ss.SSSZ |
| hideClosed | boolean | Filters out unfilled closed orders, accepts "true" (default) or "false" |
| order | string | The sort order (by time), can be "asc" or "desc" (defaults to "asc") (Only used with pagination) |
| virtualAccountId | number | Specific virtual account to retrieve orders for |
One of start and end MUST be provided (both can be provided at once).
HTTP Request
GET /user/orders
If pagination is not used, results will be streamed as chunks of JSON objects (using the transfer-encoding header)
This is the prefered way of getting large amount of records at once, but be aware that this may present long wait before the query fully finishes.
Response fields
Example of JSON structure returned by the request:
{
"data": [
{
"time": "2021-01-06T13:41:55.348Z",
"closingTime": "2021-01-06T14:12:55.348Z",
"initialDay": "2021-01-06",
"day": "2021-01-06",
"user": "undefined",
"mpid": "SHLD",
"orderId": "3061388281894476",
"status": "closed",
"type": "IDO",
"side": "Buy",
"asset": "BTC",
"currency": "USDT",
"requestedVolume": "1.0001",
"requestedPrice": "18000.43",
"executedVolume": "0.9998",
"averagePrice": "15000.43",
"total": "14997.429914",
"executionCount": "5",
"virtualAccountId": null,
"assetName": "BTC"
},
{
"time": "2021-01-06T13:41:55.348Z",
"closingTime": "2021-01-06T14:12:55.348Z",
"initialDay": "2021-01-06",
"day": "2021-01-06",
"user": "undefined",
"mpid": "SHLD",
"orderId": "8715",
"status": "closed",
"type": "IDO",
"side": "Buy",
"asset": "wBTC_C",
"currency": "USDT",
"requestedVolume": "1.0001",
"requestedPrice": "18000.43",
"executedVolume": "0.9998",
"averagePrice": "15000.43",
"total": "14997.429914",
"executionCount": "5",
"virtualAccountId": null,
"assetName": "BTC-USDT.PERP-Binance"
}
],
"count": 129
}
Where data is a JSON array of objects with the following properties:
| Header | Type | Description |
|---|---|---|
| time | Date | ISO date of Order creation |
| closingTime | Date | ISO date of Order closing or null if the order is still open |
| initialDay | Date | Session of Order creation |
| day | Date | Last session of Order lifetime |
| user | string | Name of the user that created this order (or "undefined" if not provided) |
| mpid | string | 4-character Market participant ID |
| orderId | string | Unique token supplied at order creation |
| status | string | "received", "open" or "closed" |
| type | string | Can be "DAY", "GTC", "IOC", "IDO", "IGO", "ISO", "VIO", "TWAP", "VWAP", "VTO", "SLO", "TPO", "VTO", "PTO", "LMT", "MKT" or "DCA" |
| side | string | "S" or "B" |
| asset | string | Asset identifier |
| currency | string | Currency identifier |
| requestedVolume | string | Requested volume at Order creation (can be "null" for notional orders) |
| requestedPrice | string | Requested price at Order creation (can be "null" for "MKT" or "DCA" orders) |
| executedVolume | string | Volume executed so far |
| averagePrice | string | Average price of executions |
| total | string | Total volume of currency traded for this Order |
| executionCount | number | Number of executions for this Order |
| virtualAccountId | number | Virtual account id (null indicates the "Main Account") |
| assetName | string | Human readable asset name (for SPOT assets, it is identical to the asset property) |
Orders history report
import requests
endpoint = '%s/user/orders/report?start=%s&end=%s' % (base, start, end)
response = requests.get(endpoint, headers=headers)
This route allows users to retrieve a CSV report of their order activity
The returned data is not ordered in any way for performance reasons. (Except if pagination is used)
This route takes the same optional query parameters as the JSON route except it doesn't support pagination.
HTTP Request
GET /user/orders/report
Response fields
Example CSV file returned by the request:
Time,ClosingTime,Day,InitialDay User,MPID,OrderId,Status,Type,Side,Asset,Currency,Requested Volume,Requested Price,Executed Volume,Average Price,Total,Execution Count,Virtual Account Id,Asset Name
2021-01-06T13:41:55.348Z,2021-01-06T13:41:55.348Z,2021-01-06,2021-01-06,SHLD,undefined,3061388281894476,closed,IDO,Buy,BTC,USDT,1.0001,18000.43,0.9998,15000.43,14997.429914,5,,BTC
For information about the CSV columns see the JSON route
Trades History
import requests
endpoint = '%s/user/trades?start=%s&end=%s' % (base, start, end)
response = requests.get(endpoint, headers=headers)
This route allows users to retrieve their trade activity This route takes the following optional query parameters:
| Parameter | Type | Description |
|---|---|---|
| accountId | string | Account UUID that the user has access to (can be retrieved with the /user route) |
| mpid | string | 4-character Market participant ID that the user has access to (can be retrieved with the /user route). If supplied, overwrites the account parameter. |
| asset | string | Asset filter |
| token | string | Order id filter |
| start | number or string | inclusive Can be either a Unix timestamp (in seconds), a JS timestamp (in milliseconds), or a string date with format YYYY-MM-DD HH:mm:ss or YYYY-MM-DDTHH:mm:ss.SSSZ |
| end | number or string | inclusive Can be either a Unix timestamp (in seconds), a JS timestamp (in milliseconds), or a string date with format YYYY-MM-DD HH:mm:ss or YYYY-MM-DDTHH:mm:ss.SSSZ |
| order | string | The sort order (by time), can be "asc" or "desc" (defaults to "asc") (Only used with pagination) |
| virtualAccountId | number | Specific virtual account to retrieve trades for (defaults to null) |
One of start and end MUST be provided (both can be provided at once).
HTTP Request
GET /user/trades
If pagination is not used, results will be streamed as chunks of JSON objects (using the transfer-encoding header)
This is the prefered way of getting large amount of records at once, but be aware that this may present long wait before the query fully finishes.
Response fields
Example of JSON structure returned by the request:
{
"data": [
{
"time": "2021-01-06T13:41:55.348Z",
"mpid": "SHLD",
"orderId": "3061388281894476",
"mid": "864691128455135233",
"side": "Buy",
"asset": "BTC",
"currency": "USDT",
"volume": "1.1001",
"price": "18000.43",
"total": "19997.429914",
"liquidity": "Make",
"venue": "Binance",
"virtualAccountId": null,
"assetName": "BTC"
}
],
"count": 129
}
Where data is a JSON array of objects with the following properties:
| Header | Type | Description |
|---|---|---|
| time | Date | ISO date of Trade execution |
| mpid | string | 4-character Market participant ID |
| orderId | string | Unique token supplied at order creation |
| mid | string | Match ID |
| side | string | "Sell" or "Buy" |
| asset | string | Asset identifier |
| currency | string | Currency identifier |
| volume | string | Traded volume |
| price | string | Price of traded volume (0 indicates failed execution) |
| total | string | Total volume of currency for this trade |
| liquidity | string | "Make" or "Take" |
| venue | string | The venue this trade executed on |
| virtualAccountId | number | Virtual account id (null indicates the "Main Account") |
| assetName | string | Human readable asset name (for SPOT assets, it is identical to the asset property) |
Trades history report
import requests
endpoint = '%s/user/trades/report?start=%s&end=%s' % (base, start, end)
response = requests.get(endpoint, headers=headers)
This route allows users to retrieve a CSV report of their trade activity
The returned data is not ordered in any way for performance reasons. (Except if pagination is used)
This route takes the same optional query parameters as the JSON route except it doesn't support pagination.
HTTP Request
GET /user/trades/report
Response fields
Example CSV file returned by the request:
Time,MPID,OrderId,MatchId,Side,Asset,Currency,Volume,Price,Venue,Total,Liquidity,Virtual Account Id,Asset Name
2021-01-06T13:41:55.348Z,SHLD,3061388281894476,98725452,Buy,BTC,USDT,1.1001,18000.43,Binance,19997.429914,Make,,BTC
For information about the CSV columns see the JSON route
Dust Conversions
import requests
endpoint = '%s/user/dust-conversions?start=%s&end=%s' % (base, start, end)
response = requests.get(endpoint, headers=headers)
This route allows users to retrieve their dust conversion history This route takes the following optional query parameters:
| Parameter | Type | Description |
|---|---|---|
| accountId | string | Account UUID that the user has access to (can be retrieved with the /user route) |
| asset | string | Asset filter |
| start | number or string | inclusive Can be either a Unix timestamp (in seconds), a JS timestamp (in milliseconds), or a string date with format YYYY-MM-DD HH:mm:ss or YYYY-MM-DDTHH:mm:ss.SSSZ |
| end | number or string | inclusive Can be either a Unix timestamp (in seconds), a JS timestamp (in milliseconds), or a string date with format YYYY-MM-DD HH:mm:ss or YYYY-MM-DDTHH:mm:ss.SSSZ |
| order | string | The sort order (by time), can be "asc" or "desc" (defaults to "asc") (Only used with pagination) |
| virtualAccountId | number | Specific virtual account to retrieve trades for |
HTTP Request
GET /user/dust-conversions
Response fields
Example of JSON structure returned by the request:
{
"data": [
{
"createdAt": "2021-01-06T13:41:55.348Z",
"requestId": "71c60afa-4013-55b7-b7e1-ee1a34322e49",
"subAccount": "funding",
"side": "Sell",
"assetId": "BTC",
"currencyId": "USDT",
"volume": "1.1001",
"price": "18000.43",
"total": "19997.429914",
"liquidity": "Take",
"virtualAccountId": null
}
],
"count": 12
}
Where data is a JSON array of objects with the following properties:
| Header | Type | Description |
|---|---|---|
| createdAt | Date | ISO date of dust conversions |
| requestId | string | UUID of the conversion request |
| subAccount | string | "funding" |
| side | string | "Sell" or "Buy" |
| assetId | string | Asset identifier |
| currencyId | string | Currency identifier |
| volume | string | Volume Converted |
| price | string | Price of converted volume |
| total | string | Total volume of currency for this conversion |
| liquidity | string | "Make" or "Take" |
| virtualAccountId | number | Virtual account id (null indicates the "Main Account") |
Dust Conversions Report
import requests
endpoint = '%s/user/dust-conversions/report?start=%s&end=%s' % (base, start, end)
response = requests.get(endpoint, headers=headers)
This route allows users to retrieve a CSV report of their dust conversion history This route takes the same optional query parameters as the JSON route except it doesn't support pagination.
HTTP Request
GET /user/dust-conversions/report
Response fields
Example CSV file returned by the request:
Time,Request ID,Sub Account,Side,Asset,Currency,Volume,Price,Total,Liquidity,Virtual Account ID
2021-01-06T13:41:55.348Z,b8475026-52fe-5e40-afae-4203621d66b9,funding,Sell,BTC,USDT,1.1001,18000.43,19997.429914,Make,
For information about the CSV columns see the JSON route
Market Data Websocket
Data Websocket Usage
Aplo gathers market data from all the venues it is connected to and provide a real-time market data feed to its customers.
- As the precision of prices and volumes are different for every platform we are connected to, we are unifying precision under the ticks and lots defined in universe. Therefore the prices and volumes relayed through this feed may slightly vary from original sources.
- As we are just relaying original market data we gather from external platforms, we cannot guarantee in any way the full accuracy of the market data served to clients.
Data General
Production Websocket URL: wss://sheeldmatch.com:6443/
UAT Websocket URL: wss://uat.sheeldmatch.com:6443/
Authenticate using an API key HTTP header upon connecting
The server WILL disconnect every client that:
- does not send handshake HTTP request within 10s after opening the connection.
- does not answer with a pong frame to a ping frame sent by the server within 10 seconds.
Useful ressources
Websocket Protocol Documentation: https://tools.ietf.org/html/rfc6455
Websocket in Python: https://pypi.org/project/websocket-client/
Websocket in JavaScript: https://javascript.info/websocket
JSON Format Documentation: https://www.json.org/json-en.html
Data Messages Format
Server behavior
Every websocket frame sent by the server WILL be a text frame containing one JSON object as described in the JSON format. No compression of any kind WILL be used.
The JSON object WILL contain the following JSON key with their associated JSON values:
| Key | Value |
|---|---|
| type | one of "H", "L", "B", "S", "F", "Z" or "U" |
Every client is guaranteed to receive every message. A slow client will be dropped by the server.
Client rules
The client MUST NOT send any websocket frame to the server. Subscriptions are done with url parameters (max 50 subscriptions per connection). Failure to comply will lead to disconnection of the client by the server.
Data Subscription
Subscription is done through url parameters. The client CANNOT directly unsubscribe and will need to close the websocket before opening it again with other url parameters.
wsurl="wss://sheeldmatch.com:6443/?depth=10&symbols=BTC-USDT.SPOT@Binance/ETH-USDT.SPOT@KuCoin"
The accepted parameters are the following:
| Paramater | Rule | Default | Value |
|---|---|---|---|
| depth | Optional | 1 | one of 0, 1, 10, 25 |
| symbols | Mandatory | None | a list of form instrument1@venue1/instrument2@venue2/... where venue1, venue2 ... and intrument1, intrument2, ... are described in universe |
Subscribe to depth:
- 0 and you will get trades.
- 1 and you will get trades AND snapshots for the first level of the order-book (best bid and best offer).
- 10 and you will get trades AND snapshots for the first ten levels of the order-book.
- 25 and you will get trades AND snapshots for the first twenty-five levels of the order-book.
Snapshots are sent every 100ms. Trades are sent as soon as execution happens.
Trades Messages
{
"type": "H",
"venue": "Binance",
"instrument": "BTC-USDT.SPOT",
"data": [
{
"price": 100000.0,
"volume": 1.0,
"time": "2021-04-27T06:08:28.936000000Z"
},
...
]
}
| Key | Value Type | Description |
|---|---|---|
| type | string | H for "hit the bid" (buyer is maker) OR L for "lift the offer" (seller is maker) |
| venue | string | name of the subscribed venue |
| instrument | string | name of the subscribed instrument |
| data → price | number | price at which the trade was executed |
| data → volume | number | volume of the execution |
| data → time | string | ISO8601 time format of the timestamp at which the execution happend |
Order-Book Messages
{
"type": "B",
"venue": "Binance",
"instrument": "BTC-USDT.SPOT",
"data": [
{
"depth": 0,
"price": 100000.0,
"volume": 1.0,
"time": "2021-04-27T06:08:28.936000000Z"
},
{
"depth": 1,
"price": 100000.0,
"volume": 1.0,
"time": "2021-04-27T06:08:28.936000000Z"
},
...
]
}
| Key | Value Type | Description |
|---|---|---|
| type | string | B if bids need to be updated OR S if asks need to be updated |
| venue | string | name of the subscribed venue |
| instrument | string | name of the subscribed instrument |
| data → depth | number | depth of the orderbook level to update |
| data → price | number | new price of the level that replaces the old one |
| data → volume | number | new volume of the level that replaces the old one |
| data → time | string | ISO8601 time format of the timestamp at which the order-book modification happend |
For each message, the client will have to forget ALL the old levels for the given venue and instrument, and consider the new ones. Empty data array means that there is no more levels in the orderbook (may happen when Aplo loses connexion to the exchanges).
A snapshot will be pushed on connection, and then the client will receive new snapshots every 100ms should any level within the subscribed depth change.
Sequence-is-over Messages
{
"type": "Z",
"data": []
}
| Key | Value Type | Description |
|---|---|---|
| type | string | Z for sequence is over message type |
Order-book data being sent every 100ms, the client sometimes needs to know when all order-book messages have been sent accross all venues and instruments for the current period.
The "sequence-is-over" message serves this purpose.
Micro-Structure Messages
{
"type": "U",
"venue": "Binance",
"instrument": "BTC-USDT.SPOT",
"data": [
{
"lot": 2,
"tick": 4,
"time": "2021-04-27T06:08:28.936000000Z"
}
]
}
| Key | Value Type | Description |
|---|---|---|
| type | string | U for micro-structure type |
| venue | string | name of the subscribed venue |
| instrument | string | name of the subscribed instrument |
| data → lot | number | number of decimals allowed by external venues for order volume |
| data → tick | number | number of decimals allowed by external venues for order price |
| data → time | string | ISO8601 time format of the timestamp at which the last check was done |
Micro-Structure Messages are published as soon as the client subscribes to depth >= 0 for a given instrument on a given external venue (e.g. Binance).
They are published every 10 seconds.
Funding Rate Messages
{
"type": "F",
"instrument": "BTC-USDT.SPOT",
"data": [
{
"funding_rate": 0.00025,
"settlement": "2021-04-27T08:00:00.000000000Z",
"time": "2021-04-27T06:08:28.936000000Z"
}
]
}
| Key | Value Type | Description |
|---|---|---|
| type | string | F for funding rate type |
| instrument | string | name of the subscribed instrument |
| data → funding_rate | number | the current estimated funding rate for the current period |
| data → time | string | ISO8601 time format of the timestamp at which the last estimated funding rate was retrieved |
| data → settlement | string | ISO8601 time format of the timestamp at which the settlement will happen |
Funding Rate Messages are published as soon as the client subscribes to depth >= 0 for a perpetual instrument.
They are specific to an instrument.
They are published every 1 second.
Trading Websocket
Trading Websocket Usage
Aplo offers a low-latency and optimized trading environment. However, connections to external venues can sometimes increase the native latency and requests may timeout (especially posting Immediate-or-Cancel Routed orders).
Trading General
Production Websocket URL: wss://sheeldmatch.com:9443/json
UAT Websocket URL: wss://uat.sheeldmatch.com:9443/json
Authenticate using an API key HTTP header upon connecting
The server WILL disconnect every client that:
- does not send a ping frame (see RFC) to the server at most every 60 seconds.
- does not answer with a pong frame to a ping frame sent by the server within 5 seconds.
Useful ressources
Websocket Protocol Documentation: https://tools.ietf.org/html/rfc6455
Websocket in Python: https://pypi.org/project/websocket-client/
Websocket in JavaScript: https://javascript.info/websocket
JSON Format Documentation: https://www.json.org/json-en.html
Trading Messages Format
Server behavior
Every websocket frame sent by the server WILL be a text frame containing one JSON object as described in the JSON format. No compression of any kind WILL be used.
The JSON object WILL contain the following JSON key with its associated JSON value:
| Key | Value |
|---|---|
| type | one of "O" for Order, "T" for Trade, "B" for Balance, "S" for Risk, "V" for Venues OR "I" for Instruments |
For the same type, JSON objects sent by the server WILL always have the same structure with the same JSON keys. A message sent by the server to the client is called a response. The structure of every response is described throughout this documentation.
Client behavior
Every websocket frame sent by the client WILL be a text frame containing one JSON object as described in the JSON format. No compression of any kind WILL be used.
The JSON object MUST contain the following JSON key with its associated JSON value:
| Key | Value |
|---|---|
| type | one of "A" to Add, "D" to Reduce, "M" to Modify OR "R" to Remove |
For the same type, JSON objects sent by the client MUST always have the same structure with the same JSON keys. A message sent by the client to the server is called a request. The structure of every request is described throughout this documentation.
Failure to comply to any MUST rule will lead to the disconnection of the client by the server.
Trading Subscription
Subscription is done through url parameters. The client CANNOT directly unsubscribe and will need to close the websocket before opening it again with other url parameters.
The client can subscribe to one or multiple MPIDs. A client which is subscribed to one MPID will received every request and every response about every order belonging to this MPID.
A client CANNOT subscribe to MPIDs it is not authorized to trade through. For more information about authorized MPIDs, please contact support.
wsurl="wss://sheeldmatch.com:9443/json?mpids=ABCD/EFGH/IJKL"
The accepted parameters are the following:
| Paramater | Rule | Default | Value |
|---|---|---|---|
| mpids | Mandatory | None | a list of form mpid1/mpid2/... where mpid1, mpid2, ... are MPIDs the client is authorized to trade through |
Authentification
Authentification is done the same way as for the REST API (see Authenticate each Request). The Authorization header has to be put in the headers of the websocket handshake HTTP request.
Add Request
Orders type are further described in Trading.
{
"type": "A",
"mpid": "ABCD",
"virtual_id": "0",
"token": "109298439784",
"instrument": "BTC-USDT.SPOT",
"price": "100000.01",
"volume": "100.5",
"side": "S",
// smart order routing (SOR)
"parameters": {
"type": "IDO", // or ISO, IGO, IPO, MDO, MSO, MGO, MPO
"priority": "price" // or time, both
},
// OR direct strategy access (DSA)
"parameters": {
"type": "VIO",
"participation": "50" // in percentage
},
// OR direct strategy access (DSA)
"parameters": {
"type": "TWAP", // or VWAP
"lifespan": "100" // minutes
},
// OR tailor-made order (TMO)
"parameters": {
"type": "VTO",
"stop": "90000.1" // limit volume
},
"parameters": {
"type": "SLO", // or TPO
"stop": "90000.1", // limit price
"anchor": "mid" // or bid, offer, last
},
// OR direct market access (DMA)
"parameters": {
"type": "IOC", // or DAY, GTC, POD, FOK
"venue": "Binance"
},
// Can be empty if no parameters are needed
"parameters": {
"type": "PTO" // or IMSH
}
}
| Key | Value Type | Description |
|---|---|---|
| type | string | A |
| mpid | string | the mpid placing the order |
| virtual_id | string | the Virtual Account Id placing the order, "0" for the "Main Account" |
| token | string | the token of the order (must be unique accross all orders passed by one MPID) |
| instrument | string | name of the instrument as described in universe |
| price | string | price of the order (MUST be omitted for "MKT" and "DCA" orders) |
| volume | string | volume of the order (MUST be omitted for parameters.notional orders) |
| side | string | side of the order |
| parameters → type | string | type of the order, must be one of "IDO", "ISO", "IGO", "IPO", "MDO", "MSO", "MGO", "MPO", "VIO", "POV", "FAT", "TWAP", "VWAP", "SLO", "TPO", "VTO", "IOC", "DAY", "GTC", "POD", "FOK", "PTO", "LMT", "MKT" or "DCA" |
| parameters → priority | string | ONLY FOR "ISO", "IGO", "IPO", "MDO", "MSO", "MGO" or "MPO". One of "price" for price priority, "time" for time priority OR "both" for price/time priority |
| parameters → lifespan | string | ONLY FOR "TWAP" or "VWAP. Lifespan in minutes. Accepted range 1 - 525600 |
| parameters → participation | string | ONLY FOR "VIO". Participation in percentage. Accepted range 1 - 99 |
| parameters → stop | string | ONLY FOR "VTO", "SLO" or "PTO" . Stop price (or volume for "VTO") |
| parameters → anchor | string | ONLY FOR "SLO" or "TPO". One of "mid" for mid price anchor, "bid" for best bid price anchor, "offer" for best offer price anchor or "last" for last exec price anchor. |
| parameters → venue | string | ONLY FOR "IOC", "DAY", "GCT", "FOK" or "POD". Direct market access to this venue (name described in universe) |
| parameters → start | string | ONLY FOR "DCA". Start of execution, paired with parameters.frequency. Format MUST be YYYY-MM-DD |
| parameters → frequency | string | ONLY FOR "DCA". Frequency of execution. Must be "daily", "weekly" or "monthly", see here for details |
| parameters → volume | string | ONLY FOR "DCA". Volume of each execution (see parameters.frequency) |
| parameters → notional | string | ONLY FOR "MKT", "LMT" or "DCA". Notional for the order (or notional for each execution for "DCA") |
Remove request
{
"type": "R",
"mpid": "ABCD",
"virtual_id": "9812441",
"token": "109298439784"
}
| Key | Value Type | Description |
|---|---|---|
| type | string | R |
| mpid | string | the mpid that placed the order |
| virtual_id | string | the Virtual Account Id that placed the order, "0" for the "Main Account" |
| token | string | the token of the order (must be unique accross all orders passed by one MPID) |
Modify Request
{
"type": "M",
"mpid": "ABCD",
"virtual_id": "9812441",
"token": "109298439784",
"price": "100000.01",
"instrument": "BTC-USDT.SPOT",
"volume": "100.5"
}
| Key | Value Type | Description |
|---|---|---|
| type | string | M |
| mpid | string | the mpid that placed the order |
| virtual_id | string | the Virtual Account Id that placed the order, "0" for the "Main Account" |
| token | string | the token of the order (must be unique accross all orders passed by one MPID) |
| instrument | string | instrument of the order |
| price | string | new price of the order |
| volume | string | new volume of the order |
Reduce Request
{
"type": "D",
"virtual_id": "4123",
"mpid": "ABCD",
"token": "109298439784",
"instrument": "BTC-USDT.SPOT",
"volume": "100.5"
}
| Key | Value Type | Description |
|---|---|---|
| type | string | D |
| mpid | string | the mpid that placed the order |
| virtual_id | string | the Virtual Account Id that placed the order, "0" for the "Main Account" |
| token | string | the token of the order (must be unique accross all orders passed by one MPID) |
| instrument | string | instrument of the order |
| volume | string | new volume of the order (MUST be lower than the old volume) |
Order Response
The client will receive this kind of response:
- everytime an order is created (add request), modified (modify/reduce request or execution) or removed (remove request or remove by supervision)
- when a request fails (error in protocol or errors described in Errors)
- on successful connection for every open orders
The purpose of this message is to reflect the state of the order associated to the MPID/token pair given in the response. On status error, the response is not guaranteed to be representative of the state of any order.
{
"type": "O",
"mpid": "ABCD",
"virtual_id": "0",
"token": "109298439784",
"instrument": "BTC-USDT.SPOT",
"venue": "",
"side": "S",
"price": "100000.01",
"volume": "100.5",
"time": "2021-06-21T13:09:51.950717685Z",
"creation_time": "2021-06-20T11:34:12.982647564Z",
"status": "ok",
"reason": "Add: Success",
"executed_volume": "0",
"executed_total": "0",
"execution_count": "0"
}
| Key | Value Type | Description |
|---|---|---|
| type | string | O |
| mpid | string | the mpid that placed the order |
| virtual_id | string | the Virtual Account Id that placed the order, "0" for the "Main Account" |
| token | string | the token of the order (must be unique accross all orders passed by one MPID) |
| instrument | string | name of the instrument as described in universe |
| venue | string | name of the venue as described in universe. May be empty for order type other than DMA |
| price | string | price of the order (can be null for "MKT" and "DCA" orders) |
| volume | string | remaining volume of the order. 0 means the order is closed ("null" for orders using parameters.notional) |
| side | string | side of the order |
| time | string | time of the update in ISO8601 format with nanoseconds precision (YYYY-mm-ddTHH-MM-SS.NNNNNNNNNZ) |
| creation_time | string | time of the initial order creation in ISO8601 format with nanoseconds precision (YYYY-mm-ddTHH-MM-SS.NNNNNNNNNZ) |
| status | string | one of "ok" OR "error" |
| reason | string | reason explaining why the client received the order update (described in Reasons) |
| executed_volume | string | Amount of volume executed |
| executed_total | string | Amount of executed currency |
| execution_count | string | Number of executions |
An additional property request is present for Order responses with reason "Initialization: Open Order" that are broadcasted upon connection.
This property is a copy of the initial Add request for additional information like type, parameters, volume (requested volume) and price (requested price)
Trade Response
The client will receive this kind of response everytime an execution happens.
The purpose of this message is to give the details of the execution that just happened.
{
"type": "T",
"mpid": "ABCD",
"virtual_id": "98124",
"token": "109298439784",
"instrument": "BTC-USDT.SPOT",
"venue": "Binance",
"side": "S",
"price": "100000.01",
"volume": "100.5",
"time": "2021-06-21T13:09:51.950717685Z",
"mid": "13098120498203487",
"reason": "Execution: Normal Execution",
"fees": "12",
"liquidity": "maker"
}
| Key | Value Type | Description |
|---|---|---|
| type | string | T |
| mpid | string | the mpid that placed the order |
| virtual_id | string | the Virtual Account Id that placed the order, "0" for the "Main Account" |
| token | string | the token of the order (must be unique accross all orders passed by one MPID) |
| instrument | string | name of the instrument as described in universe |
| venue | string | name of the venue on which the execution happened (name described in universe) |
| side | string | side of the associated order |
| price | string | price of the execution (can be 0 for "DCA" failed executions) |
| volume | string | executed volume |
| time | string | time of the execution in ISO8601 format with nanoseconds precision (YYYY-mm-ddTHH-MM-SS.NNNNNNNNNZ) |
| mid | string | id of the execution (64bits integer) |
| reason | string | reason detailing the execution status |
| fees | string | fees in bps |
| liquidity | string | maker, taker or unknonwn (for "DCA" failed executions) |
Balance Response
The client will receive this kind of response everytime there is a change in balances and on connection, just after the handshake.
{
"type": "B",
"mpid": "ABCD",
"virtual_id": "0",
"asset_id": "BTC",
"asset_name": "BTC",
"reason": "execution",
"time": "2021-06-21T13:09:51.950717685Z",
"total": "2.0",
"committed": "0.5",
"collateralized": "0.5",
"borrowed": "0",
"available": "1",
"total_change": "2",
"cost": 38000,
"position_cost": "32500",
"accumulated": "2.5"
}
| Key | Value Type | Description |
|---|---|---|
| type | string | B |
| mpid | string | the mpid owning this balance |
| virtual_id | string | the Virtual Account Id owning this balance, "0" for the "Main Account" |
| asset_id | string | asset id uniquely identifying the asset (may not be explicit for non spot asset) |
| asset_name | string | explicit asset name (same as asset_id for spot assets) |
| reason | string | event that led to the modification of the balance |
| time | string | time of the publication in ISO8601 format with nanoseconds precision (YYYY-mm-ddTHH-MM-SS.NNNNNNNNNZ) |
| total | string | total balance |
| committed | string | committed balance |
| collateralized | string | collateralized balance |
| borrowed | string | borrowed balance |
| available | string | available balance |
| total_change | string | change in total balance since last balance response |
| cost | string | money flow of asset |
| position_cost | string | current position cost of asset |
| accumulated | string | accumulated transacted quantity of asset |
Risk Response
The client will receive this kind of response when they have margined positions open on their mpid
{
"type": "S",
"mpid": "ABCD",
"virtual_id": "0",
"asset_id": "wBTC_A",
"asset_name": "BTC-USDT.PERP-USDT-Binance",
"time": "2021-06-21T13:09:51.950717685Z",
"margin_ratio_distance": "2.00000"
}
| Key | Value Type | Description |
|---|---|---|
| type | string | S |
| mpid | string | the mpid owning this balance |
| virtual_id | string | the Virtual Account Id owning this balance, "0" for the "Main Account" |
| asset_id | string | asset id uniquely identifying the asset (may not be explicit for non spot asset) |
| asset_name | string | explicit asset name (same as asset_id for spot assets) |
| time | string | time of the publication in ISO8601 format with nanoseconds precision (YYYY-mm-ddTHH-MM-SS.NNNNNNNNNZ) |
| margin_ratio_distance | string | Liquidation risk indicator: 0 -> No Risk, 1 -> Low Risk, 2 -> Moderate Risk, 3 -> High Risk, 4 -> Very High Risk, 5 -> Liquidation in progress |
End Of Sequence Response
The client will receive this response once after all the "O" and "B" (Order and Balance) initilization messages have been sent.
{
"type": "Z"
}
| Key | Value Type | Description |
|---|---|---|
| type | string | R |
Venues Response
The client will receive this kind of response on connection, just after the handshake.
{
"type": "V",
"venues": ["Binance", "OKEx", "Huobi"]
}
| Key | Value Type | Description |
|---|---|---|
| type | string | V |
| venues | JSON array | array of all venues |
Instruments Response
The client will receive this kind of response on connection, just after the handshake.
{
"type": "I",
"instruments": ["BTC-USDT.SPOT", "ETH-USDT.SPOT", "ADA-USDT.SPOT"]
}
| Key | Value Type | Description |
|---|---|---|
| type | string | I |
| instruments | JSON array | array of all instruments |
Reasons
Every reason is formed the same way. It starts with the "why" the client received the response and ends with an explanation.
Every reason will starts with one of the following string:
| String | Description |
|---|---|
| "Add: " | In response to an add request |
| "Remove: " | When an order is removed |
| "Modify: " | When an order is modified |
| "Reduce: " | When an order is reduced |
| "Execution: " | When an execution occurs |
| "Bad Request: " | When a request is malformed or when type/mpid/token cannot be determined |
| "Initialization: " | On connection |
Every reason will end with an explanation that will depend on the status. For status ok please refer to OK Reasons. For status error please refer to Error Reasons.
The reason for the order responses broadcasted on connection will be "Initialization: Open Order".
OK Reasons
| Explanation | Description |
|---|---|
| "Success" | The request was successful |
| "Normal Execution" | A normal execution happened |
| "Busted Execution" | The execution has been busted |
| "Self Execution" | A self execution happened |
| "Removed due to expiration" | An order expired |
| "Removed by market supervision" | An order is removed by market supervision |
| "Removed by the System (at the end of the day session)" | An order is removed because end of day is reached |
| "Removed by the Matching Engine" | An order is removed by the matching engine |
| "Open Order" | The order was open before connection |
Error Reasons
| Explanation | Description |
|---|---|
| "Temporarily Unavailable" | Try again later |
| "Time-In-Force Expiry" | When an order expires due to Time-In-Force concerns |
| "Insufficient Balance" | Balance is insufficient |
| "Limit breach" | Limits defined in backoffice have beend reached |
| "Bad Market Participant ID" | MPID is unknown, unauthorized or unsubscribed |
| "Bad Instrument" | Instrument is unknown or not allowed to trade |
| "Bad Token or Dead Order" | Token is either unknown for this mpid or belongs to an already known order (for add) or already closed order (for remove/modify/reduce) |
| "Bad Price" | Price is malformed or does not respect tick or was not accepted by external platform |
| "Race Condition" | Previous request for this pair MPID/token has not been responded yet. The client must wait for the response before sending request again. |
| "Bad Side" | Side is malformed (must be B or S) |
| "Bad Volume" | Volume is malformed or does not respect lot or was not accepted by external platform |
| "Bad Notional" | Notional is malformed or does not respect lot/tick or was not accepted by external platform |
| "General failure" | Something unexpected happened |
| "Bad Venue" | Venue is unknown |
| "Market not open" | Market is not opened |
| "One of type, mpid or token is missing or wrong" | Could not identify type/mpid/token in request |
| "Bad parameter type for Direct Market Access" | Parameter type in add request is wrong for DMA |
| "Bad parameter stop for Tailor-Made Order" | Parameter stop in add request is wrong for TMO |
| "Bad parameter stop for Volume Triggered Order" | Parameter stop in add request is wrong for VTO |
| "Bad parameter stop for Stop Loss Order" | Parameter stop in add request is wrong for SLO |
| "Bad parameter stop for Take Profit Order" | Parameter stop in add request is wrong for TPO |
| "Bad parameter participation for Volume Inline Order" | Parameter participation in add request is wrong for VIO |
| "Bad parameter type for Direct Strategy Access" | Parameter type in add request is wrong for DSA |
| "Bad parameter lifespan for Time Weighted Average Price Order" | Parameter lifespan in add request is wrong for TWAP |
| "Bad parameter lifespan for Volume Weighted Average Price Order" | Parameter lifespan in add request is wrong for VWAP |
| "Bad parameter type for Smart Order Routing" | Parameter type in add request is wrong for SOR |
| "Bad parameter priority for Smart Order Routing" | Parameter priority in add request is wrong for SOR |
| "Bad parameters" | Bad parameters for the request type |
| "Unknown Type" | Unknown request type |
Price Feed
General usage
Aplo gathers market data from all the venues it is connected to and its quoting engine generates quotes for volumes requested by customers. Quotes can then be executed using the dedicated route using a Fill-Or-Kill order.
- As the precision of prices and volumes are different for every platform we are connected to, we are unifying precision under the ticks and lots defined in universe.
- The minimum volume to request a quote is the lot. Maximum volumes can vary. Requesting a volume bigger than the maximum quoted volume will result in an empty quote (null price).
Production Websocket URL: wss://sheeldmatch.com:5443?mpid=ABCD
UAT Websocket URL: wss://uat.sheeldmatch.com:5443?mpid=ABCD
Authenticate using an API key HTTP header upon connecting
The server WILL disconnect every client that:
- does not send handshake HTTP request within 10s after opening the connection.
- does not subscribe to anything within 10s after handshake (trick: send empty subscription message to bypass this timeout while being subscribed to nothing).
- does not answer with a pong frame to a ping frame sent by the server within 10 seconds.
Every websocket frame sent by the server WILL be a text frame containing one JSON object as described in the JSON format. No compression of any kind WILL be used.
Every client is guaranteed to receive every message. A slow client will be dropped by the server.
The accepted parameters are the following:
| Paramater | Rule | Default | Value |
|---|---|---|---|
| mpid | Mandatory | None | a single mpid authorized to get the feed |
Useful ressources
Websocket Protocol Documentation: https://tools.ietf.org/html/rfc6455
Websocket in Python: https://pypi.org/project/websocket-client/
Websocket in JavaScript: https://javascript.info/websocket
JSON Format Documentation: https://www.json.org/json-en.html
Subscription Messages
Subscribe event:
# Example of request
{
"event": "subscribe",
"quotes":
[
{
"instrument": "BTC-USDT.SPOT",
"side": "buy",
"volume": "10.5"
},
{
"instrument": "BTC-USDT.SPOT",
"side": "sell",
"notional": "150000",
}
]
}
# Example of successful response
{
"event": "subscribed",
"quotes":
[
{
"instrument": "BTC-USDT.SPOT",
"side": "buy",
"volume": "10.5",
"success": true,
"error": ""
},
{
"instrument": "BTC-USDT.SPOT",
"side": "sell",
"notional": "150000",
"success": true,
"error": ""
}
],
"success": true,
"error": "",
"timestamp": "1643359449830582000",
"iso_timestamp": "2022-28-01T00:00:00Z"
}
# Example of partially successful response
{
"event": "subscribed",
"quotes":
[
{
"instrument": "BTC-USDT.SPOT",
"side": "buy",
"volume": "10.598943",
"success": false,
"error": "Bad Volume"
},
{
"instrument": "BTC-USDT.SPOT",
"side": "sell",
"notional": "150000",
"success": true,
"error": ""
}
],
"success": true,
"error": "",
"timestamp": "1643359449830582000",
"iso_timestamp": "2022-01-01T00:00:00Z"
}
# Example of already subscribed quotes
{
"event": "subscribed",
"quotes":
[
{
"instrument": "BTC-USDT.SPOT",
"side": "buy",
"volume": "10.5",
"success": false,
"error": "already subscribed"
},
{
"instrument": "BTC-USDT.SPOT",
"side": "sell",
"notional": "150000",
"success": false,
"error": "already subscribed"
}
],
"success": true,
"error": "",
"timestamp": "1643359449830582000",
"iso_timestamp": "2022-01-01T00:00:00Z"
}
Unsubscribe event:
# Example of request
{
"event": "unsubscribe",
"quotes":
[
{
"instrument": "BTC-USDT.SPOT",
"side": "buy",
"volume": "10.5"
},
{
"instrument": "BTC-USDT.SPOT",
"side": "sell",
"notional": "150000"
}
]
}
# Example of response
{
"event": "unsubscribed",
"quotes":
[
{
"instrument": "BTC-USDT.SPOT",
"side": "buy",
"volume": "10.5",
"success": true,
"error": ""
},
{
"instrument": "BTC-USDT.SPOT",
"side": "sell",
"notional": "150000",
"success": true,
"error": ""
}
],
"success": true,
"error": "",
"timestamp": "1643359449830582000",
"iso_timestamp": "2022-01-01T00:00:00Z"
}
Resubscribe event:
# Example of request
{
"event": "resubscribe",
"quotes":
[
{
"instrument": "BTC-USDT.SPOT",
"side": "buy",
"volume": "10.5"
},
{
"instrument": "BTC-USDT.SPOT",
"side": "sell",
"notional": "150000"
}
]
}
# Example of response
{
"event": "resubscribed",
"quotes":
[
{
"instrument": "BTC-USDT.SPOT",
"side": "buy",
"volume": "10.5",
"success": true,
"error": ""
},
{
"instrument": "BTC-USDT.SPOT",
"side": "sell",
"notional": "150000",
"success": true,
"error": ""
}
],
"success": true,
"error": "",
"timestamp": "1643359449830582000",
"iso_timestamp": "2022-01-01T00:00:00Z"
}
A subscription request JSON MUST contain the following JSON keys with their associated JSON values:
| Key | Value |
|---|---|
| event | one of "subscribe", "unsubscribe" OR "resubscribe" |
| quotes | a list of quote requests |
The subscribe event
The subscribe event is designed to subscribe to quote feeds. This event can be used multiple times to accumulate subscriptions into a connection. Subscription requests for already subscribed quotes are ignored.
The unsubscribe event
The unsubscribe event is designed to unsubscribe to specific quote feeds. This event can be used multiple times to remove subscriptions from a connection.
The resubscribe event
The resubscribe event is designed to unsubscribe to all currently subscribed quote feeds and subscribe to new feeds.
A quote subscription request is defined by the following parameters:
| Parameter | Rule | Value |
|---|---|---|
| instrument | Mandatory | one of the instruments described in universe |
| side | Mandatory | can be "buy" or "sell" |
| volume | Optional | volume of the quote |
| notional | Optional | notional value of the quote |
Only one of notional or volume NEEDS to be specified for every quote.
Sending a notional value creates a subscription that will have its quoted volume and price change to match the given notional value.
A subscription response JSON WILL contain the following JSON keys with their associated JSON values:
| Key | Value |
|---|---|
| event | one of "subscribed", "unsubscribed" OR "resubscribed" |
| quotes | a list of quote responses |
| success | can be true or false |
| error | error description if any |
| timestamp | time in nanoseconds |
| iso_timestamp | time in ISO format |
A quote subscription response is defined by the following parameters:
| Parameter | Value |
|---|---|
| instrument | one of the instruments described in universe |
| side | can be "buy" or "sell" |
| volume | volume of the quote (or empty when quoting notional) |
| notional | notional of the quote (or empty when quoting volume) |
| success | can be true or false |
| error | error description if any |
Quote Feed Messages
{
"event": "feed",
"quotes": [
{
"instrument": "BTC-USDT.SPOT",
"side": "buy",
"volume": "10.5",
"notional": "316001.7",
"price": "30095.4"
},
{
"instrument": "BTC-USDT.SPOT",
"side": "sell",
"volume": "4.9840",
"notional": "150000",
"price": "30095.9"
}
],
"success": true,
"error": "",
"timestamp": 1643359449830582000,
"iso_timestamp": "2022-01-01T00:00:00Z"
}
When subscribed to quote feeds, quotes are regularly updated through push messages. A message contains only the updated quotes. Unchanged quotes are absent from the message.
A push JSON quote WILL contain the following JSON keys with their associated JSON values:
| Key | Value |
|---|---|
| event | "feed" |
| quotes | a list of quotes |
| success | can be true or false |
| error | error description if any |
| timestamp | time in nanoseconds |
| iso_timestamp | time in ISO format |
A quote is defined by the following parameters:
| Parameter | Value |
|---|---|
| instrument | one of the instruments described in universe |
| side | can be "buy" or "sell" |
| volume | volume of the quote |
| notional | notional of the quote (volume * price) |
| price | price of the quote |
Depending on whether you provide notional or volume, the other side will be computed based on the given quote price.
Execute a Quote
Executing a quote corresponds to sending a Fill-Or-Kill (FOK) order with the price and volume of the quote received from the price feed. A FOK order cannot be partially executed. If the FOK order is sent before receiving an updated quote, the order is likely to be fully filled in a single execution. If the order is sent too late, the order may expire without any fills.
The interaction with this route follows the guidelines documented in the Trading section.
HTTP Request
POST /participants/:mpid/quotes/fok
import requests
endpoint = '%s/participants/%s/quotes/fok' % (base, mpid)
body = {
"token": "123456789",
"instrument": "BTC-USDT.SPOT",
"side": "buy",
"volume": "15",
"price": "11052.59"
}
response = requests.post(endpoint, headers=headers, json=body)
This endpoint requires the following parameters:
Body Parameters
| Parameter | Type | Description |
|---|---|---|
| token | string | A token provided by the participant to uniquely identify the order |
| instrument | string | Instrument Name as retrieved using the /instruments route |
| side | string | "buy", "sell" |
| volume | string | Quote size |
| price | string | Quote price |
Full Book Subscription Messages
Subscribe event:
# Example of request
{
"event": "subscribe",
"books": [
"BTC-USDT.SPOT",
"ETH-USDT.SPOT"
]
}
# Example of successful response
{
"event": "subscribed",
"books":
[
{
"instrument": "BTC-USDT.SPOT",
"success": true,
"error": ""
},
{
"instrument": "ETH-USDT.SPOT",
"success": true,
"error": ""
}
],
"success": true,
"error": "",
"timestamp": "1643359449830582000",
"iso_timestamp": "2022-28-01T00:00:00Z"
}
# Example of partially successful response
{
"event": "subscribed",
"books":
[
{
"instrument": "BTC-USDT.SPOT",
"success": false,
"error": "already subscribed"
},
{
"instrument": "ETH-USDT.SPOT",
"success": true,
"error": ""
}
],
"success": true,
"error": "",
"timestamp": "1643359449830582000",
"iso_timestamp": "2022-01-01T00:00:00Z"
}
Unsubscribe event:
# Example of request
{
"event": "unsubscribe",
"books": [
"BTC-USDT.SPOT",
"ETH-USDT.SPOT"
]
}
# Example of response
{
"event": "unsubscribed",
"books":
[
{
"instrument": "BTC-USDT.SPOT",
"success": true,
"error": ""
},
{
"instrument": "ETH-USDT.SPOT",
"success": true,
"error": ""
}
],
"success": true,
"error": "",
"timestamp": "1643359449830582000",
"iso_timestamp": "2022-01-01T00:00:00Z"
}
Resubscribe event:
# Example of request
{
"event": "resubscribe",
"books": [
"BTC-USDT.SPOT",
"ETH-USDT.SPOT"
]
}
# Example of response
{
"event": "resubscribed",
"books":
[
{
"instrument": "BTC-USDT.SPOT",
"success": true,
"error": ""
},
{
"instrument": "ETH-USDT.SPOT",
"success": true,
"error": ""
}
],
"success": true,
"error": "",
"timestamp": "1643359449830582000",
"iso_timestamp": "2022-01-01T00:00:00Z"
}
Additionally, it is possible to directly access the orderbook used to derive quotes through the books feed.
A subscription request JSON MUST contain the following JSON keys with their associated JSON values:
| Key | Value |
|---|---|
| event | one of "subscribe", "unsubscribe" OR "resubscribe" |
| books | a list of instruments described in universe |
The subscribe event
The subscribe event is designed to subscribe to book feeds. This event can be used multiple times to accumulate subscriptions into a connection. Subscription requests for already subscribed books are ignored.
The unsubscribe event
The unsubscribe event is designed to unsubscribe to specific book feeds. This event can be used multiple times to remove subscriptions from a connection.
The resubscribe event
The resubscribe event is designed to unsubscribe to all currently subscribed book feeds and subscribe to new feeds.
A subscription response JSON WILL contain the following JSON keys with their associated JSON values:
| Key | Value |
|---|---|
| event | one of "subscribed", "unsubscribed" OR "resubscribed" |
| books | a list of book responses |
| success | can be true or false |
| error | error description if any |
| timestamp | time in nanoseconds |
| iso_timestamp | time in ISO format |
A book subscription response is defined by the following parameters:
| Parameter | Value |
|---|---|
| instrument | one of the instruments described in universe |
| success | can be true or false |
| error | error description if any |
Full Book Feed Messages
{
"event": "feed",
"books": [
{
"type": "B",
"instrument": "BTC-USDT.SPOT",
"data": [
{
"depth": 0,
"price": 100000.0,
"volume": 1.0,
},
{
"depth": 1,
"price": 100000.0,
"volume": 1.0,
},
...
]
},
{
"type": "S",
"instrument": "BTC-USDT.SPOT",
"data": [
{
"depth": 0,
"price": 100000.0,
"volume": 1.0,
},
{
"depth": 1,
"price": 100000.0,
"volume": 1.0,
},
...
]
},
...
],
"success": true,
"error": "",
"timestamp": 1643359449830582000,
"iso_timestamp": "2022-01-01T00:00:00Z"
}
When subscribed to book feeds, books are regularly updated through push messages.
A push JSON book WILL contain the following JSON keys with their associated JSON values:
| Key | Value |
|---|---|
| event | "feed" |
| books | a list of book objects |
| success | can be true or false |
| error | error description if any |
| timestamp | time in nanoseconds |
| iso_timestamp | time in ISO format |
A book object is defined by the following parameters:
| Key | Value Type | Description |
|---|---|---|
| type | string | B if bids need to be updated OR S if asks need to be updated |
| instrument | string | name of the subscribed instrument |
| data → depth | number | depth of the orderbook level to update |
| data → price | number | new price of the level that replaces the old one |
| data → volume | number | new volume of the level that replaces the old one |
Price Stream Orders
Websocket usage
Aplo gathers market data from all the venues it is connected to and its quoting engine generates quotes for volumes requested by customers. Quotes can then be executed using the dedicated route using a Fill-Or-Kill order.
- As the precision of prices and volumes are different for every platform we are connected to, we are unifying precision under the ticks and lots defined in universe.
- The minimum volume to request a quote is the lot. Maximum volumes can vary. Requesting a volume bigger than the maximum quoted volume will result in an empty quote (null price).
Production Websocket URL: wss://sheeldmatch.com:5443?mpid=ABCD
UAT Websocket URL: wss://uat.sheeldmatch.com:5443?mpid=ABCD
Authenticate using an API key HTTP header upon connecting
The server WILL disconnect every client that:
- does not send handshake HTTP request within 10s after opening the connection.
- does not subscribe to anything within 10s after handshake (trick: send empty subscription message to bypass this timeout while being subscribed to nothing).
- does not answer with a pong frame to a ping frame sent by the server within 10 seconds.
Every websocket frame sent by the server WILL be a text frame containing one JSON object as described in the JSON format. No compression of any kind WILL be used.
Every client is guaranteed to receive every message. A slow client will be dropped by the server.
The accepted parameters are the following:
| Paramater | Rule | Default | Value |
|---|---|---|---|
| mpid | Mandatory | None | a single mpid authorized to get the feed |
Useful ressources
Websocket Protocol Documentation: https://tools.ietf.org/html/rfc6455
Websocket in Python: https://pypi.org/project/websocket-client/
Websocket in JavaScript: https://javascript.info/websocket
JSON Format Documentation: https://www.json.org/json-en.html
Subscription Message
Subscribe event:
# Example of request
{
"event": "subscribe",
"quotes":
[
{
"instrument": "BTC-USDT.SPOT",
"side": "buy",
"volume": "10.5",
"markup": "0.0001"
},
{
"instrument": "BTC-USDT.SPOT",
"side": "sell",
"notional": "150000",
"markup": "0.0005"
}
]
}
# Example of successful response
{
"event": "subscribed",
"quotes":
[
{
"instrument": "BTC-USDT.SPOT",
"side": "buy",
"volume": "10.5",
"markup": "0.0001",
"success": true,
"error": ""
},
{
"instrument": "BTC-USDT.SPOT",
"side": "sell",
"notional": "150000",
"markup": "0.0005",
"success": true,
"error": ""
}
],
"success": true,
"error": "",
"timestamp": "1643359449830582000",
"iso_timestamp": "2022-28-01T00:00:00Z"
}
# Example of partially successful response
{
"event": "subscribed",
"quotes":
[
{
"instrument": "BTC-USDT.SPOT",
"side": "buy",
"volume": "10.598943",
"markup": "0",
"success": false,
"error": "Bad Volume"
},
{
"instrument": "BTC-USDT.SPOT",
"side": "buy",
"volume": "10.3",
"markup": "23",
"success": false,
"error": "Markup out of range"
},
{
"instrument": "BTC-USDT.SPOT",
"side": "sell",
"notional": "150000",
"markup": "0",
"success": true,
"error": ""
}
],
"success": true,
"error": "",
"timestamp": "1643359449830582000",
"iso_timestamp": "2022-01-01T00:00:00Z"
}
# Example of already subscribed quotes
{
"event": "subscribed",
"quotes":
[
{
"instrument": "BTC-USDT.SPOT",
"side": "buy",
"volume": "10.5",
"markup": "0",
"success": false,
"error": "already subscribed"
},
{
"instrument": "BTC-USDT.SPOT",
"side": "sell",
"markup": "0",
"notional": "150000",
"success": false,
"error": "already subscribed"
}
],
"success": true,
"error": "",
"timestamp": "1643359449830582000",
"iso_timestamp": "2022-01-01T00:00:00Z"
}
Unsubscribe event:
# Example of request
{
"event": "unsubscribe",
"quotes":
[
{
"instrument": "BTC-USDT.SPOT",
"side": "buy",
"volume": "10.5",
"markup": "0"
},
{
"instrument": "BTC-USDT.SPOT",
"side": "sell",
"notional": "150000",
"markup": "0.0042"
}
]
}
# Example of response
{
"event": "unsubscribed",
"quotes":
[
{
"instrument": "BTC-USDT.SPOT",
"side": "buy",
"volume": "10.5",
"markup": "0",
"success": true,
"error": ""
},
{
"instrument": "BTC-USDT.SPOT",
"side": "sell",
"notional": "150000",
"markup": "0.0042",
"success": true,
"error": ""
}
],
"success": true,
"error": "",
"timestamp": "1643359449830582000",
"iso_timestamp": "2022-01-01T00:00:00Z"
}
Resubscribe event:
# Example of request
{
"event": "resubscribe",
"quotes":
[
{
"instrument": "BTC-USDT.SPOT",
"side": "buy",
"volume": "10.5",
"markup": "0.001"
},
{
"instrument": "BTC-USDT.SPOT",
"side": "sell",
"notional": "150000",
"markup": "0.087"
}
]
}
# Example of response
{
"event": "resubscribed",
"quotes":
[
{
"instrument": "BTC-USDT.SPOT",
"side": "buy",
"volume": "10.5",
"markup": "0.001",
"success": true,
"error": ""
},
{
"instrument": "BTC-USDT.SPOT",
"side": "sell",
"notional": "150000",
"markup": "0.0087",
"success": true,
"error": ""
}
],
"success": true,
"error": "",
"timestamp": "1643359449830582000",
"iso_timestamp": "2022-01-01T00:00:00Z"
}
A subscription request JSON MUST contain the following JSON keys with their associated JSON values:
| Key | Value |
|---|---|
| event | one of "subscribe", "unsubscribe" OR "resubscribe" |
| quotes | a list of quote requests |
The subscribe event
The subscribe event is designed to subscribe to quote feeds. This event can be used multiple times to accumulate subscriptions into a connection. Subscription requests for already subscribed quotes are ignored.
You can have multiple subscription for the same side/volume/notional but each with a different markup.
The unsubscribe event
The unsubscribe event is designed to unsubscribe to specific quote feeds. This event can be used multiple times to remove subscriptions from a connection.
The resubscribe event
The resubscribe event is designed to unsubscribe to all currently subscribed quote feeds and subscribe to new feeds.
A quote subscription request is defined by the following parameters:
| Parameter | Rule | Value |
|---|---|---|
| instrument | Mandatory | one of the instruments described in universe |
| side | Mandatory | can be "buy" or "sell" |
| volume | Optional | volume of the quote |
| notional | Optional | notional value of the quote |
| markup | Optional | Markup automatically applied to quote prices. Default: 0, Max:0.1, Max precision:0.0001 |
Only one of notional or volume NEEDS to be specified for every quote.
Sending a notional value creates a subscription that will have its quoted volume and price change to match the given notional value.
A subscription response JSON WILL contain the following JSON keys with their associated JSON values:
| Key | Value |
|---|---|
| event | one of "subscribed", "unsubscribed" OR "resubscribed" |
| quotes | a list of quote responses |
| success | can be true or false |
| error | error description if any |
| timestamp | time in nanoseconds |
| iso_timestamp | time in ISO format |
A quote subscription response is defined by the following parameters:
| Parameter | Value |
|---|---|
| instrument | one of the instruments described in universe |
| side | can be "buy" or "sell" |
| volume | volume of the quote (or empty when quoting notional) |
| notional | notional of the quote (or empty when quoting volume) |
| markup | markup of the quote |
| success | can be true or false |
| error | error description if any |
Quote Feed Message
{
"event": "feed",
"quotes": [
{
"instrument": "BTC-USDT.SPOT",
"side": "buy",
"volume": "10.5",
"notional": "316001.7",
"price": "30095.4"
},
{
"instrument": "BTC-USDT.SPOT",
"side": "sell",
"volume": "4.9840",
"notional": "150000",
"price": "30095.9"
}
],
"success": true,
"error": "",
"timestamp": 1643359449830582000,
"iso_timestamp": "2022-01-01T00:00:00Z"
}
When subscribed to quote feeds, quotes are regularly updated through push messages. A message contains only the updated quotes. Unchanged quotes are absent from the message.
A push JSON quote WILL contain the following JSON keys with their associated JSON values:
| Key | Value |
|---|---|
| event | "feed" |
| quotes | a list of quotes |
| success | can be true or false |
| error | error description if any |
| timestamp | time in nanoseconds |
| iso_timestamp | time in ISO format |
A quote is defined by the following parameters:
| Parameter | Value |
|---|---|
| instrument | one of the instruments described in universe |
| side | can be "buy" or "sell" |
| volume | volume of the quote |
| notional | notional of the quote (volume * price) |
| price | price of the quote (markup included) |
Depending on whether you provide notional or volume, the other side will be computed based on the given quote price.
Executing a Quote: Market Order
A Market Order will execute the requested volume or notional amount at current market price.
It is recommended to use the Price-Stream subscriptions in order to get up-to-date prices before sending a Market Order.
Due to market microstructure and volume precision constraints, notional orders will almost never be fully filled, though they will never overfill.
Any markup is incorporated in the execution price. For that reason, make sure that you use the same markup you may have used in the price-stream subscriptions.
The takeProfitTriggerPrice and stopLossTriggerPrice optional parameters lets you choose a trigger price before the order goes live.
For takeProfitTriggerPrice:
- If you are selling, the order will go live when the market price goes ABOVE the provided trigger price.
- If you are buying, the order will go live when the market price goes BELOW the provided trigger price.
For stopLossTriggerPrice:
- If you are selling, the order will go live when the market price goes BELOW the provided trigger price.
- If you are buying, the order will go live when the market price goes ABOVE the provided trigger price.
The interaction with this route follows the guidelines documented in the Trading section.
HTTP Request
POST /participants/:mpid/orders/pso/mkt
import requests
endpoint = '%s/participants/%s/orders/pso/mkt' % (base, mpid)
body = {
"token": "123456789",
"instrument": "BTC-USDT.SPOT",
"side": "buy",
"volume": "15",
"markup": "0.0012"
}
response = requests.post(endpoint, headers=headers, json=body)
This endpoint requires the following parameters:
Body Parameters
| Parameter | Type | Description |
|---|---|---|
| token | string | A token provided by the participant to uniquely identify the order |
| instrument | string | Instrument Name as retrieved using the /instruments route |
| side | string | "buy", "sell" |
| volume | string | optional Quote volume |
| notional | string | optional Quote notional |
| markup | string | optional Quote markup Default: 0, Max:0.1, Max precision:0.0001 |
| stopLossTriggerPrice | string | optional Stop loss trigger price |
| takeProfitTriggerPrice | string | optional Take profit trigger price |
Either volume or notional need to be provided.
You cannot provide BOTH stopLossTriggerPrice and takeProfitTriggerPrice.
Executing a Quote: Limit Order
A Limit Order will execute the requested volume or notional amount with a given limit price.
It is recommended to use the Price-Stream subscriptions in order to get up-to-date prices before sending a Limit Order.
Due to market microstructure and volume precision constraints, notional orders will almost never be fully filled, though they will never overfill.
Any markup is incorporated in the execution price. For that reason, make sure that you use the same markup you may have used in the price-stream subscriptions.
The takeProfitTriggerPrice and stopLossTriggerPrice optional parameters lets you choose a trigger price before the order goes live.
For takeProfitTriggerPrice:
- If you are selling, the order will go live when the market price goes ABOVE the provided trigger price.
- If you are buying, the order will go live when the market price goes BELOW the provided trigger price.
For stopLossTriggerPrice:
- If you are selling, the order will go live when the market price goes BELOW the provided trigger price.
- If you are buying, the order will go live when the market price goes ABOVE the provided trigger price.
The interaction with this route follows the guidelines documented in the Trading section.
HTTP Request
POST /participants/:mpid/orders/pso/lmt
import requests
endpoint = '%s/participants/%s/orders/pso/lmt' % (base, mpid)
body = {
"token": "123456789",
"instrument": "BTC-USDT.SPOT",
"side": "buy",
"volume": "15",
"price": "107935.23",
"timeInForce": "FOK",
"markup": "0.0012"
}
response = requests.post(endpoint, headers=headers, json=body)
This endpoint requires the following parameters:
Body Parameters
| Parameter | Type | Description |
|---|---|---|
| token | string | A token provided by the participant to uniquely identify the order |
| instrument | string | Instrument Name as retrieved using the /instruments route |
| side | string | "buy", "sell" |
| volume | string | optional Quote volume |
| notional | string | optional Quote notional |
| price | string | Quote price |
| timeInForce | string | "FOK", "GTC" or "IOC", see below for details |
| markup | string | optional Quote markup Default: 0, Max:0.1, Max precision:0.0001 |
| stopLossTriggerPrice | string | optional Stop loss trigger price |
| takeProfitTriggerPrice | string | optional Take profit trigger price |
Either volume or notional need to be provided.
You cannot provide BOTH stopLossTriggerPrice and takeProfitTriggerPrice.
The timeInForce parameter specifies the order's behaviour upon submission:
"FOK", Fill-Or-Kill: Executed wholly or immediately cancelled."IOC", Immediate-Or-Cancel: Executed, partially or wholly, when placed into the system. Any non-executed quantity is immediately cancelled."GTC", Good-Till-Cancel: Any unexecuted portion of the order will remain in effect until executed, cancelled by the entering party, or expiration.
Executing a Quote: Dollar-Cost-Average Order
A Dollar-Cost-Average Order will execute a requested volume or notional amount using market price at regular interval with a specified frequency.
Due to market microstructure and volume precision constraints, notional orders will almost never be fully filled, though they will never overfill.
Any markup is incorporated in the execution price.
The interaction with this route follows the guidelines documented in the Trading section.
HTTP Request
POST /participants/:mpid/orders/pso/dca
import requests
endpoint = '%s/participants/%s/orders/pso/dca' % (base, mpid)
body = {
"token": "123456789",
"instrument": "BTC-USDT.SPOT",
"side": "buy",
"volume": "15",
"frequency": "daily",
"start": "2025-10-01",
"markup": "0.0012"
}
response = requests.post(endpoint, headers=headers, json=body)
This endpoint requires the following parameters:
Body Parameters
| Parameter | Type | Description |
|---|---|---|
| token | string | A token provided by the participant to uniquely identify the order |
| instrument | string | Instrument Name as retrieved using the /instruments route |
| side | string | "buy", "sell" |
| volume | string | optional Quote volume |
| notional | string | optional Quote notional |
| frequency | string | "daily", "weekly" or "monthly", see below for details |
| start | string | optional A date in the format YYYY-MM-DD. |
| markup | string | optional Quote markup Default: 0, Max:0.1, Max precision:0.0001 |
Either volume or notional need to be provided.
The frequency and start parameters are used together to control when and how often the volume/notional amount will be executed.
Order will try to execute after 12 PM UTC, locates need to be allocated on the mpid used.
Here is the order behaviour regarding frequency:
"daily": Executes every day."weekly": Executes every 7 days."monthly": Executes on the provided day-of-month.
Here are the edge-cases regarding the frequency and start parameters.
| Frequency | Start | Current Date | First execution | Second Execution |
|---|---|---|---|---|
"daily" |
2025-10-31 | Fri, 2015-10-31 19:00:00 UTC | Sat, 2015-11-01 12:00:00 UTC | Sun, 2015-11-02 12:00:00 UTC |
"weekly" |
2025-10-31 | Fri, 2015-10-31 19:00:00 UTC | Sat, 2015-11-01 12:00:00 UTC | Sat, 2015-11-08 12:00:00 UTC |
"monthly" |
2025-10-31 | Fri, 2015-10-31 19:00:00 UTC | Sat, 2015-11-01 12:00:00 UTC | Mon, 2015-12-01 12:00:00 UTC |
"monthly" |
2025-10-31 | Fri, 2015-10-31 10:00:00 UTC | Fri, 2015-10-31 12:00:00 UTC | Sun, 2015-11-30 12:00:00 UTC |
Full Book Subscription Message
Subscribe event:
# Example of request
{
"event": "subscribe",
"books": [
"BTC-USDT.SPOT",
"ETH-USDT.SPOT"
]
}
# Example of successful response
{
"event": "subscribed",
"books":
[
{
"instrument": "BTC-USDT.SPOT",
"success": true,
"error": ""
},
{
"instrument": "ETH-USDT.SPOT",
"success": true,
"error": ""
}
],
"success": true,
"error": "",
"timestamp": "1643359449830582000",
"iso_timestamp": "2022-28-01T00:00:00Z"
}
# Example of partially successful response
{
"event": "subscribed",
"books":
[
{
"instrument": "BTC-USDT.SPOT",
"success": false,
"error": "already subscribed"
},
{
"instrument": "ETH-USDT.SPOT",
"success": true,
"error": ""
}
],
"success": true,
"error": "",
"timestamp": "1643359449830582000",
"iso_timestamp": "2022-01-01T00:00:00Z"
}
Unsubscribe event:
# Example of request
{
"event": "unsubscribe",
"books": [
"BTC-USDT.SPOT",
"ETH-USDT.SPOT"
]
}
# Example of response
{
"event": "unsubscribed",
"books":
[
{
"instrument": "BTC-USDT.SPOT",
"success": true,
"error": ""
},
{
"instrument": "ETH-USDT.SPOT",
"success": true,
"error": ""
}
],
"success": true,
"error": "",
"timestamp": "1643359449830582000",
"iso_timestamp": "2022-01-01T00:00:00Z"
}
Resubscribe event:
# Example of request
{
"event": "resubscribe",
"books": [
"BTC-USDT.SPOT",
"ETH-USDT.SPOT"
]
}
# Example of response
{
"event": "resubscribed",
"books":
[
{
"instrument": "BTC-USDT.SPOT",
"success": true,
"error": ""
},
{
"instrument": "ETH-USDT.SPOT",
"success": true,
"error": ""
}
],
"success": true,
"error": "",
"timestamp": "1643359449830582000",
"iso_timestamp": "2022-01-01T00:00:00Z"
}
Additionally, it is possible to directly access the orderbook used to derive quotes through the books feed.
A subscription request JSON MUST contain the following JSON keys with their associated JSON values:
| Key | Value |
|---|---|
| event | one of "subscribe", "unsubscribe" OR "resubscribe" |
| books | a list of instruments described in universe |
The subscribe event
The subscribe event is designed to subscribe to book feeds. This event can be used multiple times to accumulate subscriptions into a connection. Subscription requests for already subscribed books are ignored.
The unsubscribe event
The unsubscribe event is designed to unsubscribe to specific book feeds. This event can be used multiple times to remove subscriptions from a connection.
The resubscribe event
The resubscribe event is designed to unsubscribe to all currently subscribed book feeds and subscribe to new feeds.
A subscription response JSON WILL contain the following JSON keys with their associated JSON values:
| Key | Value |
|---|---|
| event | one of "subscribed", "unsubscribed" OR "resubscribed" |
| books | a list of book responses |
| success | can be true or false |
| error | error description if any |
| timestamp | time in nanoseconds |
| iso_timestamp | time in ISO format |
A book subscription response is defined by the following parameters:
| Parameter | Value |
|---|---|
| instrument | one of the instruments described in universe |
| success | can be true or false |
| error | error description if any |
Full Book Feed Message
{
"event": "feed",
"books": [
{
"type": "B",
"instrument": "BTC-USDT.SPOT",
"data": [
{
"depth": 0,
"price": 100000.0,
"volume": 1.0,
},
{
"depth": 1,
"price": 100000.0,
"volume": 1.0,
},
...
]
},
{
"type": "S",
"instrument": "BTC-USDT.SPOT",
"data": [
{
"depth": 0,
"price": 100000.0,
"volume": 1.0,
},
{
"depth": 1,
"price": 100000.0,
"volume": 1.0,
},
...
]
},
...
],
"success": true,
"error": "",
"timestamp": 1643359449830582000,
"iso_timestamp": "2022-01-01T00:00:00Z"
}
When subscribed to book feeds, books are regularly updated through push messages.
A push JSON book WILL contain the following JSON keys with their associated JSON values:
| Key | Value |
|---|---|
| event | "feed" |
| books | a list of book objects |
| success | can be true or false |
| error | error description if any |
| timestamp | time in nanoseconds |
| iso_timestamp | time in ISO format |
A book object is defined by the following parameters:
| Key | Value Type | Description |
|---|---|---|
| type | string | B if bids need to be updated OR S if asks need to be updated |
| instrument | string | name of the subscribed instrument |
| data → depth | number | depth of the orderbook level to update |
| data → price | number | new price of the level that replaces the old one |
| data → volume | number | new volume of the level that replaces the old one |
Market Analytics
The following analytics are consolidated data using our market data capture. The results may differ from other analytics tools using third party market data as Aplo may filter some data deemed incorrect and/or suspicious.
Get Analytic Universe
import requests
endpoint = '%s/analytics/universe' % (base)
response = requests.get(endpoint, headers=headers)
This route returns the current universe of available parameters for each analytic.
HTTP Request
GET /analytics/universe
Response fields
Example of JSON structure returned by the request:
{
"trades": {
"venue": ["Aggregated", "Huobi", "Binance"],
"lookback": ["1d", "1w", "1m", "1y"],
"geography": ["World", "Us", "Europe", "Asia"],
"dayOfWeek": ["AllWeek", "Monday"]
},
"volume": {
"venue": ["Aggregated", "Huobi", "Binance"],
"lookback": ["1d", "1w", "1m", "1y"],
"geography": ["World", "Us", "Europe", "Asia"],
"dayOfWeek": ["AllWeek", "Monday"]
},
"moneyFlow": {
"venue": ["Aggregated", "Huobi", "Binance"],
"lookback": ["1d", "1w", "1m", "1y"],
"geography": ["World", "Us", "Europe", "Asia"],
"dayOfWeek": ["AllWeek", "Monday"]
},
"volatility": {
"venue": ["Aggregated", "Huobi", "Binance"],
"lookback": ["1d", "1w", "1m", "1y"],
"geography": ["World", "Us", "Europe", "Asia"],
"dayOfWeek": ["AllWeek", "Monday"]
},
"seasonality": {
"venue": ["Aggregated", "Huobi", "Binance"],
"lookahead": ["1d", "1w", "1m", "1y"]
},
"marketImpact": {
"venue": ["Aggregated", "Huobi", "Binance"],
"lookahead": ["1d", "1w", "1m", "1y"]
}
}
The response is a JSON object with a key for each available analytic route and the possible values for the optional parameters they can take.
Get trades average size
import requests
endpoint = '%s/analytics/trades?instrument=%s' % (base, instrument)
response = requests.get(endpoint, headers=headers)
This analytic gives the typical size of trades of a pair on a venue based on specific lookbacks (one day, one week, one month), for a specific geography (trading hours) and a specific day in week.
This route takes the following query parameters:
| Parameter | Type | Description |
|---|---|---|
| instrument | string | Instrument name (can be retrieved using the /instruments route) |
| venue | string | optional Venue name (default: "Aggregated") |
| lookback | string | optional Lookback to filter with (default: "1d") |
| geography | string | optional Region to filter with (default: "World") |
| dayOfWeek | string | optional Specific day of the week (default: "AllWeek" ) |
Available optional parameter values can be retrieved with the /universe route.
HTTP Request
GET /analytics/trades
Response fields
Example of JSON structure returned by the request:
{
"instrument": "BTC-USDT.SPOT",
"lookback": "1d",
"size": "18000",
"venue": "Aggregated",
"geography": "World",
"dayOfWeek": "AllWeek"
}
The response is a JSON object with the following fields:
| Field | Type | Description |
|---|---|---|
| instrument | string | Name of the queried instrument |
| lookback | string | The specific lookback for this analytic |
| size | string | The average size of trades for the queried instrument and the venue/lookback pair |
| venue | string | The venue used to compute this analytic |
| geography | string | The trading hours that were used to filter the analytic |
| dayOfWeek | string | The day of the week used to filter the analytic |
Get daily average volume
import requests
endpoint = '%s/analytics/volume?instrument=%s' % (base, instrument)
response = requests.get(endpoint, headers=headers)
This analytic gives the average daily volume of a pair on a venue based on specific lookbacks (one day, one week, one month) for a specific geography (trading hours) and a specific day in week.
This route takes the following query parameters:
| Parameter | Type | Description |
|---|---|---|
| instrument | string | Instrument name (can be retrieved using the /instruments route) |
| venue | string | optional Venue name (default: "Aggregated") |
| lookback | string | optional Lookback to filter with (default: "1d") |
| geography | string | optional Region to filter with (default: "World") |
| dayOfWeek | string | optional Specific day of the week (default: "AllWeek" ) |
Available optional parameter values can be retrieved with the /universe route.
HTTP Request
GET /analytics/volume
Response fields
Example of JSON structure returned by the request:
{
"instrument": "BTC-USDT.SPOT",
"lookback": "1d",
"volume": "90",
"venue": "Aggregated",
"geography": "World",
"dayOfWeek": "AllWeek"
}
The response is a JSON object with the following fields:
| Field | Type | Description |
|---|---|---|
| instrument | string | Name of the queried instrument |
| lookback | string | The specific lookback for this analytic |
| volume | string | The average daily volume for the queried instrument and the venue/lookback pair |
| venue | string | The venue used to compute this analytic |
| geography | string | The trading hours that were used to filter the analytic |
| dayOfWeek | string | The day of the week used to filter the analytic |
Get money flow
import requests
endpoint = '%s/analytics/money-flow?instrument=%s' % (base, instrument)
response = requests.get(endpoint, headers=headers)
This analytic gives the ratio between buyers and sellers for a pair on a venue based on specific lookbacks (one day, one week, one month) for a specific geography (trading hours) and a specific day in week.
This route takes the following query parameters:
| Parameter | Type | Description |
|---|---|---|
| instrument | string | Instrument name (can be retrieved using the /instruments route) |
| venue | string | optional Venue name (default: "Aggregated") |
| lookback | string | optional Lookback to filter with (default: "1d") |
| geography | string | optional Region to filter with (default: "World") |
| dayOfWeek | string | optional Specific day of the week (default: "AllWeek" ) |
Available optional parameter values can be retrieved with the /universe route.
HTTP Request
GET /analytics/money-flow
Response fields
Example of JSON structure returned by the request:
{
"instrument": "BTC-USDT.SPOT",
"lookback": "1d",
"moneyFlow": "-0.03",
"venue": "Aggregated",
"geography": "World",
"dayOfWeek": "AllWeek"
}
The response is a JSON object with the following fields:
| Field | Type | Description |
|---|---|---|
| instrument | string | Name of the queried instrument |
| lookback | string | The specific lookback for this analytic |
| moneyFlow | string | The ratio between buyers, goes from -1 (market dominated by sellers) to 1 (market dominated by buyers) |
| venue | string | The venue used to compute this analytic |
| geography | string | The trading hours that were used to filter the analytic |
| dayOfWeek | string | The day of the week used to filter the analytic |
The following analytics are estimations of how the market is behaving. They as based on statistical analysis and models. All estimations are provided with a confidence level of 95%.
Get annualized volatility
import requests
endpoint = '%s/analytics/volatility?instrument=%s' % (base, instrument)
response = requests.get(endpoint, headers=headers)
This analytic estimates annualized volatility of a pair using Aplo’s proprietary quantitative models. This route takes the following query parameters:
| Parameter | Type | Description |
|---|---|---|
| instrument | string | Instrument name (can be retrieved using the /instruments route) |
| venue | string | optional Venue name (default: "Aggregated") |
| lookback | string | optional Lookback to filter with (default: "1d") |
| geography | string | optional Region to filter with (default: "World") |
| dayOfWeek | string | optional Specific day of the week (default: "AllWeek" ) |
Available optional parameter values can be retrieved with the /universe route.
HTTP Request
GET /analytics/volatility
Response fields
Example of JSON structure returned by the request:
{
"instrument": "BTC-USDT.SPOT",
"lookback": "1d",
"volatility": "0.09",
"venue": "Aggregated",
"geography": "World",
"dayOfWeek": "AllWeek"
}
The response is a JSON object with the following fields:
| Field | Type | Description |
|---|---|---|
| instrument | string | Name of the queried instrument |
| lookback | string | The specific lookback for this analytic |
| volatility | string | The rate of volatility of the queried instrument, in the range [0, 1] |
| venue | string | The venue used to compute this analytic |
| geography | string | The trading hours that were used to filter the analytic |
| dayOfWeek | string | The day of the week used to filter the analytic |
Get estimated seasonality
import requests
endpoint = '%s/analytics/seasonality?instrument=%s' % (base, instrument)
response = requests.get(endpoint, headers=headers)
This analytic estimates trade volume of a pair for specific lookahead (currently restricted to one day) using Aplo’s proprietary quantitative models. This route takes the following query parameters:
| Parameter | Type | Description |
|---|---|---|
| instrument | string | Instrument name (can be retrieved using the /instruments route) |
| venue | string | optional Venue name (default: "Aggregated") |
| lookahead | string | optional Lookahead to filter with (default: "1d") |
Available optional parameter values can be retrieved with the /universe route.
HTTP Request
GET /analytics/seasonality
Response fields
Example of JSON structure returned by the request:
{
"instrument": "BTC-USDT.SPOT",
"lookahead": "1d",
"seasonality": "98",
"venue": "Aggregated"
}
The response is a JSON object with the following fields:
| Field | Type | Description |
|---|---|---|
| instrument | string | Name of the queried instrument |
| lookahead | string | The specific lookahead for this analytic |
| seasonality | string | The estimated trade volume for the queried instrument |
| venue | string | The venue used to compute this analytic |
Get market impact
import requests
endpoint = '%s/analytics/marker-impact?instrument=%s&impact=%s&volume=%s' % (base, instrument, impact, volume)
response = requests.get(endpoint, headers=headers)
This route can represent three different analytic depending on the params used.
This route takes the following query parameters:
| Parameter | Type | Description |
|---|---|---|
| instrument | string | Instrument name (can be retrieved using the /instruments route) |
| impact | string | optional Rate of market impact (in the range [0, 1]) |
| volume | string | optional Notional volume |
| lookahead | string | optional Lookahead time |
The lookahead parameter must have the following format:
| Scale | Value |
|---|---|
| h | hour |
| d | day |
| w | week |
| m | month |
| y | year |
Examples of valid lookahead values: 2d, 3.5w, 1m;
This route MUST take exactly 2 of the 3 optional parameters impact, volume and lookahead.
The returned analytic depends on the 2 parameters supplied:
volume+impact: estimates the time required to trade specific volumes ($100k, $500k, $1m, $5m, $10m) while staying below specific market impact levels (0.25%, 0.50%, 1.00%, 2.50%, 5.00%);lookahead+volume: estimates the market impact over specific lookaheads (one hour, eight hours, one day) if specific volumes are traded ($100k, $500k, $1m, $5m, $10m).lookahead+impact: estimates the maximum volume over specific lookaheads (one hour, eight hours, one day) to be traded while staying below specific market impact levels (0.25%, 0.50%, 1.00%, 2.50%, 5.00%);
HTTP Request
GET /analytics/market-impact
Response fields
Example of JSON structure returned by the request:
{
"instrument": "BTC-USDT.SPOT",
"lookahead": "1d",
"volume": "67.23",
"impact": "0.01",
"venue": "Aggregated"
}
The response is a JSON object with the following fields:
| Field | Type | Description |
|---|---|---|
| instrument | string | Name of the queried instrument |
| lookahead | string | The provided lookahead OR the estimated time required to trade the provided volume while staying below the provided impact |
| volume | string | The provided volume OR the estimated volume over the provided lookahead to be traded while staying below the provided market impact |
| impact | string | The provided market impact OR the estimated market impact over the provided lookahead if the provided volume are traded |
| venue | string | The venue used to compute this analytic |
Virtual Accounts
The routes below relate directly to the virtual accounts themselves (creation/editing/list).
GET routes can take "virtualAccountId=XXX" as a query param, PATCH/PUT/POST routes can take "virtualAccountId" as a body param to indicate the specific virtual account to impact/use.
If no "virtualAccountId" (by default) is provided, the "Main Account" (indicated by a "virtualAccountId" of null) is used instead.
Create a virtual account
import requests
endpoint = '%s/accounts/%s/virtual-accounts/' % (base, accountId)
body = {
"email": "usr1+test@aplo.io",
"name": "Legitimate Businessperson",
}
response = requests.post(endpoint, headers=headers, json=body)
This route allows you to create a virtual account.
HTTP Request
POST /accounts/:accountId/virtual-accounts/
Body Parameters
| Parameter | Type | Description |
|---|---|---|
| name | string | Human readable name for this virtual account |
| string | optional Email address used to identify this virtual account |
Response fields
Example of JSON structure returned by the request:
{
"id": 9842,
"accountId": "13b65571-3efc-53c0-acf2-d6761f5d39cf",
"name": "Legitimate Businessperson",
"email": "usr1+test@aplo.io",
"earnRateTierId": null,
"createdAt": "2022-03-01T11:24:10.015Z"
}
The returned body is a JSON object with the following properties:
| Header | Type | Description |
|---|---|---|
| id | number | The virtual account Id |
| accountId | string | The virtual account's owner account Id |
| name | string | Name of the virtual account (can be null) |
| string | Email associated with the virtual account | |
| earnRateTierId | string | Earn rate tier Id, used for the earn product (can be null to indicate the same rate tier as the parent account) |
| createdAt | Date | Date of account creation |
Get all virtual accounts
import requests
endpoint = '%s/accounts/%s/virtual-accounts' % (base, accountId)
response = requests.get(endpoint, headers=headers)
This route returns all virtual accounts for the specified account. This route can take the following optional query parameters:
| Parameter | Type | Description |
|---|---|---|
| string | Email filter for virtual accounts that contains the filter | |
| name | string | Name filter for virtual accounts that contains the filter |
| id | string | Id filter for virtual accounts that contains the filter |
HTTP Request
GET /accounts/:accountId/virtual-accounts
Response fields
Example of JSON structure returned by the request:
{
"data": [
{
"id": 28545579,
"accountId": "13b65571-3efc-53c0-acf2-d6761f5d39cf",
"name": null,
"email": "usr1+virtual@aplo.io",
"earnRateTierId": null,
"createdAt": "2022-03-01T11:24:10.015Z"
},
{
"id": 1750416876,
"accountId": "13b65571-3efc-53c0-acf2-d6761f5d39cf",
"name": "Ursula Beohim",
"email": "usr2+virtual@aplo.io",
"earnRateTierId": null,
"createdAt": "2022-03-01T12:24:10.015Z"
}
],
"count": 329
}
Where data is a JSON array of objects with the following properties:
| Header | Type | Description |
|---|---|---|
| id | number | The virtual account Id |
| accountId | string | The virtual account's owner account Id |
| name | string | Name of the virtual account |
| string | Email associated with the virtual (can be null)account |
|
| earnRateTierId | string | Earn rate tier Id, used for the earn product (can be null to indicate the same rate tier as the parent account) |
| createdAt | Date | Date of account creation |
Get specific virtual account's information
import requests
endpoint = '%s/accounts/%s/virtual-accounts/%s' % (base, accountId, virtualAccountId)
response = requests.get(endpoint, headers=headers)
This route allows users to retrieve the information of a specific virtual account.
HTTP Request
GET /accounts/:accountId/virtual-accounts/:virtualAccountId
Response fields
Example of JSON structure returned by the request:
{
"id": 425221005,
"accountId": "13b65571-3efc-53c0-acf2-d6761f5d39cf",
"name": "Ursula Beohim",
"email": "usr2+virtual@aplo.io",
"earnRateTierId": null,
"createdAt": "2022-03-01T12:24:10.015Z"
}
Where data is a JSON array of objects with the following properties:
| Header | Type | Description |
|---|---|---|
| id | number | The virtual account Id |
| accountId | string | The virtual account's owner account Id |
| name | string | Name of the virtual account |
| string | Email associated with the virtual (can be null)account |
|
| earnRateTierId | string | Earn rate tier Id, used for the earn product (can be null to indicate the same rate tier as the parent account) |
| createdAt | Date | Date of account creation |
Modify a virtual account
import requests
endpoint = '%s/accounts/%s/virtual-accounts/%s' % (base, accountId, virtualAccountId)
body = {
"name": "Legitimate Businessdude",
}
response = requests.patch(endpoint, headers=headers, json=body)
This route allows you to modify a virtual account.
HTTP Request
PATCH /accounts/:accountId/virtual-accounts/:virtualAccountId
Body Parameters
| Parameter | Type | Description |
|---|---|---|
| name | string | optional Human readable name for this virtual account |
| string | optional Email address used to identify this virtual account |
Response fields
Example of JSON structure returned by the request:
{
"id": 9842,
"accountId": "13b65571-3efc-53c0-acf2-d6761f5d39cf",
"name": "Legitimate Businessdude",
"email": "usr1+test@aplo.io",
"earnRateTierId": null,
"createdAt": "2022-03-01T11:24:10.015Z"
}
The returned body is a JSON object with the following properties:
| Header | Type | Description |
|---|---|---|
| id | number | The virtual account Id |
| accountId | string | The virtual account's owner account Id |
| name | string | Name of the virtual account (can be null) |
| string | Email associated with the virtual account | |
| earnRateTierId | string | Earn rate tier Id, used for the earn product (can be null to indicate the same rate tier as the parent account) |
| createdAt | Date | Date of account creation |
Errors
The Aplo Trade API uses the following error codes:
| Error Code | Meaning |
|---|---|
| 400 | Bad Request -- Your request is invalid. |
| 401 | Unauthorized -- Your authentication is invalid. |
| 403 | Forbidden -- You do not have permission to access this endpoint. A missing User-Agent header may trigger it. |
| 404 | Not Found -- The specified ID could not be found. |
| 405 | Method Not Allowed -- You tried to access an endpoint with an invalid method. |
| 406 | Not Acceptable -- You requested a format that isn't json. |
| 410 | Gone -- This endpoint is no longer available. |
| 429 | Too Many Requests -- You're sending too many requests. |
| 500 | Internal Server Error -- We had a problem with our server. Try again later. |
| 503 | Service Unavailable -- We're temporarily offline for maintenance. Please try again later. |
Changelog
December 2025
November 2025
- Add
creation_timeto O WS messages - Add new staking routes
- The PUT /accounts/:accountId/addresses/:addressId/assets route has been DEPRECATED, you should instead use POST /accounts/:accountId/addresses and PATCH /accounts/:accountId/addresses/:addressId to set
assetsusing body parameters - The PUT /accounts/:accountId/banks/:bankId/assets route has been DEPRECATED, you should instead use POST /accounts/:accountId/banks to set
assetIdon creation, as it cannot be modified later.
October 2025
- Add new Price-Stream-Orders, LMT, MKT and DCA orders
- Add new
parametersand optionalvolumeandpricefor LMT, MKT and DCA orders for the WebSocket's Add Request - Add
executed_total,executed_volume,execution_countandrequestto the WebSocket's Order response volumeandrequestedVolumecan now be"null"for orders usingnotionalas a limit (LMT/MKT/DCA) in the WebSocket and HTTP endpoints.pricecan now be"null"for orders without a limit price (MKT/DCA) in the WebSocket and HTTP endpoints.- add rate-limiter information on relevant routes.
- add
assetUniverseto the GET /user route
August 2025
Banking overhaul:
Banking fees for both deposits and withdrawals have been added to Aplo's internal bank accounts.
You can now choose the bank account Aplo will use to send withdrawn funds.
- Add
sourceBankIdto the FIAT withdrawals parameters. - Add
conversionAssetIdfor FIAT withdrawals. This is used to indicate if a withdrawal will include an automatic conversion between USDC and USD (or EURC and EUR). - Add the GET internal banks route to get all available Aplo bank accounts used for FIAT deposits and withdrawals.
- The GET withdrawal destination route has been DEPRECATED, you should instead use GET withdrawal addresses for Crypto withdrawals, and both GET withdrawal banks and GET internal banks for FIAT withdrawal (in order to choose the bank account sending you the funds).
- Remove
internalBanksandexternalBanksfrom the GET deposit destinations route, it is now only used for Crypto assets. For FIAT deposits, use the new GET internal banks route - Rename
isLinkedtoisApprovedByAplofor the GET withdrawal banks route. - Changed
assetstoassetIdfor the GET withdrawal banks route. - Remove all
intermediaryBankXXXfields except forintermediaryBankIdfrom internal and withdrawal banks since they weren't used.
May 2025
- Replace
SIGNETmentions withBLINCnetwork for banks - Remove
Earningfeature as it has been discontinued - Add
High Touchsubmission route
April 2025
For security reasons, the User-Agent header is now required for all incoming requests. Lack of User-Agent will trigger a 403 error.
February 2025
DEPRECATE the following routes:
- POST /accounts/:accountId/addresses/:addressId/assets/:assetId route
- DELETE /accounts/:accountId/addresses/:addressId/assets/:assetId route
- POST /accounts/:accountId/banks/:bankId/assets/:assetId route
- DELETE /accounts/:accountId/banks/:bankId/assets/:assetId route
Add a new route
The parameter -day in the GET /participants/:mpid/orders/:token(-:day) route has been renamed to :initialDay to reflect its actual value and has been made MANDATORY for this route, you now have to indicate both the :token and :initialDay when calling this route.
Add virtualAccountId optional parameters to all /participants routes in addition to the Virtual Account Trading section for clarity
Fix margin_ratio to margin_ratio_distance for Risk Response
November 2024
Billing rework:
- remove
/accounts/:accountId/billing/settleroute. - change the return body of the GET /accounts/:accountId/billing/balance route
- add the GET /accounts/:accountId/invoices route
- add the POST /accounts/:accountId/invoices/:invoiceId/settle route
add the GET /accounts/:accountId/invoices/:invoiceId/token route
add
tokenfilter for GET /user/trades routeremove deprecated
/participants/:mpid/executionsroute, use the GET /user/trades route instead
October 2024
- Add
openOnlyfilter for GET /participants/:mpid/orders route
July 2024
- Add
mpidparameter in websocket price feed endpoint
March 2024
- Add
notionalquoting for the Price Feed - Migration towards
numbers as stringfor every endpoint dealing with decimals. - Remove deprecated
/ordersroutes
Authentication
The API authentication has been migrated from JSON Web Tokens(JWT) to API Keys.
- Remove deprecated
GET /auth/signinroute. - All authenticated endpoints now require the X-API-Key header with a valid API key as value.
February 2024
- Add Dust Conversion endpoints
- Change
accountoptional parameter toaccountIdin/user/XXXfund administration routes
January 2024
- Remove
minIncrementfrom the GET /assets route because it was obsolete and could be misinterpreted.
December 2023
- Clarify the meaning of
activityDatain the GET /user/activity route - Better behaviour for the GET /participants/:mpid/orders/:token route
- Market Data Websocket is now an authenticated route
November 2023
Improve Fund Administration routes /user/trades, /user/orders and /user/activity and sister /export routes:
- when pagination is used, limit the number of maximum rows returned for the given filters to 10000
- when pagination is not used (for both the normal and the
/exportroute), the response is sent in chunks (with transfer-encoding set tochunked) - no longer automatically select yesterday session's data if
startandendfilters are ommited: one of them is now mandatory - add
instrumentandvenueto Locates Requests
Add transferDuration and hasMemo to Universe Network route GET /networks
Improve external addresses (withdrawal addresses):
- Add
isUniversalproperty toexternalAddress, which allow you to use one address for all assets' withdrawal on the network of the address - Modify properties to
externalAddress: renaming some property names and add additional properties. (See GET /accounts/:accountId/addresses for details about the new external address properties) - Modify body parameters for Add new withdrawal address and Modify withdrawal address
- Add PUT /accounts/:accountId/addresses/:addressId/assets
October 2023
- Add
parametersto participant Order routes:/participants/:mpid/orders,/participants/:mpid/orders/:tokenand/participants/:mpid/orders/open - Add new Risk and End Of Sequence Trading Websocket messages
- Add
virtual_idto almost all trading websocket messages
September 2023
Improve all date/datetime query params and body params. They now support second/milliseconds/nanoseconds timestamps AND human readable strings
June 2023
- Add
activityTimeproperty for the GET /user/activity route - Add
activityTimeto the csv file returned by GET /user/activity/report route
April 2023
Handling of multi-day orders.
- Remove
vidandiidfrom Orders, addinitialDayandclosingTimein order to accomodate multi-day orders. - Add
initialDayandclosingTimefor the GET /user/orders route
January 2023
Split of addresses into internal and external addresses. Similarly to banks, internal addresses are Aplo's addresses and are used for deposits. External addresses are your addresses that are used for withdrawals.
- Rename
addressesproperty tointernalAddressesfor the GET /accounts/:accountId/deposit-destinations route - Rename
addressesproperty toexternalAddressesfor the GET /accounts/:accountId/withdrawals-destinations route - Rename
destinationTypevalue from"address"to"externalAddress"for withdrawal requests in POST /accounts/:accountId/withdrawal-requests route - Rename
destinationTypevalue from"address"to"externalAddress"AND renameaddresstoexternalAddressin thedetailssub-object for withdrawal information in the withdrawal requests history router - Rename
destinationAddresstodestinationInternalAddressand the value ofdestinationTypefrom"address"to"internalAddress"for the deposit history route - Rename
destinationAddresstodestinationExternalAddressand the value ofdestinationTypefrom"address"to"externalAddress"for the withdrawals history route - Remove
isInternalproperty from the withdrawal addresses route
Additional external address information has been added in the related routes
December 2022
Addition of bank accounts for fiat deposit/withdrawal.
- Add GET /addresses route
- Add GET /banks route
- Add POST /banks route
- Add POST /banks/:bankId/approve route
- Add PATCH /banks/:bankId route
- Add POST /banks/:bankId/assets/:assetId route
- Add DELETE /banks/:bankId/assets/:assetId route
- Split
banksproperty intointernalBanksandexternalBanksfor deposit destinations/accounts/:accountId/deposit-destinations - Add
externalBanksproperty for withdrawal destinations/accounts/:accountId/withdrawal-destinations - Add Fiat specific intructions for withdrawal-requests on SIGNET or other networks
- Remove
sourceAddressfrom fund-historypaymentfield. - Rename
banktoexternalBankfor fund-history withdrawal-requests - Split
sourceBankanddestinationBankintosourceInternalBank/sourceExternalBankanddestinationInternalBank/destinationExternalBankfor fund-history payments
October 2022
Major overhaul to the assets and instrument naming convention meant to accomodate future/put/call classifications moving forward.
Trading
- Add
midproperty to GET /user/trades route - Remove
currencyproperty from GET /instruments route as it is no longer useful. - Add
description,underlyingId,currencyId,issuingVenue,marginId,expiration,strikePriceanddurationCodeto GET /assets/ route andassetJSON object in general. - Remove
underlyingandclassificationproperty from GET /instruments route since it was moved to the asset level.
September 2022
Trading
- Add collateral locates route for perpetual trading.
- Add locates request list route for perpetual trading
Backoffice
- Allow transfer requests to have different virtual account id source and destination.
- Split
virtualAccountIdintosourceVirtualAccountIdanddestinationVirtualAccountIdto accomodate transfers potentially having different source and destination virtual account Id.
August 2022
Summary of changes: removed most of the virtual accounts routes in favor of updating previous ones to support virtual accounts. Added documentation about withdrawal addresses, billing and earning APIs
Virtual Accounts
- Add
virtualAccountIdquery and body params on most routes (see trading, backoffice and fund administration). All routes have a default value ofnullin order to behave exactly as before. - Removed ALL virtual accounts routes except those related to the actual virtual accounts (GET/POST)
- Add PATCH
/accounts/:accountId/virtual-accounts/:virtualAccountIdroute to modify email/name - Add
"email"and"id"optional query params for the GET virtual accounts route - Add
"virtualAccountId"property and"Virtual Account Id"csv column to the activity, trades and orders fund administration routes - Add
"virtualAccountId"to the balances route
Deposit/Withdrawal Destinations
- Rename
/accounts/:accountId/assets/deposit-destinationsto/accounts/:accountId/deposit-destinations(removed the/assetsnamespace) - Rename
/accounts/:accountId/assets/external-addressesto/accounts/:accountId/withdrawal-destinations
Deposit / Withdrawals / Withdrawal Requests / Transfer Requests
- Use GET
/accounts/:accountId/fund-historyroute to handle deposits/withdrawals/transfers retrieval - Remove GET
/accounts/:accountId/deposits,/accounts/:accountId/withdrawals,/accounts/:accountId/withdrawal-requestsand/accounts/:accountId/transfer-requestsin favor of fund-history.
Withdrawal Addresses
- Add several routes to manage withdrawal addresses
Earning
- Add several routes to use the Earn product