AllXBTUSD6469.5+0.61%XBTU186412.5+0.61%XBTZ186417.5+0.63%XBT7D_U1050.00010-92.06%XBT7D_D950.00009-91.26%ADAU180.00001565+4.13%BCHU180.0840+1.20%EOSU180.0007392+0.16%ETHUSD299.30+1.05%ETHU180.04623+0.83%LTCU180.00891+0.00%TRXU180.00000314+6.44%XRPU180.00004874+6.93%.BXBT6470.02+0.57%.ETHXBT0.04612+0.35%.BVOL24H3.75-12.79%Funding: 00:04:02 @ -0.0566%Time: 11:55:57 AM
REST API

Using the BitMEX REST API

For working code and examples, please see our HTTP Connectors on GitHub.

If you are logged in, you may access the API Key Management interface.

For a list of endpoints and return types, view the REST documentation in the API Explorer.

Specification and Clients

The BitMEX API conforms to the Swagger spec for REST endpoints. Any Swagger-compatible client can connect to the BitMEX API and execute commands.

An updated list of available clients is listed here.

Examples of basic communication to our API are in our api-connectors repository.

Note that all Bitcoin quantities are returned in Satoshis: 1 XBt (Satoshi) = 0.00000001 XBT (Bitcoin).

Authentication

To access private endpoints, a permanent API key is required.

Details about authentication via API Key are available via a separate document.

Limits

Request Rate Limits

Requests to our REST API are rate limited to 300 requests per 5 minutes. This counter refills continuously. If you are not logged in, your ratelimit is 150/5minutes.

Be very careful about the number of errors your tools throw! If a large number of 4xx or 5xx responses are delivered in a short period of time, your IP may be banned for an hour. Multiple bans in a short time will result in a week ban.

Viewing Your Request Rate Limit

On each request to the API, these headers are returned:

"x-ratelimit-limit": 300
"x-ratelimit-remaining": 297
"x-ratelimit-reset": 1489791662

Use these headers to determine your current limit and remaining requests. At the UNIX timestamp designated by x-ratelimit-reset, you will have enough requests left to retry your current request. If you have not exceeded your limit, this value is always the current timestamp.

If you are limited, you will receive a 429 response and an additional header, Retry-After, that indicates the number of seconds you should sleep before retrying.

Increasing Your Request Rate Limit

If you are running up against our limits and believe that you have a legitimate need, please email us at support@bitmex.com to discuss upgrading your access limits.

Before increasing your rate limits, we require that your programs at least:

  • Use the WebSocket feeds to avoid polling data.
  • Use our bulk order, bulk amend, and bulk cancel features to reduce load on the system.
    • Due to how BitMEX does real-time auditing, risk checks, and margining, orders submitted, amended, and canceled in bulk are faster to execute. For this reason, bulk actions are ratelimited at 1/10 the normal rate!
    • Bulk cancels, regardless of count, always only count as one request.

When emailing us about a ratelimit increase, please include:

  • Your application’s purpose and intended growth
  • Your desired rate limit
  • Acknowledgement that your program is using the API efficiently, as mentioned above.

Order Count Limits

To keep an orderly market, BitMEX imposes limits on the number of open orders per account. These limits are:

  1. Maximum 200 open orders per contract per account;
  2. Maximum 10 stop orders per contract per account;
  3. Maximum 10 contingent orders per contract per account.

When placing a new order that causes these caps to be exceeded, it will be rejected with the message “Too many [open|stop|contingent] orders”.

Order Minimum Size Limits

We intentionally set the contract sizes of BitMEX products at low values to encourage traders both large and small to trade on BitMEX. However, some traders abuse this and spam the orderbook or trade feed with many small orders.

Accounts with too many open orders with a gross value less than 0.0025 XBT each will be labeled as a Spam Account.

If you are marked as a Spam Account:

  • Orders below 0.0025 XBT in value will automatically become hidden orders.
  • Hidden orders do not show in the orderbook and always pay the taker fee.
  • Post-Only spam orders will be Rejected instead of being hidden.
  • Too many spam orders may be grounds to temporarily ban an account from trading.
  • Spam Account designations are re-evaluated and lifted automatically every 24 hours if user behavior has changed.

WebSocket Limits

WebSocket Limits are documented on the WebSocket API page.

HTTP Keep-Alive

BitMEX does not support placing or canceling orders via WebSocket, only via HTTP.

Our servers support HTTP Keep-Alive and cache SSL sessions. If you keep a connection alive, you will get websocket-like latency, obviating the need to use the websocket for transactional communication.

Our Keep-Alive timeout is 90 seconds.

Overload

Due to growth in the crypto space, BitMEX is currently under extremely high load.

To help improve responsiveness during high-load periods, the BitMEX trading engine will begin load-shedding when requests reach a critical queue depth. When this happens, you will quickly receive a 503 status code with the message "The system is currently overloaded. Please try again later." The request will not have reached the engine, and you should retry after at least 500 milliseconds.

We will keep clients updated as we improve peak capacity on the trading engine.

Filtering

Many table endpoints take a filter parameter. This is expected to be JSON. For example, the filter query {"side":"Buy"} can be url-encoded and sent to the trade endpoint (click to run).

Most values can only be filtered by simple equality. Timestamps, which are all UTC, can be queried in many ways:

Timestamp Filters

The following fields can be passed in the "filter" param as JSON key/value pairs:

Key Description Example Example Description
"startTime" Start timestamp. "2014-12-26 11:00" On or after 11:00am on 26 December 2014.
"endTime" End timestamp. "2014-12-26 13:00" On or before 1:00pm on 26 December 2014.
"timestamp" Exact timestamp. "2014-12-26 12:00" Exactly noon on 26 December 2014.
"timestamp.date" Exact day. "2014-12-26" The entire day of 26 December 2014.
"timestamp.month" Exact month. "2014-12" The entire month of December 2014.
"timestamp.year" Exact year. 2014 The entire year of 2014.
"timestamp.mm" Month of year. 12 December of each year.
"timestamp.dd" Day of month. 26 26th of each month.
"timestamp.ww" Day of week. 6 Friday of each week. 0 = Sat, 1 = Sun
"timestamp.time" Exact time. "12:00:00.000" Exactly noon of each day.
"timestamp.second" Exact second. "12:00:00" The entire second from noon of each day.
"timestamp.minute" Exact minute. "12:00" The entire minute from noon of each day.
"timestamp.hh" Hour of day. 12 12th hour of each day. (i.e. noon)
"timestamp.uu" Minute of hour. 30 30th minute of each hour.
"timestamp.ss" Second of minute. 15 15th second of each minute.

For example, the .BVOL7D index is calculated and published on the trade feed every 5 minutes. To filter to just noon on Fridays, send the payload:

{"symbol": ".BVOL7D", "filter": {"timestamp.time":"12:00", "timestamp.ww":6}}

(Click to run)

OrderBookL2

A special note on the orderBookL2 table, which is the canonical table for orderbook updates and the only way to retrieve all levels:

This orderbook is keyed by a unique ID, not price, so that all levels are unique across all symbols. This may be unintuitive at first but ensures that each level across the entire system is uniquely keyed. Therefore, when you retrieve an orderBookL2 update, it may look like this:

{"table":"orderBookL2","action":"update","data":[{"symbol":"XBTUSD","id":8798952400,"side":"Sell","size":8003}]}

Notice that this does not include the price, which you should already have set on the level. The process for handling updates, inserts, deletes, and partials on this stream is exactly the same as any other stream and requires no special handling. However, some tooling may make assumptions about book entries, like keying them by price.

In that case, there are a few ways to handle this:

  1. Keep a local hashmap of ids you’ve seen to their price. When you receive an update or delete, look up the price in this map. This is simple but will consume some memory.
  2. Use the following formula to reverse-engineer the ID:
ID = (100000000 * symbolIdx) - (price / instrumentTickSize)
price = ((100000000 * symbolIdx) - ID) * instrumentTickSize

Definitions:

  • symbolIdx is the index of the instrument in the list of instruments
  • instrumentTickSize as the instrument’s tickSize property.
    • Due to in-flight changes of tickSize on some XBT contracts, an override may need to be applied. See below.

This can be written as:

// This is a compatibility change as the tick sizes of live instruments changed in-flight. If you are listing
// these instruments, you must use their original tick as part of your calculations. If not, this can be ignored,
// and you can use `instrument.tickSize` directly.
const LEGACY_TICKS = {"XBTUSD":0.01};
function instrumentTickSize(instrument) {
  return legacyTicks[instrument.symbol] || instrument.tickSize;
}

// You should have a copy of the full instruments list on startup.
// Fetch from: https://www.bitmex.com/api/v1/instrument?columns=symbol,tickSize&start=0&count=500
const instrumentsList = fetchInstrumentsFromBitMEX();
function getInstrumentAndIdx(symbol) {
  const instrument = instrumentsList.find((i) => i.symbol === symbol);
  const instrumentIdx = instrumentsList.indexOf(instrument);
  return [instrument, instrumentIdx];
}

// To get a price from an ID:
export function priceFromID(id, symbol) {
  const [instrument, instrumentIdx] = getInstrumentAndIdx(symbol);
  return ((100000000 * instrumentIdx) - id) * instrumentTickSize(instrument);
}

// And reversed:
export function IDFromPrice(price, symbol) {
  const [instrument, instrumentIdx] = getInstrumentAndIdx(symbol);
  return (100000000 * instrumentIdx) - (price / instrumentTickSize(instrument));
}

Applied to our update above, where the ID was 8798952400, you should get a resulting price of 10476:

price = ((1e8 * symbolIdx) - ID) * instrumentTickSize
10476 = ((100000000 * 88) - 8798952400) * 0.01