⬡ TECHNICAL TUTORIAL · NO AFFILIATE LINKS

Deriv API in Python: WebSocket connection from scratch.

A technical programming tutorial. We are going to connect to the Deriv API, authenticate, pull candles in real time and structure the base of a bot. Educational content — this page does not promote trading on Deriv.

By the RoboTraderIA Team· 12 min read· intermediate level
Content typeTECHNICAL
Tutorialon programming
Learn the structure. Apply it where it makes sense.
Language Python 3.11+ Protocol WebSocket Libraries websockets, asyncio Account demo (free)
Skip to the code →
100% educational · no affiliate links

Why this tutorial exists (and what it is not): Deriv has a genuinely public API and a well-documented WebSocket, which is rare among platforms in this segment. It is excellent material for learning to program bots — authentication, data streaming, asynchronous processing. This page teaches the technology. It does not recommend trading Deriv's synthetic indices or fixed-odds contracts — those products are not regulated by Brazil's securities regulator (CVM) and carry the risks typical of the segment. Use the WebSocket knowledge you pick up here to connect to any platform with an API.

01Why study the Deriv API

Three reasons it makes a good technical case study: public, clear documentation, a free sandbox/demo for testing (with no deposit required), and the WebSocket protocol — which is exactly what you will need to understand in order to connect to any modern crypto exchange, to MT5 via a bridge, or to any platform with real-time data streaming. What you learn here transfers directly to Binance, Bybit and Deribit.

02Setting up the environment

You need Python 3.11+, a virtualenv and two libraries. In the terminal:

# Create the project and an isolated environment
mkdir bot-deriv && cd bot-deriv
python -m venv venv
source venv/bin/activate  # Linux/Mac
# venv\Scripts\activate     # Windows

# Install dependencies
pip install websockets asyncio python-dotenv

Next, create a demo account on Deriv (free, no deposit) just to get an API token with read-only scope. You do not need to deposit or trade.

  1. Go to app.deriv.com and create a demo account
  2. Go to Settings → API tokens
  3. Create a token with the Read (read-only) scope for this tutorial
  4. Copy the token and store it in a .env file in the project:
# .env
DERIV_TOKEN=seu_token_aqui
DERIV_APP_ID=1089  # public test app_id

03Hello World: connecting to the WebSocket

Deriv's server uses WebSocket at wss://ws.derivws.com/websockets/v3. All communication is asynchronous — you send a JSON message and the server replies in JSON too, possibly as a stream. First contact:

# conectar.py
import asyncio, json, os
import websockets
from dotenv import load_dotenv

load_dotenv()
APP_ID = os.getenv("DERIV_APP_ID")
URL    = f"wss://ws.derivws.com/websockets/v3?app_id={APP_ID}"

async def ping():
    async with websockets.connect(URL) as ws:
        await ws.send(json.dumps({"ping": 1}))
        resposta = await ws.recv()
        print("Servidor respondeu:", resposta)

asyncio.run(ping())

Run it with python conectar.py. You should see {"echo_req": {"ping": 1}, "msg_type": "pong", "ping": "pong"}. If you did, congratulations — you are connected. If not, check your firewall or proxy.

04Authentication

After the handshake, you authorize with the token:

# autenticar.py
async def autenticar():
    token = os.getenv("DERIV_TOKEN")
    async with websockets.connect(URL) as ws:
        await ws.send(json.dumps({"authorize": token}))
        resp = json.loads(await ws.recv())
        if "error" in resp:
            print("Falha:", resp["error"]["message"])
        else:
            print("Conta:", resp["authorize"]["loginid"])
            print("Saldo:", resp["authorize"]["balance"])

Security best practice: never commit the token to Git. Always use .env + .gitignore. For production, use secret vaults (AWS Secrets Manager, HashiCorp Vault).

05Streaming candles in real time

This is where WebSocket shines. Instead of polling a REST endpoint every second, you subscribe to a stream and the server pushes updates on its own:

# candles.py
async def stream_candles(symbol="frxEURUSD", granularity=60):
    async with websockets.connect(URL) as ws:
        await ws.send(json.dumps({
            "ticks_history": symbol,
            "adjust_start_time": 1,
            "count": 100,
            "end": "latest",
            "style": "candles",
            "granularity": granularity,
            "subscribe": 1
        }))

        while True:
            msg = json.loads(await ws.recv())
            if msg.get("msg_type") == "ohlc":
                c = msg["ohlc"]
                print(f"{c['epoch']} O:{c['open']} H:{c['high']} L:{c['low']} C:{c['close']}")

asyncio.run(stream_candles())

The subscribe: 1 parameter is the magic — it keeps the connection open and pushes every new candle. You do not have to ask again.

06Structuring it like a real bot

The example above is for teaching. A real bot has three separate layers: connection (automatic reconnection, heartbeat), strategy (pure, testable rules), and execution (sending orders, managing state). Recommended skeleton:

# structure/
# ├── client.py       # WebSocket + reconnect + heartbeat
# ├── strategy.py     # Pure functions: given a DataFrame, returns a signal
# ├── executor.py     # Sends orders, tracks the open position
# ├── risk.py         # Stop loss, take profit, position sizing
# └── main.py         # Glues it all together

# strategy.py - exemplo: cruzamento de médias
import pandas as pd

def calcular_sinal(df: pd.DataFrame) -> str:
    df["ma_curta"] = df["close"].rolling(9).mean()
    df["ma_longa"] = df["close"].rolling(21).mean()

    ultima = df.iloc[-1]
    anterior = df.iloc[-2]

    # Cross up
    if anterior["ma_curta"] < anterior["ma_longa"] and ultima["ma_curta"] > ultima["ma_longa"]:
        return "compra"
    # Cross down
    if anterior["ma_curta"] > anterior["ma_longa"] and ultima["ma_curta"] < ultima["ma_longa"]:
        return "venda"
    return "hold"

The separation matters: a strategy written as a pure function is testable with pytest in seconds, without connecting to anything. You run a backtest with 5 years of data, validate it, and only then plug it into the real executor.

07Error handling and reconnection

A WebSocket in production will drop. Servers restart, networks wobble, timeouts expire. A bot that does not reconnect is a useless bot. Recommended pattern:

async def conectar_com_retry():
    backoff = 1
    while True:
        try:
            async with websockets.connect(URL, ping_interval=20) as ws:
                print("Conectado")
                backoff = 1  # reset
                await rotina_principal(ws)
        except (websockets.ConnectionClosed, OSError) as e:
            print(f"Conexão caiu: {e}. Reconectando em {backoff}s")
            await asyncio.sleep(backoff)
            backoff = min(backoff * 2, 60)  # exponential, capped at 60s

08Where to apply this knowledge

The WebSocket + asyncio pattern you have learned works on almost every modern platform with streaming. Where it takes you:

Want the complete bot skeleton?

Download our sample project on GitHub — code commented line by line, with tests and a clean architecture.

Download the free bot →
MIT License · use it, adapt it, share it

Related reading

🎁 Get the free trading bot (with the Quotex, Deriv and IQ Option APIs)

Open source bot + ready-made API code. You edit it by chatting with ChatGPT/Claude — no programming. Free, no spam.

👉 Want the ready-made bot instead? Go to botbinaryoptions.com →