bitfinex-node-api
v4.3.1
Published
Bitfinex Node.js API
Downloads
67
Readme
Bitfinex Node.js API

Node.js client for the Bitfinex v1 API — public and authenticated REST plus the v1 WebSocket endpoint — and the v2 public REST + v2 authenticated REST + v2 WebSocket endpoints.
Installation
npm install bitfinex-node-apiUsage
PublicClient
import { PublicClient } from "bitfinex-node-api";
const client = new PublicClient();const ticker = await client.getTicker({ symbol: "BTCUSD" });const stats = await client.getStats({ symbol: "BTCUSD" });const book = await client.getFundingBook({
currency: "USD",
limit_bids: 10,
limit_asks: 5,
});const book = await client.getOrderBook({
symbol: "BTCUSD",
limit_bids: 20,
limit_asks: 10,
group: 1,
});const trades = await client.getTrades({
symbol: "BTCUSD",
timestamp: 1444266681,
limit_trades: 10,
});const lends = await client.getLends({
currency: "USD",
timestamp: 1444266681,
limit_lends: 10,
});const symbols = await client.getSymbols();const details = await client.getSymbolDetails();PublicClientV2
Client for the Bitfinex v2 public REST
endpoints (https://api-pub.bitfinex.com/v2/). Positional arrays are decoded
into typed objects with named fields. Trading and funding pairs share the same
endpoints; the decoded payload is discriminated by a type field. Trading
symbols start with t (e.g. tBTCUSD); funding currencies start with f
(e.g. fUSD).
import { PublicClientV2 } from "bitfinex-node-api";
const client = new PublicClientV2();const { status } = await client.getPlatformStatus(); // 0 = maintenance, 1 = operativeconst ticker = await client.getTicker({ symbol: "tBTCUSD" });
// { type: "trading_ticker", symbol, bid, bid_size, ask, ask_size,
// daily_change, daily_change_relative, last_price, volume, high, low }
const funding = await client.getTicker({ symbol: "fUSD" });
// { type: "funding_ticker", symbol, frr, bid, bid_period, bid_size,
// ask, ask_period, ask_size, daily_change, daily_change_relative,
// last_price, volume, high, low, frr_amount_available }const tickers = await client.getTickers({ symbols: ["tBTCUSD", "fUSD"] });
const all = await client.getTickers({ symbols: "ALL" });const history = await client.getTickersHistory({
symbols: ["tBTCUSD"],
limit: 100,
});const trades = await client.getTrades({
symbol: "tBTCUSD",
limit: 100,
sort: -1,
});
// [{ type: "trading_trade", id, mts, amount, price }, ...]
// or for funding: [{ type: "funding_trade", id, mts, amount, rate, period }, ...]const book = await client.getBook({
symbol: "tBTCUSD",
precision: "P0",
len: 25,
});
// Aggregated trading: { type: "book", price, count, amount }
// Raw trading (R0): { type: "raw_book", order_id, price, amount }
// Aggregated funding: { type: "funding_book", rate, period, count, amount }
// Raw funding (R0): { type: "raw_funding_book", offer_id, period, rate, amount }const last = await client.getStats({
key: "pos.size",
size: "1m",
symbol: "tBTCUSD",
side: "long",
section: "last",
});
const hist = await client.getStats({
key: "funding.size",
size: "1m",
symbol: "fUSD",
section: "hist",
limit: 100,
});const last = await client.getCandles({
timeframe: "1D",
symbol: "tBTCUSD",
section: "last",
});
const hist = await client.getCandles({
timeframe: "1m",
symbol: "fUSD",
section: "hist",
aggr: 30,
period_start: "2",
period_end: "30",
limit: 100,
});const configs = await client.getConfigs({
configs: ["pub:list:currency", "pub:list:pair:exchange"],
});const status = await client.getDerivativesStatus({ keys: ["tBTCF0:USTF0"] });const history = await client.getDerivativesStatusHistory({
key: "tBTCF0:USTF0",
limit: 100,
});const liquidations = await client.getLiquidations({ limit: 50 });const board = await client.getLeaderboards({
key: "plu",
timeframe: "3h",
symbol: "tBTCUSD",
limit: 25,
});const stats = await client.getFundingStats({ symbol: "fUSD", limit: 100 });const vasps = await client.getVASPs(); // [{ id, name }, ...]const avg = await client.getMarketAveragePrice({
symbol: "tBTCUSD",
amount: "1.0",
});
// { rate_avg, amount }const fx = await client.getForeignExchangeRate({ ccy1: "BTC", ccy2: "USD" });
// { current_rate }Error envelope
When a v2 endpoint replies with ["error", CODE, MESSAGE] (the documented
maintenance error
["error", 20060, "maintenance"] is one example), PublicClientV2 detects
the envelope in both get and post and rejects with a BitfinexError so
decoders never run on the malformed body.
import { BitfinexError, PublicClientV2 } from "bitfinex-node-api";
const client = new PublicClientV2();
try {
await client.getBook({ symbol: "tBTCUSD" });
} catch (error) {
if (error instanceof BitfinexError) {
// error.code — number (e.g. 20060 for maintenance)
// error.message — `Bitfinex error <code>: <text>`
}
}AuthenticatedClient
import { AuthenticatedClient } from "bitfinex-node-api";
const client = new AuthenticatedClient({
key: "BitfinexAPIKey",
secret: "BitfinexAPISecret",
});const info = await client.getAccountInfo();const fees = await client.getAccountFees();const summary = await client.getSummary();const address = await client.getDepositAddress({
method: "bitcoin",
wallet_name: "trading",
renew: 1,
});const permissions = await client.getKeyPermissions();const margin = await client.getMarginInformation();const balances = await client.getWalletBalances();const result = await client.transfer({
amount: "1.00954735",
currency: "BTC",
walletfrom: "trading",
walletto: "exchange",
});const result = await client.withdraw({
amount: "1.0",
withdraw_type: "bitcoin",
address: "1DKwqRhDmVyHJDL4FUYpDmQMYA3Rsxtvur",
walletselected: "exchange",
});const order = await client.newOrder({
amount: "1",
price: "3",
type: "limit",
symbol: "ETCUSD",
side: "buy",
is_postonly: true,
});const result = await client.newOrders({
orders: [
{
amount: "1",
price: "3",
type: "limit",
symbol: "ETCUSD",
side: "buy",
is_postonly: true,
},
{
amount: "2",
price: "2",
type: "limit",
symbol: "ETCUSD",
side: "buy",
},
],
});const order = await client.cancelOrder({ order_id: 446915287 });const { result } = await client.cancelOrders({ order_ids: [1, 2] });const { result } = await client.cancelAllOrders();const order = await client.replaceOrder({
order_id: 1,
amount: "3",
price: "101",
type: "limit",
symbol: "ETCUSD",
side: "sell",
is_postonly: true,
});const order = await client.getOrder({ order_id: 448411153 });const orders = await client.getOrders();const orders = await client.getOrderHistory({ limit: 50 });const positions = await client.getPositions();const position = await client.claimPosition({
position_id: 943715,
amount: "1.0",
});const history = await client.getBalanceHistory({
currency: "USD",
since: "1444277602.0",
});const history = await client.getDepositsWithdrawals({
currency: "BTC",
since: "1444277602.0",
limit: 10,
});const trades = await client.getPastTrades({
symbol: "BTCEUR",
limit_trades: 25,
reverse: 1,
});const offer = await client.newOffer({
currency: "USD",
amount: "50.0",
rate: "20.0",
period: 2,
direction: "lend",
});const offer = await client.cancelOffer({ offer_id: 13800585 });const offer = await client.offerStatus({ offer_id: 13800585 });const credits = await client.activeCredits();const offers = await client.getOffers();const offers = await client.offersHistory({ limit: 25 });const trades = await client.getFundingTrades({
symbol: "USD",
limit_trades: 1,
until: "1444141858.0",
});const funds = await client.getTakenFunds();const funds = await client.getUnusedFunds();const funds = await client.getTotalFunds();const funding = await client.closeFunding({ swap_id: 11576737 });const response = await client.closePosition({ position_id: 943715 });AuthenticatedClientV2
Client for the Bitfinex v2 authenticated REST
endpoints. The default base URL is https://api.bitfinex.com/v2/ — distinct
from the public https://api-pub.bitfinex.com/v2/ host that PublicClientV2
uses; override via the url option if needed. The class extends
PublicClientV2, so all v2 public methods are available on the same
instance, but they will hit whichever host the auth client was configured
with (so by default the public methods on an AuthenticatedClientV2 instance
go to api.bitfinex.com, which serves them with stricter rate limits than
api-pub.bitfinex.com). For high-volume public data, prefer a dedicated
PublicClientV2 instance. Each request is signed with HMAC-SHA384 over
/api/v2/{path}{nonce}{body} and sent with the bfx-apikey, bfx-nonce,
bfx-signature headers. Responses use the same BitfinexError envelope
handling as PublicClientV2, and write endpoints return a typed
INotificationV2<T> envelope.
import { AuthenticatedClientV2 } from "bitfinex-node-api";
const client = new AuthenticatedClientV2({
key: "BitfinexAPIKey",
secret: "BitfinexAPISecret",
});The default nonce generator (createMonotonicNonce()) is anchored to
Date.now() * 1000 (microsecond resolution) and bumps +1 whenever a second
call lands inside the same millisecond, so concurrent Promise.all and fast
sequential usage stay strictly increasing as Bitfinex requires. You can still
provide a custom generator via the nonce constructor option or the
client.nonce = ... setter.
Wallets
const wallets = await client.getWallets();Orders
const all = await client.getActiveOrders();
const btc = await client.getActiveOrders({ symbol: "tBTCUSD", id: [1] });const result = await client.submitOrder({
type: "EXCHANGE LIMIT",
symbol: "tBTCUSD",
amount: "0.01",
price: "50000",
});await client.updateOrder({ id: 1, price: "51000" });await client.cancelOrder({ id: 1 });await client.cancelOrdersMultiple({ all: 1 });await client.orderMulti({
ops: [
[
"on",
{ type: "LIMIT", symbol: "tBTCUSD", amount: "0.01", price: "50000" },
],
["oc", { id: 1 }],
],
});await client.getOrdersHistory({ symbol: "tBTCUSD", limit: 25 });await client.getOrderTrades({ symbol: "tETHUSD", id: 33963608932 });await client.getTradesHistory({ limit: 50, sort: -1 });await client.getOtcOrdersHistory({ symbol: "tBTCUSD" });await client.getLedgers({ currency: "USD", limit: 100 });Positions
await client.getMarginInfo({ key: "base" });
await client.getMarginInfo({ key: "tBTCUSD" });await client.getPositions();claimPositionincreasePositiongetIncreasePositionInfogetPositionsHistorygetPositionsSnapshotgetPositionsAuditupdatePositionFundingTypederivPositionCollateralSetderivPositionCollateralLimits
await client.claimPosition({ id: 142031891 });
await client.increasePosition({ symbol: "tBTCUSD", amount: "0.1" });
await client.getIncreasePositionInfo({ symbol: "tBTCUSD" });
await client.derivPositionCollateralSet({
symbol: "tBTCF0:USTF0",
collateral: 100,
});
await client.derivPositionCollateralLimits({ symbol: "tBTCF0:USTF0" });Margin Funding
getFundingOfferssubmitFundingOffercancelFundingOffercancelAllFundingOffersfundingClosefundingAutoRenewkeepFundinggetFundingOffersHistorygetFundingLoansgetFundingLoansHistorygetFundingCreditsgetFundingCreditsHistorygetFundingTradesHistorygetFundingInfo
await client.submitFundingOffer({
type: "LIMIT",
symbol: "fUSD",
amount: "50",
rate: "0.001",
period: 2,
});
await client.cancelAllFundingOffers({ currency: "USD" });
await client.getFundingLoans({ symbol: "fUSD" });
await client.getFundingInfo({ key: "fUST" });Account actions
getUserInfogetSummarygetLoginsHistorygetKeyPermissionsgenerateTokengetAuditHistorytransfergetDepositAddressgetDepositAddressAllgenerateDepositInvoicelnxInvoicePaymentswithdrawgetMovementsgetMovementInfogetAlertssetAlertdeleteAlertgetCalcOrderAvailablegetUserSettingssetUserSettingsdeleteUserSettings
await client.transfer({
from: "exchange",
to: "margin",
currency: "UST",
amount: "100",
});
await client.getDepositAddress({ wallet: "exchange", method: "bitcoin" });
await client.withdraw({
wallet: "exchange",
method: "ethereum",
amount: "0.1",
address: "0xabc...",
});
await client.setAlert({ type: "price", symbol: "tETHUSD", price: "185" });
await client.deleteAlert({ symbol: "tBTCUSD", price: 600 });
await client.getUserSettings({ keys: ["bit"] });
await client.setUserSettings({ settings: [["bit", "finex"]] });Thalex Derivatives
await client.thalexDeposit({
provider: "thalex",
amount: "1000",
ccy: "USE",
tfaToken: { method: "u2f" },
});
await client.thalexFreeTransferCount({ provider: "thalex" });WebSocketClient
The v1 WebSocket endpoint
(wss://api.bitfinex.com/ws/1). Raw array frames are parsed into typed
objects with named fields. Every channel message carries channel_id and
type so consumers can discriminate without parsing positional arrays.
import { WebSocketClient } from "bitfinex-node-api";
const ws = new WebSocketClient({
key: "BitfinexAPIKey", // optional, only required for `auth`
secret: "BitfinexAPISecret", // optional, only required for `auth`
});
ws.on("message", (message) => {
console.log(message);
});
await ws.connect();connect/disconnect
await ws.connect();
await ws.disconnect();ping— docs
const pong = await ws.ping();subscribeTicker— push every ticker update for a pair. Funding currencies such asUSD/fUSDare emitted asfunding_tickermessages.
const subscription = await ws.subscribeTicker({ pair: "BTCUSD" });
// Incoming `message` payload:
// {
// channel_id: 56771,
// type: "ticker",
// bid: 76892,
// bid_size: 5.80585799,
// ask: 76926,
// ask_size: 7.03177505,
// daily_change: 810,
// daily_change_perc: 0.01064893,
// last_price: 76874,
// volume: 1438.81140233,
// high: 76984,
// low: 74027,
// }const subscription = await ws.subscribeTicker({ pair: "USD" });
// Incoming `message` payload:
// {
// channel_id: 57169,
// type: "funding_ticker",
// currency: "USD",
// frr: 0.00039778,
// bid: 0.00029,
// bid_period: 2,
// bid_size: 21298086.02518276,
// ask: 0.00008723,
// ask_period: 2,
// ask_size: 123300.84010007,
// daily_change: -0.000015,
// daily_change_perc: -0.15,
// last_price: 0.00007587,
// volume: 1000,
// high: 0.0004,
// low: 0.00007,
// frr_amount_available: 42,
// }subscribeTrades— pushes one initial snapshot of recent trades, thentrade_executed(te) andtrade_updated(tu) live events. Funding currencies such asUSD/fUSDare emitted as funding trade messages.
const subscription = await ws.subscribeTrades({ pair: "BTCUSD" });
// Snapshot:
// { channel_id, type: "trades_snapshot", trades: [{ id, timestamp, price, amount }, ...] }
// Live update:
// { channel_id, type: "trade_executed", seq, timestamp, price, amount }
// { channel_id, type: "trade_updated", seq, id, timestamp, price, amount }
// Funding snapshot:
// { channel_id, type: "funding_trades_snapshot", currency,
// trades: [{ id, timestamp, amount, rate, period }, ...] }
// Funding live update:
// { channel_id, type: "funding_trade_executed", currency, id, timestamp, amount, rate, period }
// { channel_id, type: "funding_trade_updated", currency, id, timestamp, amount, rate, period }subscribeBook— aggregated order book (prec∈P0–P3). Funding currencies such asUSD/fUSDare emitted as funding book messages.
const subscription = await ws.subscribeBook({
pair: "BTCUSD",
prec: "P0",
freq: "F0",
len: 25,
});
// Snapshot:
// { channel_id, type: "book_snapshot", book: [{ price, count, amount }, ...] }
// Update:
// { channel_id, type: "book_update", price, count, amount }
// Funding snapshot:
// { channel_id, type: "funding_book_snapshot", currency,
// book: [{ rate, period, count, amount }, ...] }
// Funding update:
// { channel_id, type: "funding_book_update", currency, rate, period, count, amount }subscribeRawBook— raw order book at order-id granularity (prec = R0). Funding currencies are emitted as raw funding book messages.
const subscription = await ws.subscribeRawBook({ pair: "BTCUSD", len: 100 });
// Snapshot:
// { channel_id, type: "raw_book_snapshot", book: [{ order_id, price, amount }, ...] }
// Update:
// { channel_id, type: "raw_book_update", order_id, price, amount }
// Funding snapshot:
// { channel_id, type: "raw_funding_book_snapshot", currency,
// book: [{ offer_id, period, rate, amount }, ...] }
// Funding update:
// { channel_id, type: "raw_funding_book_update", currency, offer_id, period, rate, amount }- Heartbeats are emitted on any channel that has not pushed data for a second:
// { channel_id, type: "heartbeat" }unsubscribe
await ws.unsubscribe({ chanId: subscription.chanId });auth— authenticate to receive private channels. v1 accepts only the documented five fields (event,apiKey,authSig,authNonce,authPayload); the v2 extensionsfilter/dms/calcare not supported.
const response = await ws.auth();Once authenticated the server pushes private events on channel id 0. This
client decodes wallet snapshots and
updates:
// { channel_id: 0, type: "wallet_snapshot",
// wallets: [{ wallet_type, currency, balance, unsettled_interest, balance_available }, ...] }
// { channel_id: 0, type: "wallet_update",
// wallet_type, currency, balance, unsettled_interest, balance_available }Every other private message comes through as a generic envelope with the v1
mnemonic carried in type and the raw payload preserved:
// { channel_id: 0, type: "os" | "on" | "ou" | "oc" | ..., payload: [...] }Refer to the official docs to interpret each payload:
| type | Reference |
| ----------------------- | ------------------------------------------------------------------------------- |
| os/on/ou/oc | Orders |
| ps/pn/pu/pc | Positions |
| te/tu | Trades |
| fos/fon/fou/foc | Funding offers |
| fcs/fcn/fcu/fcc | Funding credits |
| fls/fln/flu/flc | Funding loans |
| fte/ftu | Funding trades |
| bu | Balance info |
| miu | Margin info |
| fiu | Funding info |
| n | Notifications |
unauth— drop the authenticated session without closing the socket. Failure comes back as{event: "error", code: 10201, ...}and is surfaced as a rejection.
await ws.unauth();send— send any raw payload to the server.
await ws.send({ event: "ping" });Aborting requests
Every method that returns a promise accepts an AbortSignal:
const controller = new AbortController();
setTimeout(() => {
controller.abort();
}, 1000);
const sub = await ws.subscribeTicker({ signal: controller.signal });WebSocketClientV2
Client for the Bitfinex v2 WebSocket
API. The default URL is wss://api-pub.bitfinex.com/ws/2 (public); when key
and secret are supplied it defaults to the authenticated host
wss://api.bitfinex.com/ws/2. Override either via the ws_url option. Raw
positional array frames are parsed into typed objects discriminated by a
type field (and channel_id). v2 subscriptions use symbol (e.g.
tBTCUSD, fUSD), unlike the v1 client's pair.
import { WebSocketClientV2 } from "bitfinex-node-api";
const ws = new WebSocketClientV2({
key: "BitfinexAPIKey", // optional, only required for `auth`
secret: "BitfinexAPISecret", // optional, only required for `auth`
});
ws.on("message", (message) => {
console.log(message);
});
await ws.connect();connect/disconnect
await ws.connect();
await ws.disconnect();ping— docs. The client tags each ping with an auto-incrementingcidand resolves with the matchingpong.
const pong = await ws.ping(); // { event: "pong", ts, cid }conf— change connection settings via bitwise flags. The exportedConfFlagsenumeratesTIMESTAMP,SEQ_ALL,OB_CHECKSUM,BULK_UPDATES.
import { ConfFlags } from "bitfinex-node-api";
await ws.conf({ flags: ConfFlags.OB_CHECKSUM }); // 131072
// Checksum frames then arrive as { channel_id, type: "checksum", checksum }subscribeTicker— trading symbols emitticker, funding currencies emitfunding_ticker.
const subscription = await ws.subscribeTicker({ symbol: "tBTCUSD" });
// { channel_id, type: "ticker", symbol, bid, bid_size, ask, ask_size,
// daily_change, daily_change_relative, last_price, volume, high, low }subscribeTrades— onetrades_snapshotthentrade_executed/trade_updated(te/tu). Funding currencies emitfunding_*variants. The v2 trade layout is[ID, MTS, AMOUNT, PRICE].
const subscription = await ws.subscribeTrades({ symbol: "tBTCUSD" });const book = await ws.subscribeBook({ symbol: "tBTCUSD", prec: "P0", len: 25 });
const raw = await ws.subscribeRawBook({ symbol: "tBTCUSD", len: 25 });const candles = await ws.subscribeCandles({ key: "trade:1m:tBTCUSD" });
// { channel_id, type: "candles_snapshot" | "candle_update", key, ... }subscribeStatus—deriv:SYMBOLemitsderivatives_status,liq:globalemitsliquidation_feed.
const deriv = await ws.subscribeStatus({ key: "deriv:tBTCF0:USTF0" });
const liq = await ws.subscribeStatus({ key: "liq:global" });unsubscribe
await ws.unsubscribe({ chanId: subscription.chanId });auth— authenticate to receive account events on channel id0.dms: 4enables the Dead-Man-Switch (cancel all orders on disconnect);filternarrows the delivered events.
const response = await ws.auth({ dms: 4, filter: ["trading", "wallet"] });Once authenticated, account frames are decoded into typed messages:
// { channel_id: 0, type: "wallet_snapshot", wallets: [...] }
// { channel_id: 0, type: "wallet_update", wallet_type, currency, balance, ... }
// { channel_id: 0, type: "position_snapshot" | "position_new" | "position_update" | "position_close", ... }
// { channel_id: 0, type: "order_snapshot" | "order_new" | "order_update" | "order_cancel", ... }
// { channel_id: 0, type: "balance_update", aum, aum_net }
// { channel_id: 0, type: "funding_offer_snapshot" | "funding_offer_new" | ..., ... }
// { channel_id: 0, type: "funding_credit_snapshot" | ..., ... }
// { channel_id: 0, type: "funding_loan_snapshot" | ..., ... }
// { channel_id: 0, type: "notification", mts, notification_type, status, text, ... }Account frames whose payload is not decoded (e.g. te/tu account trades,
miu margin info, fiu funding info, historical snapshots) come through as a
generic envelope with the
v2 mnemonic:
// { channel_id: 0, type: "auth_envelope", mnemonic: "miu", payload: [...] }unauth/send
await ws.unauth();
await ws.send({ event: "ping", cid: 1234 });Every method accepts an AbortSignal, and async iterators are available for
all public channels (tickers, trades, books, rawBooks, candles,
status):
for await (const update of ws.tickers({ symbol: "tBTCUSD" })) {
console.log(update);
}Signature
v1 signature (base64 payload + HMAC-SHA384, headers X-BFX-*):
import { signature } from "bitfinex-node-api";
const headers = signature({
key: "BitfinexAPIKey",
secret: "BitfinexAPISecret",
payload: Buffer.from(JSON.stringify({ request, nonce })).toString("base64"),
});v2 signature (HMAC-SHA384 over /api/v2/{path}{nonce}{body}, headers
bfx-apikey/bfx-nonce/bfx-signature):
import { signatureV2 } from "bitfinex-node-api";
const body = JSON.stringify({});
const nonce = `${Date.now() * 1000}`;
const headers = signatureV2({
key: "BitfinexAPIKey",
secret: "BitfinexAPISecret",
path: "auth/r/wallets",
nonce,
body,
});Test
npm test