Create swap order
/swap/createCreate a new swap order. Returns a fresh deposit address where the user should send their funds. The swap is processed automatically once the deposit is confirmed: the deposit is credited, the payout is sent to toAddress, and (on Tron, Ethereum, Avalanche and Solana) the deposit is later moved to Bitania's hot wallet — none of that needs a call from you.
Payment window: 60 minutes for both rate types (Bitania can change this; always drive your countdown from data.time.left). A swap that receives no deposit in the window becomes EXPIRED.
Important: Save the returned id — it is needed to check the status and to resolve a rate change or a refund. The response also includes a token: a per-swap secret that authorizes status and emergency calls from another API key, and the url of the public status page you can hand to the user. See the Swap Aggregator Guide.
Authentication: HMAC-signed (v2) request — X-API-KEY, X-API-SIGN, X-API-TIMESTAMP, X-API-NONCE headers. See the Swap Aggregator Guide for the signing scheme.
Request
Request Body (required)
Content-Type: application/json
{
type: string, enum: float|fixed, default "float" — Rate type (fixed needs fixed_rate_enabled on your key)
from: string, required, e.g. "BTC" — Source currency code
to: string, required, e.g. "USDTTRC" — Destination currency code
direction: string, enum: from|to, default "from" — 'from' or 'to' (a 'to' order is always fixed-rate)
amount: number, required, e.g. 0.5 — Amount in the direction currency
toAddress: string, required — Destination address for the payout; validated for the destination chain
refundAddress: string — Address for refunds; strongly recommended. A deposit below the minimum or above our liquidity is refunded there automatically; without it the swap waits (REFUNDING, issue.needsRefundAddress) until you send an address via /swap/emergency.
dryrun: boolean, default false — Create a simulated swap for integration testing (DR-prefixed id, no chain interaction, advance it with POST /swap/advance). Requires dryrun_enabled on your API key.
}
Response 201
Swap created; status is NEW and data.from.address is the deposit address
{
code: integer, e.g. 0 — 0 = success, 1 = error
msg: string, e.g. "" — Error message (empty on success)
data: object — Swap order (returned by create, status, advance and emergency)
{
id: string, e.g. "k7m3px9qw2" — Swap id: 10 characters (dry-run swaps: "DR" + 8). Use it for status and emergency calls.
token: string, e.g. "uYx9j2k3Lm4nOp5qRs6tUw" — Per-swap access token (22 characters). Lets another API key, or a shared link, read the status. Treat it as a secret.
type: string, enum: float|fixed — Rate type
status: string, enum: NEW|PENDING|CONFIRMING|RATE_CHANGED|EXCHANGING|SENDING|REFUNDING|DONE|EXPIRED|REFUNDED|FAILED — Current status (see Get swap status)
time: object — UNIX timestamps in seconds
{
reg: integer — Swap creation time
start: integer|null — When the deposit was first detected
finish: integer|null — When the swap ended (DONE or REFUNDED)
update: integer — Last change
expiration: integer — End of the payment window
left: integer|null — Seconds left in the payment window; null once a deposit was seen or the swap ended
}
from: object — What the user sends
{
code: string, e.g. "TRX" — Currency code
coin: string, e.g. "TRX" — Coin ticker
network: string, e.g. "TRX" — Chain
name: string, e.g. "Tron" — Display name
amount: string, e.g. "1000" — Amount the user is expected to send (string, full precision)
amountDeposited: string, e.g. "0" — Sum of the confirmed deposits so far
address: string — Deposit address to show the user
reqConfirmations: integer, e.g. 1 — Confirmations we wait for before crediting a deposit
tx: object|null — The deposit transaction, once detected
{
id: string — Transaction hash
amount: string — Deposited amount
fee: string|null — Network fee (not known for deposits)
feeCurrency: string|null — Currency of the fee
timeReg: integer — When we first saw the transaction
timeBlock: integer|null — When it reached the required confirmations
confirmations: integer — Current confirmation count
}
}
to: object — What the user receives
{
code, coin, network, name — as in from
amount: string, e.g. "339.15" — Amount we pay out: the quote until the deposit is confirmed, the final amount afterwards
address: string — Destination address
tx: object|null — The payout transaction, once sent: id, amount, fee (the network fee we paid), feeCurrency, timeReg, confirmations (refreshed while you poll, until the coin's own threshold), txKey (Monero only: the transaction key that proves the payment to the recipient's address; null elsewhere)
}
refund: object
{
address: string|null — Refund address (from the create call, or from an emergency REFUND call)
tx: object|null — The refund transaction, once sent
}
issue: object — Why the swap is not simply executing, and what was decided
{
type: string|null, enum: LESS|MORE|BELOW_MINIMUM|NO_LIQUIDITY — LESS / MORE: a fixed-rate deposit under / over the ordered amount (RATE_CHANGED). BELOW_MINIMUM: the deposit is under the pair minimum. NO_LIQUIDITY: the payout is more than we hold. Both of the latter are refunded automatically.
reason: string|null — Human-readable explanation while the swap is RATE_CHANGED, REFUNDING or FAILED
needsRefundAddress: boolean — true when we want to refund but no refund address is known: send one with POST /swap/emergency (choice REFUND)
choice: string, enum: NONE|EXCHANGE|REFUND — What was decided: EXCHANGE (executed at the new rate), REFUND (being / been refunded)
}
rate: string, e.g. "0.34" — Rate at creation: to-coin per 1 from-coin
rateActual: string|null — Rate actually applied; set when the swap is DONE
fee: object — Our service fee: { percent: number, e.g. 0.25 — amount: string, in the to-coin }
slippage: number|null — Applied rate vs quoted rate, in percent; set when the deposit is credited (0 for a fixed-rate swap honoured as quoted)
proposed: object|null — While RATE_CHANGED: the re-quote the user accepts with EXCHANGE: { rate, amount }
dryrun: boolean — true for simulated swaps
url: string|null — Public status page of the swap on bitania.com (null for dry-run swaps)
}
}
Response 400
Validation or business error, for example: That is not a valid USDT address, The refund address is not a valid BTC address, Minimum is 0.001 BTC, Dry-run mode is not enabled for your API key, BTC deposits are temporarily unavailable, please try again later
{
code: integer, e.g. 1 — Error code (1)
msg: string, e.g. "Missing required fields: from, to, amount" — Error message
data: null
}
Response 401
Authentication failed: unknown or disabled key, IP not whitelisted (Invalid credentials), or a bad signature, stale timestamp or reused nonce (Invalid signature)
{
code: integer, e.g. 1
msg: string, e.g. "Invalid credentials"
data: null
}
Response 429
Rate limit of this endpoint exhausted; wait retry_after seconds (also sent as the Retry-After header)
{
code: integer, e.g. 1
msg: string, e.g. "Rate limit exceeded. Please retry after 12 seconds."
data: null
retry_after: integer, e.g. 12
}