Python port of unofficial XTB xStation5 API client
Project description
xtb-api-python
Unofficial — Reverse-engineered from xStation5. Not affiliated with XTB. Use at your own risk.
Python client for the XTB xStation5 trading platform. Dead-simple API that handles all authentication, token refresh, and transport selection transparently.
Features
- Single Client — One
XTBClienthandles everything, no mode selection needed - Auto Auth — Full CAS login flow with automatic TGT/JWT refresh
- 2FA Support — Automatic TOTP handling when
totp_secretis provided - Real-time Data — Live quotes, positions, balance via WebSocket push events
- Trading — Buy/sell market orders with SL/TP via gRPC-web
- Volume-Validated Orders —
buy/sellrejectvolume < 1before touching the wire - Persistent Instrument Cache —
InstrumentRegistrycaches symbol → instrument-ID lookups to disk - Full Symbol Search — Search and resolve all listed instruments with caching
- Modern Python — async/await, Pydantic models, strict typing, Python 3.12+
Requirements
- Python 3.12 or 3.13
- Chromium browser (installed via playwright — see post-install step below)
- An XTB trading account
Install
pip install xtb-api-python
# With automatic 2FA handling:
pip install "xtb-api-python[totp]"
Post-install setup (REQUIRED)
This library uses Playwright to authenticate with
XTB's servers (the REST login path is blocked by a WAF). After pip install,
you must download the Chromium binary:
playwright install chromium
Without this step, the first call to client.connect() will fail with a
CASError("BROWSER_CHROMIUM_MISSING", ...) and a pointer back here.
To verify your install is complete, run:
python -m xtb_api doctor
Development install
pip install -e ".[dev,totp]"
playwright install chromium
pre-commit install
Quick Start
import asyncio
from xtb_api import XTBClient
async def main():
client = XTBClient(
email="your@email.com",
password="your-password",
account_number=12345678,
totp_secret="BASE32SECRET", # optional, auto-handles 2FA
session_file="~/.xtb_session", # optional, persists auth across restarts
)
await client.connect()
# Account data
balance = await client.get_balance()
print(f"Balance: {balance.balance} {balance.currency}")
positions = await client.get_positions()
orders = await client.get_orders()
# Live quote
quote = await client.get_quote("EURUSD")
if quote:
print(f"Bid: {quote.bid}, Ask: {quote.ask}")
# Search instruments
results = await client.search_instrument("Apple")
# Persistent instrument cache (avoids re-fetching the full symbol list)
from xtb_api import InstrumentRegistry
registry = InstrumentRegistry("~/.xtb_instruments.json")
matched = await registry.populate(client, ["AAPL.US", "EURUSD"])
instrument_id = registry.get("AAPL.US") # int | None
# Trading (USE WITH CAUTION!)
result = await client.buy("AAPL.US", volume=1, stop_loss=150.0, take_profit=200.0)
print(f"Order: {result.order_id}")
await client.disconnect()
asyncio.run(main())
Real-time Events
client.on("tick", lambda tick: print(f"{tick['symbol']}: {tick['bid']}/{tick['ask']}"))
client.on("position", lambda pos: print(f"Position update: {pos['symbol']}"))
await client.subscribe_ticks("EURUSD")
Advanced Trade Options
from xtb_api import TradeOptions
# Simple: flat kwargs
await client.buy("EURUSD", volume=1, stop_loss=1.0850, take_profit=1.0950)
# Advanced: TradeOptions object
await client.sell("CIG.PL", volume=100, options=TradeOptions(
trailing_stop=50,
amount=1000.0, # amount-based sizing
))
TradeResult.priceis populated by polling open positions immediately after fill. If the position cannot be located within the poll window,priceremainsNone.
API Reference
XTBClient
| Method | Returns | Description |
|---|---|---|
connect() |
None |
Connect and authenticate |
disconnect() |
None |
Disconnect and clean up |
get_balance() |
AccountBalance |
Account balance, equity, free margin |
get_positions() |
list[Position] |
Open trading positions |
get_orders() |
list[PendingOrder] |
Pending limit/stop orders |
get_quote(symbol) |
Quote | None |
Current bid/ask for a symbol |
search_instrument(query) |
list[InstrumentSearchResult] |
Search instruments |
buy(symbol, volume, ...) |
TradeResult |
Execute BUY order |
sell(symbol, volume, ...) |
TradeResult |
Execute SELL order |
on(event, callback) |
None |
Register event handler |
subscribe_ticks(symbol) |
None |
Subscribe to real-time ticks |
Constructor Parameters
| Parameter | Required | Default | Description |
|---|---|---|---|
email |
Yes | — | XTB account email |
password |
Yes | — | XTB account password |
account_number |
Yes | — | XTB account number |
totp_secret |
No | "" |
Base32 TOTP secret for auto 2FA |
session_file |
No | None |
Path to persist auth session |
ws_url |
No | Real server | WebSocket endpoint URL |
endpoint |
No | "meta1" |
Server endpoint name |
account_server |
No | "XS-real1" |
gRPC account server |
auto_reconnect |
No | True |
Auto-reconnect on disconnect |
InstrumentRegistry
Persistent symbol → instrument-ID cache, stored as JSON.
| Method | Returns | Description |
|---|---|---|
InstrumentRegistry(path) |
— | Load (or create) the JSON cache at path |
get(symbol) |
int | None |
Cached instrument ID for symbol, or None |
set(symbol, id) |
None |
Cache one mapping and persist immediately |
populate(client, symbols) |
dict[str, int] |
Download the full symbol list via client, match requested symbols (case-insensitive, dot-less fallback), persist, return new matches |
ids |
dict[str, int] |
Read-only copy of the full cache |
WebSocket URLs
| Environment | URL |
|---|---|
| Real | wss://api5reala.x-station.eu/v1/xstation (default) |
| Demo | wss://api5demoa.x-station.eu/v1/xstation |
Advanced: Direct Access
For advanced use cases, access the underlying clients:
# WebSocket client (always available)
ws = client.ws
# gRPC client (available after first trade)
grpc = client.grpc_client
# Auth manager (accessor, or import the public alias)
auth = client.auth
from xtb_api import XTBAuth # public alias for the AuthManager class
tgt = await auth.get_tgt()
Architecture
XTBClient (public facade)
|
+-- AuthManager (shared auth session)
| +-- CASClient (REST + Playwright browser auth)
| +-- TGT cache (memory + disk)
|
+-- XTBWebSocketClient (quotes, positions, balance)
| +-- Auto-reconnect with fresh service tickets
|
+-- GrpcClient (trading, lazy-initialized)
+-- JWT auto-refresh from shared TGT
How Authentication Works
- Login — REST CAS attempt, falls back to Playwright browser if WAF blocks
- TGT — 8-hour token, cached in memory + optional session file
- Service Ticket — Derived from TGT, used for WebSocket login
- JWT — 5-minute token for gRPC trading, auto-refreshed from TGT
- 2FA — Automatic TOTP if
totp_secretprovided
All token refresh is transparent. If a TGT expires mid-session, the full auth chain re-runs automatically.
Disclaimer
This is an unofficial, community-driven project. NOT affiliated with, endorsed by, or connected to XTB S.A.
- Use at your own risk — trading involves financial risk
- No warranty — provided "as is"
- API stability — XTB may change their internal APIs at any time
- Always test on demo accounts first
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file xtb_api_python-0.7.1.tar.gz.
File metadata
- Download URL: xtb_api_python-0.7.1.tar.gz
- Upload date:
- Size: 96.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1e11c3302d9a08c4d66a227ab53d890131c45c65b75ce1a93300f8657eb2cd95
|
|
| MD5 |
3eed1ddee4295b31d3885a6826a2f04f
|
|
| BLAKE2b-256 |
c8826c9fc780067837b568e024a8e5459329d9d9e97a63da01e526d1b25e5298
|
Provenance
The following attestation bundles were made for xtb_api_python-0.7.1.tar.gz:
Publisher:
semantic-release.yml on liskeee/xtb-api-unofficial-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
xtb_api_python-0.7.1.tar.gz -
Subject digest:
1e11c3302d9a08c4d66a227ab53d890131c45c65b75ce1a93300f8657eb2cd95 - Sigstore transparency entry: 1343327580
- Sigstore integration time:
-
Permalink:
liskeee/xtb-api-unofficial-python@ee15864592ded9136da4c4d99d02109f5021a3df -
Branch / Tag:
refs/heads/master - Owner: https://github.com/liskeee
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
semantic-release.yml@ee15864592ded9136da4c4d99d02109f5021a3df -
Trigger Event:
workflow_run
-
Statement type:
File details
Details for the file xtb_api_python-0.7.1-py3-none-any.whl.
File metadata
- Download URL: xtb_api_python-0.7.1-py3-none-any.whl
- Upload date:
- Size: 57.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
281ce0771883eb9967792ba9abfbc077fb7b60ff3726a01269ae621bb3d101d0
|
|
| MD5 |
bc79ad124b6a3591a69a060fabe97530
|
|
| BLAKE2b-256 |
9963bcfd854402fdb2b237546b52846389a283eda6f93188d730cf87d0554d4a
|
Provenance
The following attestation bundles were made for xtb_api_python-0.7.1-py3-none-any.whl:
Publisher:
semantic-release.yml on liskeee/xtb-api-unofficial-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
xtb_api_python-0.7.1-py3-none-any.whl -
Subject digest:
281ce0771883eb9967792ba9abfbc077fb7b60ff3726a01269ae621bb3d101d0 - Sigstore transparency entry: 1343327588
- Sigstore integration time:
-
Permalink:
liskeee/xtb-api-unofficial-python@ee15864592ded9136da4c4d99d02109f5021a3df -
Branch / Tag:
refs/heads/master - Owner: https://github.com/liskeee
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
semantic-release.yml@ee15864592ded9136da4c4d99d02109f5021a3df -
Trigger Event:
workflow_run
-
Statement type: