Best Practices for Trading
Build safe, scalable conversion experiences across USDT, USDC, Bitcoin, and NGN.
Trading involves real-time pricing, user input, and asset movement. To ensure reliability and protect both your users and your internal systems, follow these best practices.
Use Quote Locking for Predictable User Experience
Whenever you present pricing to an end user:
Use POST /api/trading/quotes to fetch a quote — see Quotes
Store the quote's id and expires_at on your backend
Send that id as quote_id in your POST /api/trading/orders call — see Create Order — before expires_at (about 10 seconds in production; in sandbox, 30 seconds for NGN pairs and about 5 minutes for crypto pairs)
Why: This guarantees your user receives exactly the rate they saw — no surprises due to market volatility.
If you skip the quote, the trade executes at the best available live rate.
For NGN pairs, quote locking is mandatory — naira orders must include a quote_id, so they cannot execute at a live market rate.
Always Use a Unique reference per Trade
Every trade should include a globally unique reference ID:
Use a UUID or transaction-specific string (e.g. user123_swap_001)
This is your idempotency key. On NGN pairs a replay returns the original order instead of executing twice; crypto pairs do not enforce it, so check before you retry
Prevents duplicate execution of trades due to network timeouts or retries
React to Webhooks, Not Just Synchronous Responses
The /api/trading/orders response returns immediately, but for production environments:
Wait for trade.completed webhook to update balances or mark the trade as “done”
Log trade.failed events to capture failure reasons and retry or notify the user
This allows you to build event-driven trade state management, which is more reliable at scale.
Track and Reconcile All Trade Activity
Reconcile against Get Orders, and resolve individual trades with Get Order By ID. Log the following in your internal ledger per trade:
reference, tradeId
from, to, rate, inputAmount, outputAmount
status (completed or failed)
createdAt timestamp
Run daily or hourly reconciliation:
Match your ledger entries with /api/trading/orders history
Match trade amounts with corresponding wallet debits/credits
Investigate any trades that were initiated but lack final webhook confirmation
Handle Quote Expiry Gracefully
If a quote expires before trade execution — you can re-read its state with Get Quote by ID:
You will receive a 422 response with detail "Quote <id> has expired"
Return a clear message to the user:
“The rate has expired. Please refresh the quote and try again.” Always validate the quote's expires_at timestamp server-side before use.
Validate Trade Amounts and Units
Trading amounts go in quantity, as a string in major units of the base currency — not cents, satoshis or kobo
When the base currency is USDT or USDC, "100" = 100 USDT
When the base currency is BTC, "0.001" = 0.001 BTC
When the base currency is NGN, "5000" = ₦5,000
Balances, transactions and withdrawals use base units instead (6-decimal units for USDT/USDC, satoshis for BTC, kobo for NGN), so convert when you reconcile a trade against the ledger
Normalize user input in your frontend or backend, not at the API boundary.
Secure Your Webhooks
Use Bitnob’s signed webhook system:
Validate X-Bitnob-Signature using HMAC SHA-512 with your webhook secret
Store all received events with delivery timestamp and status
Make your webhook processor idempotent and retry-safe
Protect Against Frontend Abuses
If you're exposing trading to users:
Set min/max limits in your frontend. Bitnob enforces ₦500–₦10,000,000 per trade on NGN pairs and a maximum size per quote on crypto pairs, and has no daily or monthly caps
Throttle or queue trade attempts to prevent race conditions
Log all trade attempts for abuse or velocity monitoring
Be Transparent in Your UX
Show users:
The amount they’re converting
The output they’ll receive
The chain/asset used
Any associated fees (if applicable)
A visible "Rate locked for X seconds" timer when using quotes
This builds trust and reduces confusion during high-volatility moments.
Handle Failures Intelligently
Handle these known failure states in your code:
scenario | response code | how to respond |
|---|---|---|
Quote expired | 422 | Fetch a new quote and ask the user to confirm the new rate |
Insufficient balance | 422 | Prevent trade initiation, notify user, retry later |
Internal execution error | 500 | Retry once with exponential backoff or log for review |
With these patterns in place, your trading workflows will be more robust, easier to debug, and ready to scale with zero slippage and zero surprises.