Polyester charges a spot trading fee on each fill. A fill that adds liquidity pays the maker rate, and a fill that takes liquidity pays the taker rate. Rates depend on the VIP tier of the Main Account and can vary by Pair. A maker rate can be negative, which means the fill earns a rebate.
Effective rates for your account
FeeService.GetSpotFeeRates (GET /v1/spot/fee-rates) returns the rates that apply
to the selected account right now, one row per Pair. Each row carries the Pair (symbolId on
ConnectRPC, a symbol such as BTC-USDT on REST), makerFeeRatePercent, takerFeeRatePercent, and
the vipTier used to resolve them. Fills are charged these effective rates. Each row also carries
the rates an order reserves its fee at, a breakdown of how the effective rates are made up, and nextChangeAt; see Size a buy and How your effective rates are made up.
Rates are percentage strings without %, of the fill's notional, so divide by 100 to get a fraction.
A negative maker rate is a rebate; the taker rate is never negative. Parse them as exact decimals,
not floating-point numbers.
- Omit the Pair filter to get every listed, enabled Pair. Request up to 100 Pairs with repeated
symbol(REST) orsymbolId(ConnectRPC) values. - Pass
subaccountIdto read a Subaccount. Without it, a Subaccount-scoped API key reads its bound Subaccount, and every other caller reads its Main Account. Rates always follow the VIP tier of the Main Account that owns the target account. - Session callers and API keys can call it. An API key needs
READ_SPOT.
Fees on each fill
The fee Asset is chosen per order with feeAsset. SELL fees are always in quote; a BUY can pay in
quote or have the fee deducted from the base it receives. See Fee Assets.
When a BUY pays its fee in quote, the order reserves its quote cost plus a fee allowance. Polyester
charges the actual fee per fill and returns any unused reserve to available as the order fills or
ends. See Balances and holds.
Each fill records its fee once it settles:
| Where | Fields |
|---|---|
Your trades (GetUserTrades) | feeAmountE18, feeAsset, and feeIsRebate for the individual fill. |
| Order reads | feeTotal, the net fee of the order's fills, with isRebate when rebates exceed fees. |
Ledger history (ListTransfers) | Fee and rebate legs with transferCode MAKER_FEE, TAKER_FEE, or REBATE. |
A fill's fee can appear shortly after the fill itself. A tier change applies to later fills; it does not recalculate fees on fills that already settled. A tier earned under a new VIP policy version takes effect only once that version is in force; until then, the account keeps its current tier and rates.
Size a buy that pays its fee in quote
Each GetSpotFeeRates row also carries the rates a new order reserves its fee at when the fee is
paid in quote:
reserveFeeRatePercent(ConnectRPCreserve_fee_rate_percent): the highest maker or taker rate of every VIP tier under the Pair's fee schedule. It is at least your effective rates, and discounts never lower it.postOnlyReserveFeeRatePercent: the same for a post-only limit order, from maker rates only.
A buy sized by a quote amount (maxQuoteDebit) reserves at the lower of reserveFeeRatePercent and takerFeeRatePercent.
Size the largest buy limit order your balance allows from the reserve rate r, not from your
effective maker or taker rate. Work in units of 10⁻¹⁸ of the quote Asset, whatever its own decimals:
N = ceil(price × qty) notional, rounded up to 10⁻¹⁸ quote
F = ceil(N × r / 100) fee reserve, rounded up to 10⁻¹⁸ quote
the order has sufficient quote balance when N + F ≤ availableDo not round N or F to the quote Asset's own decimals: that asks for more than the order
reserves, so the largest order that fits looks refused. The largest order is the largest quantity on
the Pair's step size that fits the balance and still passes the Pair's other limits, such as minimum
and maximum quantity, tick size, and maximum notional.
At a reserve rate of 0.15 and a price of 1, a buy of 998502.24663005 base reserves N + F = 999999.999999995075 quote and fits an available balance of 1000000. One more step does
not fit.
How your effective rates are made up
breakdown lists the components of the row's effective maker and taker rates, each with the time
it took effect and, when scheduled, the time it ends:
tierMakerFeeRatePercentandtierTakerFeeRatePercent: your VIP tier's rates under the fee policy in force.marketMultiplier, when set: the Pair's fee schedule, asmakerMultiplierPercentandtakerMultiplierPercent, witheffectiveFromand an optionaleffectiveTo.accountDiscounts: the account discounts in force, each withmakerDiscountPercent,takerDiscountPercent,allMarkets,effectiveFrom, and an optionaleffectiveTo. The highest maker discount and the highest taker discount apply, possibly from different discounts.referralDiscount, when set: the referral discount in force, in the same shape.
The effective rate is the tier rate times the multiplier, times one minus the highest account
discount, times one minus the referral discount: a 10% account discount and a 20% referral discount
on a 0.1% rate give 0.1% × 0.9 × 0.8 = 0.072%. Discounts apply only to a positive rate; a negative
maker rate, a rebate, is scaled by the multiplier alone. The SpotFeeRateBreakdown reference gives
the integer steps that reproduce the served rates exactly.
nextChangeAt, when present, is the earliest time a component starts or ends. Fetch the rates again
then. REST times are RFC 3339 UTC strings. REST always sends accountDiscounts, as [] when there
are none; ConnectRPC JSON omits an empty list and fields at their default value.
VIP tiers
The VIP tier is set for the Main Account, and every Subaccount inherits it. A tier is earned separately from two rolling 30-day metrics, and the higher result applies:
- Settled spot volume in USD across the Main Account and its Subaccounts
- Average portfolio value in USD across the same accounts
The qualification rules are explained in VIP tiers.
Tier catalog
VIPService.ListVIPTiers (GET /v1/vip/tiers) is public and cacheable. It returns the active policyVersion, its effectiveFrom time, the retentionThresholdBp used to keep an average
portfolio value tier near its boundary, and all eleven tiers. Each tier carries:
volumeThresholdUsdandaopThresholdUsd, the entry thresholds (VIP0 has no portfolio threshold)makerFeeRatePercentandtakerFeeRatePercent, the tier's base rates before Pair-specific adjustments
Use GetSpotFeeRates for the rate a Pair actually charges.
Your status
VIPService.GetVIPStatus (GET /v1/vip/status) returns the caller's Main Account
tier. It accepts interactive sessions only and has no Subaccount selector. API keys are refused with AUTH_SESSION_KIND_NOT_ALLOWED.
The response gives the effective tier and the tier each metric earns on its own (volumeTier, aopTier). Each rolling metric has a quality status: COMPLETE, PRICE_UNAVAILABLE, or INCOMPLETE_HISTORY, with the count of incomplete hours.
settledVolume30dUsdis a lower bound while its status isPRICE_UNAVAILABLE, and is omitted while it isINCOMPLETE_HISTORY.averageAop30dUsdis sent only while its status isCOMPLETE.nextTierThresholdsis omitted at VIP10.
An incomplete volume metric can raise the tier when its proven lower bound reaches a higher threshold, but never lowers it. An incomplete portfolio metric keeps its previous tier. A new account uses VIP0 until its first assignment, so the evaluation fields are omitted.
Network and route fees for deposits and withdrawals are separate from trading fees. See Deposit and withdrawal fees. Field schemas are in the API reference.