client.fees is the spot fee surface: the maker and taker percents a fill is charged at, for one
account and market, plus the rates a new order reserves its fee at and the components behind the
effective rates. They are effective rates: the account's VIP tier, any market multiplier, and any
discounts are already applied. These are the numbers to quote in a UI or estimate a cost
from, not the catalog percents on VIP.
Every method is authenticated and account-scoped, so each input accepts an optional account field
("main", "active", or { subaccountId }). See Account scoping for how the default resolves.
Percents are decimal strings without a % sign ("0.02" is 0.02%). Maker can be negative (a
rebate), in -100 to 100. Taker is 0 to 100.
Methods
| Method | Summary |
|---|---|
getSpotRates | Effective and reserve spot rates with their breakdown, per market. |
getSpotRates(input?, options?)
Fetches a SpotFeeRate[] for the resolved account, ordered by symbolId ascending. The list is
a finite snapshot, not a page. Input is optional.
Omit symbolIds, or pass [], to load every currently listed, non-disabled spot market. Pass up
to 100 unique symbolId values to restrict the response. IDs must be integers from 1 through
4,294,967,295. The SDK drops duplicates before the request leaves your process.
await client.catalog.ensureReady();
const rates = await client.fees.getSpotRates();
for (const rate of rates) {
const symbol = client.catalog.market.requirePairSymbolBySymbolId(rate.symbolId);
console.log(symbol, rate.makerFeeRatePercent, rate.takerFeeRatePercent);
}
const symbolId = client.catalog.market.requireSymbolIdByPairSymbol("BTC-USDT");
const [btc] = await client.fees.getSpotRates({
symbolIds: [symbolId],
account: "main",
});Use the catalog to turn "BTC-USDT" into a symbolId. Passing a symbol string is a validation
error.
Right after a policy is created for or attached to a Subaccount, a Subaccount-scoped call can
briefly fail with a retryable unavailable error (HTTP 503) while the new policy takes effect.
Retry with backoff for a few seconds. A permission denial remains final.
GetSpotFeeRatesInput
| Field | Type | Required | Notes |
|---|---|---|---|
symbolIds | number[] | Market ids. Max 100 unique values. Default [] (every market). | |
account | AccountScope | Scope override. |
SpotFeeRate
vipTier is the tier the row was resolved at (0 through 10), so you can show which tier earned a
rate without a second call to VIP.
interface SpotFeeRate {
symbolId: number;
makerFeeRatePercent: string; // "0.02" is 0.02%; may be negative (rebate)
takerFeeRatePercent: string; // 0 to 100
vipTier: number; // 0 through 10
reserveFeeRatePercent: string; // 0 to 100
postOnlyReserveFeeRatePercent: string; // 0 to 100
breakdown: SpotFeeRateBreakdown;
nextChangeAt?: number; // epoch ms
}
interface SpotFeeRateBreakdown {
tierMakerFeeRatePercent: string;
tierTakerFeeRatePercent: string;
marketMultiplier?: {
makerMultiplierPercent: string;
takerMultiplierPercent: string;
effectiveFrom: number; // epoch ms, inclusive
effectiveTo?: number; // epoch ms, exclusive
};
accountDiscounts: SpotFeeDiscount[]; // [] when none
referralDiscount?: SpotFeeDiscount;
}
interface SpotFeeDiscount {
makerDiscountPercent: string;
takerDiscountPercent: string;
allMarkets: boolean; // false: selected markets, this one included
effectiveFrom: number; // epoch ms, inclusive
effectiveTo?: number; // epoch ms, exclusive; absent with no scheduled end
}Reserve rates
A new order whose fee is paid in the quote asset reserves that fee at reserveFeeRatePercent: the
highest maker or taker rate of any VIP tier under the market's fee schedule. It is at least the
effective rates, and discounts never lower it. Size buy orders by the reserve rate, not the
effective one: a buy limit order has enough quote balance when its notional plus that notional
times the reserve rate is at most the available balance. A post-only limit order reserves at postOnlyReserveFeeRatePercent, the highest maker rate of any tier. A buy sized by a quote amount
reserves at the lower of the reserve rate and takerFeeRatePercent. Fills charge the effective
rate, and the rest of the reserve is released as the order fills or ends.
Breakdown
breakdown lists the components of the effective rates. Applying them to the maker and taker
rates separately, in this order, reproduces the effective rates: start from the VIP tier rate,
apply marketMultiplier when set, then the highest discount in accountDiscounts, then referralDiscount. The steps multiply: a 10% account discount and a 20% referral discount on a
0.1% rate leave 0.1% × 0.9 × 0.8 = 0.072%. A negative maker rate is a rebate, which only the
multiplier scales. The highest maker discount and the highest taker discount can come from
different entries of accountDiscounts.
To reproduce the rates exactly, work in integers: a rate R is its percent times 10,000,000, and
a multiplier M or discount D is its percent times 100. The multiplier step is R = trunc((R * M + 5000) / 10000), and each discount step, applied only while R is above 0, is R = trunc((R * (10000 - D) + 5000) / 10000).
nextChangeAt is the earliest time a component starts or ends, such as a discount ending or a new
VIP tier taking effect. The effective and reserve rates may change then, or stay the same. It is undefined when no change is scheduled; read the rates again after it passes.
Related
- VIP to read the public tier catalog or the caller's root-account qualification.
- Trading to place orders that fill at these rates.
- Trading fees for the published schedule and fee-asset rules.