Deriv API
K

Complete Workflows

End-to-end examples of common trading workflows using the Deriv API

Prerequisites

  1. Log in to developers.deriv.com: Create an account or log in with your credentials to access the dashboard.
  2. Register a new application: Navigate to the Dashboard and register a new application under your account. Choose the appropriate application type based on your use case:
    • PAT type: Choose this when browser redirects are not practical and manual token entry is acceptable. For example, desktop tools, CLI apps, or native clients. The user generates a Personal Access Token in Deriv and pastes it into your app.
    • OAuth type: Choose this when your product can handle browser redirects and you need a standard delegated flow with user authorisation. For example, web dashboards or browser apps. OAuth 2.0 issues short-lived tokens and minimises long-term credential sharing.

    This will generate a new App ID. Your legacy App IDs will not work with the new APIs.

  3. Generate an authorization token:
    • If using PAT type: In the Dashboard, go to the API tokens section. Create a new PAT (Personal Access Token) and select the appropriate scopes (e.g., trade, account management). Copy and securely store your token. It cannot be viewed again after creation.
    • If using OAuth type: You do not need to generate a token manually. Proceed to the OAuth 2.0 authentication flow described below. The flow will provide a short-lived access token after successful authentication. Ensure you have your client_id, client_secret, and an HTTPS redirect_uri registered.
  4. Configure your request headers: Every REST API request must include both required headers:
required-headers.js
javascript
1// Required headers for ALL REST API calls
2headers: {
3  'Authorization': 'Bearer YOUR_AUTHORIZATION_TOKEN',  // Authorization token (PAT or JWT)
4  'Deriv-App-ID': 'YOUR_APP_ID',                      // App ID from your registered application
5  'Content-Type': 'application/json'
6}

Options Trading Workflow (REST + WebSocket)

  1. REST: Get an authenticated WebSocket URL via the OTP endpoint (requires your authorization token)
  2. WebSocket: Connect using the authenticated URL from the OTP response
  3. WebSocket: Perform trading operations

Step 1: Get Authenticated WebSocket URL (REST)

get-otp.js
javascript
1// Get authenticated WebSocket URL via OTP endpoint
2// Note: This REST call requires your authorization token
3const otpResponse = await fetch(
4  `https://api.derivws.com/trading/v1/options/accounts/${accountId}/otp`,
5  {
6    method: 'POST',
7    headers: {
8      'Authorization': 'Bearer YOUR_AUTHORIZATION_TOKEN',  // PAT or JWT token
9      'Deriv-App-ID': 'YOUR_APP_ID'
10    }
11  }
12);
13
14const otpResult = await otpResponse.json();
15const wsUrl = otpResult.data.url;
16console.log('Authenticated WebSocket URL:', wsUrl);
17// Output: wss://api.derivws.com/trading/v1/options/ws/demo?otp=abc123xyz789

Step 2: Connect to WebSocket

connect-websocket.js
javascript
1// Connect to Options WebSocket using the authenticated URL from OTP response
2// The URL already contains the correct endpoint (demo/real) and authentication
3const ws = new WebSocket(wsUrl);
4
5ws.onopen = () => {
6  console.log('Connected to Options trading WebSocket');
7  // Connection is now authenticated and ready for trading
8};
9
10ws.onmessage = (msg) => {
11  const data = JSON.parse(msg.data);
12  console.log('Received:', data);
13};
14
15ws.onerror = (error) => {
16  console.error('WebSocket error:', error);
17};
18
19ws.onclose = () => {
20  console.log('WebSocket connection closed');
21};

Step 3: Start Trading Operations

trading-operations.js
javascript
1// Once connected, you can send trading commands through WebSocket
2// Example: Get account balance
3ws.send(JSON.stringify({
4  balance: 1,
5  subscribe: 1,
6  req_id: 1
7}));
8
9// Example: Subscribe to tick stream
10ws.send(JSON.stringify({
11  ticks: "1HZ100V",
12  subscribe: 1,
13  req_id: 2
14}));
15
16// Example: Get price proposal
17ws.send(JSON.stringify({
18  proposal: 1,
19  amount: 10,
20  basis: "stake",
21  contract_type: "MULTDOWN",
22  currency: "USD",
23  duration_unit: "s",
24  multiplier: 10,
25  underlying_symbol: "1HZ100V",
26  subscribe: 1,
27  req_id: 3
28}));
complete-workflow.js
javascript
1async function setupOptionsTrading() {
2  const AUTH_TOKEN = 'YOUR_AUTHORIZATION_TOKEN';  // PAT or JWT token
3  const APP_ID = 'YOUR_APP_ID';                   // App ID from registered application
4  const API_BASE = 'https://api.derivws.com';
5  const accountId = 'YOUR_ACCOUNT_ID';            // Your demo or real account ID
6
7  try {
8    // Step 1: Get authenticated WebSocket URL (REST, requires authorization token)
9    const otpResponse = await fetch(
10      `${API_BASE}/trading/v1/options/accounts/${accountId}/otp`,
11      {
12        method: 'POST',
13        headers: {
14          'Authorization': `Bearer ${AUTH_TOKEN}`,
15          'Deriv-App-ID': APP_ID
16        }
17      }
18    );
19
20    if (!otpResponse.ok) throw new Error(`HTTP error! status: ${otpResponse.status}`);
21    const otpData = await otpResponse.json();
22    const wsUrl = otpData.data.url;
23    console.log('✓ Authenticated WebSocket URL obtained');
24
25    // Step 2: Connect to WebSocket using the authenticated URL
26    const ws = new WebSocket(wsUrl);
27
28    ws.onopen = () => {
29      console.log('✓ WebSocket connected');
30
31      // Step 3: Start trading
32      // Subscribe to balance updates
33      ws.send(JSON.stringify({
34        balance: 1,
35        subscribe: 1,
36        req_id: 1
37      }));
38
39      // Subscribe to ticks
40      ws.send(JSON.stringify({
41        ticks: "1HZ100V",
42        subscribe: 1,
43        req_id: 2
44      }));
45    };
46
47    ws.onmessage = (msg) => {
48      const data = JSON.parse(msg.data);
49
50      if (data.msg_type === 'balance') {
51        console.log('Balance:', data.balance.balance, data.balance.currency);
52      }
53
54      if (data.msg_type === 'tick') {
55        console.log('Tick:', data.tick.quote);
56      }
57    };
58
59    return ws;
60
61  } catch (error) {
62    console.error('Setup failed:', error);
63    throw error;
64  }
65}
66
67// Run the setup
68setupOptionsTrading().then(ws => {
69  console.log('Trading setup complete. WebSocket ready for operations.');
70}).catch(err => {
71  console.error('Failed to setup trading:', err);
72});

Authentication Workflows

Workflow A: PAT-Based Authentication

With a PAT app, the user generates a Personal Access Token in Deriv and manually enters or pastes it into your application. The app securely stores the token and includes it in API requests as a bearer token. This is best suited for desktop tools, CLI apps, and native clients where browser redirects are not practical.

  1. Log in to developers.deriv.com with your credentials
  2. Register a new application with PAT type in the Dashboard
  3. Generate a PAT token with appropriate scopes
  4. Include Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN> and Deriv-App-ID in all REST request headers
  5. Make authenticated REST API calls
pat-authentication.js
javascript
1// PAT-Based Authentication: REST API
2const AUTH_TOKEN = 'YOUR_AUTHORIZATION_TOKEN';  // Your PAT token
3const APP_ID = 'YOUR_APP_ID';
4
5// All REST calls use the authorization token as a Bearer token
6const response = await fetch('https://api.derivws.com/trading/v1/options/accounts', {
7  method: 'POST',
8  headers: {
9    'Authorization': `Bearer ${AUTH_TOKEN}`,    // Authorization token (PAT)
10    'Deriv-App-ID': APP_ID,                      // App ID from registered application
11    'Content-Type': 'application/json'
12  },
13  body: JSON.stringify({
14    currency: 'USD',
15    group: 'row',
16    account_type: 'demo'
17  })
18});
19
20const result = await response.json();
21console.log('Authenticated REST call successful:', result);

Workflow B: OAuth 2.0 Authentication

OAuth 2.0 lets users grant your app access without sharing their password. Your app redirects the user to a Deriv sign-in and consent page. After the user logs in and approves permissions, Deriv returns an authorization code to your app. You exchange this code for an access token, which you then use to authenticate API requests. Recommended for web-based applications onboarding end users.

Before You Begin

  • Ensure your redirect URL is correctly registered in the dashboard
  • The redirect URL must use HTTPS
  • Your app must handle redirects, read the authorization code, and exchange it for tokens
  • You must have a registered OAuth 2.0 client with valid credentials: client_id, client_secret, and redirect_uri
  • All redirect URLs (including subdirectories) must be whitelisted. URLs must match exactly.

OAuth 2.0 Flow Steps

  1. Your app redirects the user to Deriv's OAuth 2.0 authorization page to sign in and review permissions
  2. The authorization server handles login and consent securely
  3. After login, Deriv redirects the user back to your app with an authorization code and state parameter
  4. Your app exchanges this code for tokens (with PKCE if used)
  5. The OAuth server returns the access token (and optional refresh token)
  6. Your app securely stores and uses the access token for WebSocket or REST API calls
oauth-authentication.js
javascript
1// OAuth 2.0 Authentication Flow (Authorization Code with PKCE)
2const CLIENT_ID = 'YOUR_CLIENT_ID';
3const REDIRECT_URI = 'https://your-app.com/callback';
4
5// --- PKCE Helper Functions ---
6function generateCodeVerifier() {
7  const array = new Uint8Array(32);
8  crypto.getRandomValues(array);
9  return btoa(String.fromCharCode(...array))
10    .replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
11}
12
13async function generateCodeChallenge(verifier) {
14  const encoder = new TextEncoder();
15  const data = encoder.encode(verifier);
16  const digest = await crypto.subtle.digest('SHA-256', data);
17  return btoa(String.fromCharCode(...new Uint8Array(digest)))
18    .replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
19}
20
21// Step 1: Generate PKCE values and redirect user to authorization endpoint
22const codeVerifier = generateCodeVerifier();
23const codeChallenge = await generateCodeChallenge(codeVerifier);
24const state = crypto.randomUUID();  // Generate a random state value
25
26// Store codeVerifier and state securely (e.g., sessionStorage or server-side)
27sessionStorage.setItem('code_verifier', codeVerifier);
28sessionStorage.setItem('oauth_state', state);
29
30// Redirect user to Deriv's authorization endpoint:
31const authUrl = new URL('https://auth.deriv.com/oauth2/auth');
32authUrl.searchParams.set('response_type', 'code');
33authUrl.searchParams.set('client_id', CLIENT_ID);
34authUrl.searchParams.set('redirect_uri', REDIRECT_URI);
35authUrl.searchParams.set('scope', 'openid');
36authUrl.searchParams.set('state', state);
37authUrl.searchParams.set('code_challenge', codeChallenge);
38authUrl.searchParams.set('code_challenge_method', 'S256');
39
40window.location.href = authUrl.toString();
41
42// Step 2: User signs in and approves permissions (handled by Deriv)
43
44// Step 3: Handle the callback. Extract the authorization code.
45// Deriv redirects to: https://your-app.com/callback?code=AUTH_CODE&state=ORIGINAL_STATE
46const urlParams = new URLSearchParams(window.location.search);
47const authorizationCode = urlParams.get('code');
48const returnedState = urlParams.get('state');
49
50// IMPORTANT: Verify the state matches the original value to prevent CSRF attacks
51const savedState = sessionStorage.getItem('oauth_state');
52if (returnedState !== savedState) {
53  throw new Error('State mismatch: possible CSRF attack');
54}
55
56// Note: The authorization code is short-lived and single-use
57
58// Step 4: Exchange the authorization code for tokens
59const savedVerifier = sessionStorage.getItem('code_verifier');
60const tokenResponse = await fetch('https://auth.deriv.com/oauth2/token', {
61  method: 'POST',
62  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
63  body: new URLSearchParams({
64    grant_type: 'authorization_code',
65    client_id: CLIENT_ID,
66    code: authorizationCode,
67    redirect_uri: REDIRECT_URI,
68    code_verifier: savedVerifier
69  })
70});
71
72// Step 5: Token response
73const tokenData = await tokenResponse.json();
74// Response: { "access_token": "...", "expires_in": 3600, "token_type": "bearer" }
75const accessToken = tokenData.access_token;
76
77// IMPORTANT: Store tokens securely on the server. Never expose them in frontend code.
78console.log('Access token obtained, expires in', tokenData.expires_in, 'seconds');
79
80// Step 6: Use the access token for authenticated API calls
81const response = await fetch('https://api.derivws.com/trading/v1/options/accounts', {
82  method: 'GET',
83  headers: {
84    'Authorization': `Bearer ${accessToken}`,  // Access token (JWT)
85    'Deriv-App-ID': CLIENT_ID,
86    'Content-Type': 'application/json'
87  }
88});
89
90const result = await response.json();
91console.log('Authenticated API call successful:', result);

WebSocket Authentication (OTP)

To connect to an authenticated WebSocket endpoint, you need to call the OTP REST endpoint using your authorization token (PAT or JWT). The response contains an authenticated WebSocket URL that you can connect to directly. The URL handles the authentication for you.

WebSocket Endpoints:

Public
No authentication required

Use for market data and public information. No login needed.

Demo
Authenticated

Use for demo/virtual account trading operations. The OTP response URL will point to this endpoint for demo accounts.

Real
Authenticated

Use for live/real account trading. The OTP response URL will point to this endpoint for real accounts.

websocket-otp-auth.js
javascript
1// Step 1: Get authenticated WebSocket URL via REST (requires authorization token)
2const otpResponse = await fetch(
3  `https://api.derivws.com/trading/v1/options/accounts/${accountId}/otp`,
4  {
5    method: 'POST',
6    headers: {
7      'Authorization': 'Bearer YOUR_AUTHORIZATION_TOKEN',  // PAT or JWT token
8      'Deriv-App-ID': 'YOUR_APP_ID'
9    }
10  }
11);
12
13const otpResult = await otpResponse.json();
14const wsUrl = otpResult.data.url;
15// The URL already includes the correct endpoint and authentication
16// Output: wss://api.derivws.com/trading/v1/options/ws/demo?otp=abc123xyz789
17
18// Step 2: Connect to WebSocket using the authenticated URL
19const ws = new WebSocket(wsUrl);
20
21ws.onopen = () => {
22  console.log('WebSocket authenticated and connected');
23  // Ready for trading operations
24};

Complete Trading Workflow

  1. Establish connection and authenticate
  2. Get active symbols using active_symbols
  3. Subscribe to tick stream for chosen symbol using ticks
  4. Get contract proposal using proposal (with subscribe)
  5. Monitor real-time price updates
  6. When ready, buy contract using buy
  7. Subscribe to contract updates using proposal_open_contract
  8. Monitor contract status in real-time
  9. Optionally sell early using sell
  10. Check portfolio using portfolio
trading-workflow.js
javascript
1// After authentication...
2
3// 1. Get active symbols
4ws.send(JSON.stringify({
5  active_symbols: "brief",
6  req_id: 3
7}));
8
9// 2. Subscribe to ticks
10ws.send(JSON.stringify({
11  ticks: "1HZ100V",
12  subscribe: 1,
13  req_id: 4
14}));
15
16// 3. Get price proposal
17ws.send(JSON.stringify({
18  proposal: 1,
19  amount: 10,
20  basis: "stake",
21  contract_type: "MULTDOWN",
22  currency: "USD",
23  duration_unit: "s",
24  multiplier: 10,
25  underlying_symbol: "1HZ100V",
26  subscribe: 1,
27  req_id: 5
28}));
29
30// 4. Buy the contract (when ready)
31// Use proposal ID from previous response
32ws.send(JSON.stringify({
33  buy: "PROPOSAL_ID_HERE",
34  price: 100,
35  req_id: 6
36}));
37
38// 5. Monitor contract status
39ws.send(JSON.stringify({
40  proposal_open_contract: 1,
41  contract_id: CONTRACT_ID,
42  subscribe: 1,
43  req_id: 7
44}));

Market Data Workflow

  1. Connect to the public WebSocket endpoint (no auth needed)
  2. Request active_symbols to see available markets
  3. Subscribe to ticks for real-time price updates
  4. Optionally get ticks_history for historical data
  5. Use contracts_for to see available contract types
  6. Stream continues until forget or disconnect
market-data.js
javascript
1// Connect to the public WebSocket endpoint (no authentication required)
2const ws = new WebSocket('wss://ws.binaryws.com/websockets/v3');
3
4ws.onopen = () => {
5  // Get available symbols
6  ws.send(JSON.stringify({
7    active_symbols: "brief",
8    product_type: "basic",
9    req_id: 1
10  }));
11
12  // Subscribe to tick stream
13  ws.send(JSON.stringify({
14    ticks: "1HZ100V",
15    subscribe: 1,
16    req_id: 2
17  }));
18
19  // Get historical data
20  ws.send(JSON.stringify({
21    ticks_history: "1HZ100V",
22    count: 100,
23    end: "latest",
24    style: "ticks",
25    req_id: 3
26  }));
27};
28
29ws.onmessage = (msg) => {
30  const data = JSON.parse(msg.data);
31
32  if (data.msg_type === 'active_symbols') {
33    console.log('Available symbols:', data.active_symbols);
34  }
35
36  if (data.msg_type === 'tick') {
37    console.log('Current price:', data.tick.quote);
38    // Update your chart here
39  }
40
41  if (data.msg_type === 'history') {
42    console.log('Historical data:', data.history);
43    // Initialize your chart here
44  }
45};

Troubleshooting

401 Unauthorized
"You are not authorised to access this resource"

Common causes:

  • Missing Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN> header in REST requests
  • Using an expired or invalid authorization token
  • Using a legacy App ID instead of a new App ID registered on developers.deriv.com
  • Mismatched application type, such as using a PAT token with an OAuth-type application, or vice versa

Solution: Ensure your REST requests include Authorization: Bearer YOUR_AUTHORIZATION_TOKEN and use a new App ID registered on developers.deriv.com. Make sure your token type matches your application type.

403 Forbidden
Insufficient permissions

Common causes:

  • Authorization token does not have the required scopes for the endpoint
  • You created the token without trade or account management scope

Solution: Regenerate your token with the correct scopes selected. At least one scope must be defined when creating a token.

Invalid App ID
App ID not recognised

Common causes:

  • Using a legacy App ID with the new API
  • Using an App ID not registered on developers.deriv.com
  • Using an App ID with the wrong type. For example, a PAT-type App ID for an OAuth flow, or an OAuth-type App ID with a PAT token

Solution: Log in to developers.deriv.com and register a new application with the correct type (PAT or OAuth) to get a new App ID.

Expired Access Token
Token no longer valid

Common causes:

  • OAuth 2.0 access tokens are short-lived (typically 3600 seconds / 1 hour) and expire automatically
  • PAT tokens can be revoked manually from the dashboard

Solution: For OAuth apps, implement token refresh logic using the refresh token. For PAT apps, generate a new token from the dashboard. Never store tokens in frontend code or expose them in URLs.

OAuth Redirect Failure
Consent flow fails or the app does not redirect the user

Common causes:

  • Redirect URL is not whitelisted in the application dashboard
  • Redirect URL includes subdirectories that were not registered
  • Mismatch between the URL used in the OAuth request and the URLs registered in the dashboard

Solution: Ensure all redirect URLs (including subdirectories) are registered in your application settings on developers.deriv.com. The URLs must match exactly.

Common Patterns

Subscription Management
How to manage WebSocket subscriptions effectively
  • Always store subscription IDs
  • Use forget to unsubscribe
  • Use forget_all to clear all
  • Clean up subscriptions before disconnect
Error Handling
Best practices for handling API errors
  • Always check for error field
  • Implement exponential backoff for retries
  • Log errors with context
  • Handle network disconnections gracefully
Request IDs
How to track requests and responses
  • Use unique req_id for each request
  • Match responses using req_id
  • Helps with concurrent requests
  • Essential for debugging
Connection Lifecycle
Managing WebSocket connection state
  • Handle onopen, onclose, onerror
  • Implement auto-reconnect logic
  • Re-authenticate after reconnect
  • Restore subscriptions on reconnect
Click to open live chat support. Get instant help from our support team.