Skip to main content
Version: v2

Rates & Pricing

How quoting works: rate semantics, quote lifetimes, and the recommended create-order flow. The endpoint reference lives at Rates; this page explains the model.

Pair naming and rate direction​

A pair name is {to}{from} — the receive currency first, then the send currency. BTCUSDC means "you send USDC, you receive BTC". Every rate row spells the direction out explicitly:

  • from — what you send (deposit currency)
  • to — what you receive (withdraw currency)

rate is quoted as deposit units per 1 receive unit (the marked-up ask). It does not include fixed fees. The amount you receive is therefore:

withdraw_amount = deposit_amount / rate − withdrawal_fee

where withdrawal_fee is the combined fixed fees expressed in the receive (to) currency.

Don't compute user-facing quotes by hand

Instead of applying the formula client-side, pass deposit_amount to GET /rate/ with exactly one pair — the response then includes a withdraw_amount preview computed through the same pricing path used at order creation. That is the exact number to show the user.

Pricing modes​

ModeRequestrate_idexpiration_time_unixBehavior
CeFi fixed (default)is_fixed_rate=true or omitpresentpresentQuote snapshot stored; pass rate_id at order creation to bind validation to it
CeFi floatingis_floating_rate=trueabsentabsentNo price lock; the order executes at the live rate at creation time
DeFi floatingis_defi=truepresentpresentIndicative pricing; amount determined at execution
DeFi fixedis_defi=true&is_fixed_rate=truepresentpresentThe row's rate is what the order promises; see DeFi fixed-rate quotes below
DeFi limitorder-level is_limit_ordern/an/aNo quote at all — you set limit_receive_amount; see Orders

is_fixed_rate and is_floating_rate are mutually exclusive. is_floating_rate is CeFi-only and ignored for DeFi.

DeFi fixed-rate quotes​

Every DeFi fixed-rate row is bindable: the rate you display is the rate the order promises. The flow has three steps:

  1. GET /rate/?is_defi=true&is_fixed_rate=true (filtered to the pairs you show) to display prices.
  2. When the user picks a pair and amount, fetch that pair again for a fresh rate_id. Passing deposit_amount does not change a DeFi rate.
  3. POST /orders/ with the same deposit amount and that rate_id.

At order creation the venue is quoted for the exact deposit. The order promises deposit / rate (minus the network fee on Ethereum-source orders, shown as withdrawal_fee), and that promise is what gets delivered. If the venue cannot reach it, creation either proceeds with a slightly lower amount (within a small tolerance) or fails with The market has moved beyond this rate's tolerance. Please request a new rate. In both cases, show the user the amount from the order creation response, not a client-side estimate.

curl -X GET "https://api.n.exchange/en/api/v2/rate/?pairs=USDCARBETHARB&is_defi=true&is_fixed_rate=true" \
-H "Authorization: ApiKey YOUR_API_KEY" \
-H "x-referral-token: YOUR_REFERRAL_TOKEN"
actionable_quote removed

The actionable_quote parameter no longer exists. It is ignored if sent, so existing integrations keep working and receive the same bindable row as everyone else.

Quote lifetime: rate_id and expiration_time_unix​

  • rate_id identifies a stored quote snapshot for that specific row (pair + mode). It is not transferable across pairs or modes.
  • expiration_time_unix is a string containing a unix timestamp in seconds. Parse it as an integer.
  • Quote TTLs are short — typically tens of seconds. Under fast-moving markets a returned quote may already be at or past its expiry when it reaches you. Always compare expiration_time_unix against your own clock at the moment of use, not receipt, and treat non-future expiries as "refresh before use".
  1. Display prices from a recent GET /rate/ read.
  2. On user submit, fetch a fresh quote for the exact pair and amount.
  3. Create the order with that row's rate_id.
  4. If creation fails with a rate-expired validation error, fetch one more fresh quote, compare the new withdraw_amount against what the user accepted, and either retry once with the new rate_id or surface a "price moved" confirmation to the user.

Passing rate_id binds order validation to the quote snapshot. Omitting it means the order is priced at creation time with no reference to what you displayed.

No absolute floor guarantee

Binding a rate_id validates the order against the quote snapshot at creation, but the finally confirmed withdraw_amount can still differ on volatile pairs (e.g. DeFi execution slippage within allowed bounds). If your UX promises a minimum received amount, verify the created order's amounts in the response and reconcile on completion — or use a DeFi limit order, which enforces a minimum receive amount by construction.

Always filter your rate requests​

GET /rate/ without a pairs filter (or currency parameters) returns the entire catalogue — thousands of rows and a response measured in megabytes, with correspondingly slow generation. Production integrations should always request specific pairs:

# Good — bounded and fast
GET /rate/?pairs=BTCETH,ETHBTC

# Avoid in production — full catalogue scan
GET /rate/

If you need the full pair universe, fetch GET /pair/ (names only, cheap) and request rates for the pairs you actually display.

Pair availability is dynamic​

The set of quotable pairs changes with liquidity and can be direction-asymmetric — ETHARBUSDCARB may be quotable while USDCARBETHARB is not. A pair present in GET /pair/ may still have no rate row right now. Treat "no row returned for the requested pair" / 404 No trading pairs found as pair temporarily unavailable, not as an integration error, and re-check later rather than caching the absence.

ETA​

Pass include_eta=true to add eta_seconds to each row — the estimated swap completion time (median of recent orders on the same network pair for CeFi; a fixed estimate for DeFi). null means no estimate is available. Off by default.

Fees and partner markup​

  • withdrawal_fee — combined fixed fees in the receive currency (see formula above).
  • markup — dynamic partner fee in basis points (integer, 50 = 0.5%). Preferred spelling; requires the capability to be pre-enabled on your referral code.
  • referral_rate — deprecated decimal-fraction spelling of the same thing ("0.005"). Sending both markup and referral_rate returns 400.
  • applied_referral_rate — in rate rows and order responses: the fee actually applied after any partner cap (excess above the cap is split 50-50). Only present when a dynamic fee was requested.

Next steps​