Deriv API
Documentation
Advanced

Best practices

Habits that keep your Deriv API integration robust, efficient, and resilient. Apply these alongside the per-endpoint reference.

Connection management

Open a single WebSocket connection and reuse it for every request and subscription, instead of opening one connection per call. Multiplex requests over the shared socket and match each response to its request using req_id.

When the connection drops, reconnect with exponential backoff (for example 1s, 2s, 4s, 8s, capped) plus a little jitter, rather than retrying in a tight loop. Send a periodic ping to keep the connection alive and to detect silent disconnects early.

// keepalive: send a ping on an interval over the shared socket
setInterval(() => socket.send(JSON.stringify({ ping: 1 })), 30000);

Rate limiting and request throttling

Throttle outgoing requests to stay within the API's rate limits. Queue and pace calls instead of sending large bursts, and debounce user-driven actions so a single interaction doesn't fan out into many requests.

If you're rate limited, back off exponentially before retrying, increasing the delay after each rejection. Prefer subscriptions over polling for data that updates continuously, so the server pushes changes instead of you re-requesting them.

When you retry a non-idempotent operation such as a purchase or withdrawal, supply a request id so you can poll for its final status and reconcile the result, rather than resubmitting blindly and risking a duplicate.

For the figures themselves — the per-IP and per-account request limits, and the shared budgets that groups of WebSocket calls draw on — see Limits.

Authentication and token hygiene

Authenticate a connection once by sending authorize with a valid token before making account-scoped requests. Use tokens with the least privilege the task requires by requesting only the scopes your endpoints need, such as trade or account_manage; a token that lacks a required scope is rejected with a 403 error.

For user-facing applications, use the OAuth 2.0 Authorization Code flow with PKCE rather than embedding tokens. Always perform the token exchange on your backend, not in the browser, and validate the state parameter on the callback before using the authorization code. Authorization codes are single-use and short-lived, so exchange them immediately and never store or log them. Generate a fresh code_verifier and state for every request, and clear them once the exchange succeeds. Access tokens are short-lived, typically one hour, so handle expiry by refreshing the token or re-running the authorization flow instead of assuming it stays valid. See the OAuth 2.0 guide for the full implementation.

Never embed long-lived API tokens in client-side code, public repositories, or build artifacts. Store secrets server-side, rotate them regularly, and revoke any token that may have been exposed.

// authenticate the connection before account-scoped calls
socket.send(JSON.stringify({ authorize: "<your-token>" }));

Error handling and subscription cleanup

Always inspect the error field on every response before using its data, and surface the error code and message to your own logging. Match responses to requests using req_id so a failure is attributed to the correct call.

Release subscriptions you no longer need: use forget with a subscription id to stop a single stream, and forget_all by stream type to stop every stream of that kind. Cleaning up prevents leaked subscriptions and unnecessary traffic.

// stop a single stream by its subscription id
socket.send(JSON.stringify({ forget: subscriptionId }));
// stop every stream of a given type
socket.send(JSON.stringify({ forget_all: "ticks" }));

Contract lifecycle

A trade moves through a short lifecycle: request a price with proposal, execute it with buy, follow the open position with proposal_open_contract, and close it with sell or let it settle at expiry. Buy against a fresh proposal rather than a stale quote — prices are short-lived. Pass a maximum price in the buy call so moves between quote and execution don't leave you with a worse fill.

Track each open contract by subscribing to proposal_open_contract instead of polling portfolio. The subscription pushes spot and profit updates and flips the is_sold and is_expired flags the moment the contract settles.

When is_sold becomes true, call forget on that subscription so you release the stream and don't leak subscriptions over time. If a buy response is lost or times out, reconcile by looking up the position in portfolio or proposal_open_contract before retrying, so you don't buy twice.

Before closing early, check the contract's own flags — use sell only when is_valid_to_sell is true, and use cancel only within the deal cancellation window for supported trades. For supported contract types, set stop loss and take profit with contract_update so exits happen server-side rather than watching the stream and selling manually.

// track an open contract, then release the stream once it settles
socket.send(JSON.stringify({
  proposal_open_contract: 1,
  contract_id: contractId,
  subscribe: 1,
}));

// on each update, when data.proposal_open_contract.is_sold is true:
//   socket.send(JSON.stringify({ forget: subscriptionId }));

Pagination

For endpoints that return lists, request data in pages rather than fetching everything at once. Use the limit and offset parameters to walk through results, and keep page sizes modest to reduce latency and memory pressure.

Continue requesting subsequent pages until a page returns fewer rows than the limit you asked for, which signals that you've reached the end of the data set.

Any other questions? Get in touch

Click to open live chat support. Get instant help from our support team.