xapi-python
xapi-python copied to clipboard
API is closing
Hi, I did received an email: "Dear Customers, it will happen that as of 14.03.2025, there will be an application to the xtb API services via host: xapi.xtb.com , ws.xtb.com
for use from our web platform available at
https://xstation5.xtb.com/ or mobile application."
Do we have any plan what to do? Will it change to scalping-like lib?
At the moment I have no plan.
Regarding the xStation5 closed API. What I found:
xStation5 uses its own websocket endpoint: wss://api5reala.x-station.eu/v1/xstation
This is the only websocket used by the xStation web service. It is used to listen for price changes and also to modify trades. Messages sent through this closed API are human readable and cover most (if not all) of the open xAPI commands.
You can easily preview messages using the browser's DevTools.
If we could only figure out how to get an authentication code (called a serviceTicket), the rest would be easy.
Hi Guys,
does it means we have time until 14.03.2025?
Sorry for asking, but the xtb email lang is somehow difficult to understand.
Peace!
I called them and simple answer is: YES, it will stop working at 14.03.25.
This is weird situation, normally companies deprecate service and cancel if after year or half at least. We have less than month.
The way I see it, we have three options: 1. Accept that the xAPI will stop working and move on. 2. Reverse-engineer the xStation API to make it work. 3. Switch to another broker with a supported API (e.g., IBKR, BOSSA, Alpaca or others).
Honestly, based on what I’ve seen in your code, if anyone can make xStation API work, it’s you guys. But it won’t be easy—far from it.
Among alternative brokers, IBKR API seems to be the closest in terms of fees.
The way I see it, we have three options: 1. Accept that the xAPI will stop working and move on. 2. Reverse-engineer the xStation API to make it work. 3. Switch to another broker with a supported API (e.g., IBKR, BOSSA, Alpaca or others).
Honestly, based on what I’ve seen in your code, if anyone can make xStation API work, it’s you guys. But it won’t be easy—far from it.
Among alternative brokers, IBKR API seems to be the closest in terms of fees.
Problem with IBKR is their API is crazy, documentation have flaws and they require memory-heavy software to make it work.
Honestly I do not think any not official way of working with XTB would work. Even if we would find the way to do so, they might try to fight with us, also any change on their side can be immediate, so we will not have time to fix it on our side, which could cause days off, which we definitely do not want.
@pawelkn Thanks for your work, I truly appeciate that.
I've talked to support from XTB and they said that it may be possible to reintroduce xAPI early next year. From what they told me, it was deprecated because of some technical difficulties. I will regularly keep bugging (sic :D) them with questions whether they gonna reintroduce it.
@pawelkn Yep, thank you very much for your work. Highly appreciated :)
Well if they bring it back early next year, that is way too late for me. I will switch broker, they completly screwed up my workflow by just deprecating the API
@pawelkn: Many many thanks for all the work on xapi
Same for me. I invested really alot of effort in order to build strategy and the process around this API, and once I started the actual benchmarks they just shoot it down. I'm really mad. Now I'm wondering if it's worth to invest even more time either more reasonably is to switch into another, more predictible and responsible broker.
Regarding
You can easily preview messages using the browser's DevTools.
If we could only figure out how to get an authentication code (called a serviceTicket), the rest would be easy.
Maybe, instead of debugging traffic from XStation5 would be easier to decompile apk code and check it's Java implementation.
Adding to my last post, I've got different responses from Xtb support - over phone call they told me that it was shut down because of some technical difficulties and it may be reintroduced next year, but though mail their wrote me that there are no plans of turning in back on. So I guess we shouldn't count on them too mach and either go with this:
You can easily preview messages using the browser's DevTools.
If we could only figure out how to get an authentication code (called a serviceTicket), the rest would be easy.
or that:
Maybe, instead of debugging traffic from XStation5 would be easier to decompile apk code and check it's Java implementation.
Besides do you know any API from broker which has access GPW (Warsaw Stock Exchange: WSE) and has lowest fees (ideally it would be the same as in XTB - no fees whatsoever :))?
One other thing is that I guess it's worth that whoever wishes is to contact support, so they know someone was using (or was planning to use) their API. I've told support that I was actively using API and they told me that this feedback is taken into account and will be shared with higher management.
Bartosz, honestly I think we cannot rely of such company, nobody on earth is closing their services with only one month notice. Some people could have really huge money there and now what. Even if they will fix their API you do not know when they will close it again, or if they will have any more stupid ideas. How they could not plan fix of the API, how they do not know if they will open it back again, wtf is wrong with communication, thats is crazy, especially for financial company. Interactive brokers could not do that crap to people.
To get the ST (Service Ticket):
- Login through web.
- Look for the cookie 'CASTGC'. It should look like this:
TGT-xxxxx-xxx-xxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxxx-xstation.xtb.com curl -X 'POST' -H 'Host: xstation.xtb.com' -H 'Content-Type: application/x-www-form-urlencoded' --data 'service=xapi5' 'https://xstation.xtb.com/signon/v1/tickets/TGT-xxxxx-xxx-xxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxxx-xstation.xtb.com'- You get as response the ST:
ST-xxxxxx-xxx-xxxxxxxxxxxxxxxxxxxxxxx-xstation.xtb.com
below require local proxy.
import os
import base64
import httpx
from cryptography.fernet import Fernet
# Injected protobuf code
"""Generated protocol buffer code."""
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pool as _descriptor_pool
from google.protobuf import runtime_version as _runtime_version
from google.protobuf import symbol_database as _symbol_database
from google.protobuf.internal import builder as _builder
_runtime_version.ValidateProtobufRuntimeVersion(
_runtime_version.Domain.PUBLIC, 6, 30, 1, "", "auth.proto"
)
_sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(
b'\n\nauth.proto\x12\x1cpl.xtb.ipax.pub.grpc.auth.v1".\n\x0b\x43redentials\x12\r\n\x05login\x18\x01 \x01(\t\x12\x10\n\x08password\x18\x02 \x01(\t"\x9f\x01\n%CreateAccessTokenByCredentialsRequest\x12>\n\x0b\x63redentials\x18\x01 \x01(\x0b\x32).pl.xtb.ipax.pub.grpc.auth.v1.Credentials\x12\x36\n\x07\x61\x63\x63ount\x18\x02 \x01(\x0b\x32%.pl.xtb.ipax.pub.grpc.auth.v1.Account"_\n\x18\x43reateAccessTokenRequest\x12\x0b\n\x03tgt\x18\x01 \x01(\t\x12\x36\n\x07\x61\x63\x63ount\x18\x02 \x01(\x0b\x32%.pl.xtb.ipax.pub.grpc.auth.v1.Account"1\n\x19\x43reateAccessTokenResponse\x12\x14\n\x0c\x61\x63\x63\x65ss_token\x18\x01 \x01(\t")\n\x07\x41\x63\x63ount\x12\x0e\n\x06number\x18\x01 \x01(\x03\x12\x0e\n\x06server\x18\x02 \x01(\t2\xb5\x02\n\x0b\x41uthService\x12\x9e\x01\n\x1e\x43reateAccessTokenByCredentials\x12\x43.pl.xtb.ipax.pub.grpc.auth.v1.CreateAccessTokenByCredentialsRequest\x1a\x37.pl.xtb.ipax.pub.grpc.auth.v1.CreateAccessTokenResponse\x12\x84\x01\n\x11\x43reateAccessToken\x12\x36.pl.xtb.ipax.pub.grpc.auth.v1.CreateAccessTokenRequest\x1a\x37.pl.xtb.ipax.pub.grpc.auth.v1.CreateAccessTokenResponseBH\n\x1cpl.xtb.ipax.pub.grpc.auth.v1P\x01Z&.auth-service-proto;auth_service_protob\x06proto3'
)
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "auth_pb2", _globals)
if not _descriptor._USE_C_DESCRIPTORS:
_globals["DESCRIPTOR"]._loaded_options = None
_globals["DESCRIPTOR"]._serialized_options = (
b"\n\034pl.xtb.ipax.pub.grpc.auth.v1P\001Z&.auth-service-proto;auth_service_proto"
)
_globals["_CREDENTIALS"]._serialized_start = 44
_globals["_CREDENTIALS"]._serialized_end = 90
_globals["_CREATEACCESSTOKENBYCREDENTIALSREQUEST"]._serialized_start = 93
_globals["_CREATEACCESSTOKENBYCREDENTIALSREQUEST"]._serialized_end = 252
_globals["_CREATEACCESSTOKENRESPONSE"]._serialized_start = 351
_globals["_CREATEACCESSTOKENRESPONSE"]._serialized_end = 400
# ----------------------
# FUNCTION: Get login and password from XTB
# ----------------------
def get_login_credentials():
username = os.getenv("XTB_USER_ID")
cipher_suite = Fernet(os.getenv("ENCRYPTING_KEY"))
password = cipher_suite.decrypt(os.getenv("XTB_PASSWORD").encode()).decode("utf-8")
return username, password
# ----------------------
# FUNCTION: Send request to XTB API (ByCredentials)
# ----------------------
def get_access_token_by_credentials():
print("Sending request to XTB API (login + password)...")
username, password = get_login_credentials()
request = CreateAccessTokenByCredentialsRequest(
credentials=Credentials(login=username, password=password)
)
serialized = request.SerializeToString()
framed = b"\x00" + len(serialized).to_bytes(4, byteorder="big") + serialized
grpc_payload = base64.b64encode(framed).decode("utf-8")
headers = {
"Accept": "application/grpc-web-text",
"Content-Type": "application/grpc-web-text",
"Origin": "https://xstation5.xtb.com",
"Referer": "https://xstation5.xtb.com/",
"User-Agent": "Mozilla/5.0",
"X-Grpc-Web": "1",
"X-User-Agent": "grpc-web-javascript/0.1",
"TE": "trailers",
"Content-Length": str(len(grpc_payload)),
"grpc-timeout": "5S",
}
url = "http://localhost:8080/pl.xtb.ipax.pub.grpc.auth.v1.AuthService/CreateAccessTokenByCredentials"
try:
with httpx.Client(http2=True, timeout=10.0) as client:
resp = client.post(url, headers=headers, content=grpc_payload)
except httpx.RemoteProtocolError as e:
print("HTTP/2 protocol error:", str(e))
return None
except Exception as e:
print("Other communication error:", str(e))
return None
print("Status:", resp.status_code)
print("Headers:", resp.headers)
if resp.status_code == 200 and resp.text:
raw = base64.b64decode(resp.text)
if raw[0] != 0:
print("Unexpected gRPC frame flag:", raw[0])
length = int.from_bytes(raw[1:5], "big")
message_bytes = raw[5 : 5 + length]
response_obj = CreateAccessTokenResponse()
response_obj.ParseFromString(message_bytes)
return response_obj.access_token
else:
print("No token.")
print("grpc-status:", resp.headers.get("grpc-status"))
print("grpc-message:", resp.headers.get("grpc-message"))
print("Content:", resp.text)
return None
# ----------------------
# MAIN
# ----------------------
if __name__ == "__main__":
access_token = get_access_token_by_credentials()
if access_token:
print("Retrieved access Token:", access_token)
@huka81 , what is cipher_suite = Fernet(os.getenv("ENCRYPTING_KEY"))? Especially where did you get ENCRYPTING_KEY from?
CreateAccessTokenByCredentials is standard grpc-web operation, and request/response payload proto3 definitions could be easily extracted from xstation js files. huka81 solution is a bit weird, but I assume he tried to extract it from some 3rd party app.
However I'm not sure yet if/how login/password is encrypted there.
@peku33 it's your encryption key, in order to avoid storing password as a plain text. however if you are fine with plain text password, you can use something like:
def get_login_credentials():
return os.getenv("XTB_USER_ID"), os.getenv("XTB_PASSWORD")
@huka81 thanks, did you manage to login to xstation websocket api using access token? It seems like this requires this ST-XYZ "ticket", not the access token. Or maybe you have a way to exchange the access token into the ticket?
try to add it into a header
def get_header(access_token: str, request_id: str, xtb_user_id: int):
return {
"Accept": "application/grpc-web-text",
"Content-Type": "application/grpc-web-text",
"Origin": "https://xstation5.xtb.com",
"Referer": "https://xstation5.xtb.com/",
"User-Agent": "Mozilla/5.0",
"X-Grpc-Web": "1",
"X-User-Agent": "grpc-web-javascript/0.1",
"TE": "trailers",
"Authorization": f"Bearer {access_token}",
"grpc-timeout": "20S",
"x-client-request-id": f"{request_id}-{int(time.time())}",
"Connection": "keep-alive",
"acn-bin": base64.b64encode(xtb_user_id.to_bytes(8, "big")).decode("ascii"),
}
I recently learned that the old API has been quietly moved to the XTB's subsidiary: X Open Hub (https://xopenhub.pro/api/xapi-protocol-documentation/).
The only change required to run the xapi-python library is to replace host with ws.xapi.pro in the credentials file. All commands should work, as the API version is the same as the latest XTB version prior to closure.
I haven't tried it yet and I don't know if XTB accounts are accessible through this child company.
Oh, great news! I was playing around with X Open Hub API about 2 years ago, but completely forgot about them :D.
As far as I remember I had some issues, that for example polish stocks which were available in ordinary XTB (there were more than 500 polish stocks) some of them where missing in X Open Hub.
But will recheck it and come back to you with my findings :).
Hi, so coming back with results from my research I have following findings:
-
I was able to login on WEB (https://xs5.xopenhub.pro/xoh/) with DEMO account which I created on ordinary XTB site (https://xstation5.xtb.com/) - link to create demo account is located in footnote. After logging in on WEB I was able to place both buy and sell limit orders. Also as far as I checked, every instrument which is available on ordinary XTB (Stock, CFD, Crypto, Forex, etc.) is available also on X OPEN HUB - I checked it both on WEB and API (using
getAllSymbols()API method)Unfortunately on that account through API while trying to perform buy I wasn't able to do that. Although I received this response from server:
{'status': True, 'returnData': {'order': 786435381}}, WEB showed following errorXApi trading disabled: -
I was able to login on WEB (https://xs5.xopenhub.pro/xoh/) with DEMO account which I created on X OPEN HUB site (https://xopenhub.pro/try-demo/). After logging in on WEB I was able to place both buy and sell limit orders. Unfortunately NOT every instrument is available on X OPEN HUB, for example there are no Stocks available at all (only Stock CFDs), so it confirms what I wrote in my previous post - I checked it both on WEB and API (using
getAllSymbols()API method)I was able to successfully place buy and sell limit orders on that account through API :
-
When trying to login on WEB (https://xs5.xopenhub.pro/xoh/) using REAL account created on ordinary XTB site (https://xstation5.xtb.com/) I wasn't able to do that - it was complaining about incorrect credentials. The same with API - it failed to authenticate me with following response from server:
login_failed_error = LoginFailed({'status': False, 'errorCode': 'EX017', 'errorDescr': 'You are trying to login using the account from a different platform'})
I will write to X Open Hub asking whether there is possibility to login on Real account from XTB on their site. And if not, I will ask them if they allow creating real account for individual users and ask them if there is possibility to enable Stocks on such account.
hey Guys, do You have any updates on this one?
The only way to trade for now is to use the websocket based xstation5 api, same as web client uses. It's however hard to automatically obtain TGT, as password -> TGT is WAF bot protected. If you accept to copy TGT from your browser manually, then you can exchange it for service ticket with their api, and then use it for trading on xstation5 api. This is the flow you will see in the developer console of your browser.
Is there a way of using this TGT with xapi-python library by injecting it somewhere, or we have to use those XTB websockets "by hand"?
@peku33 Do you know the answer to above?
@peku33 Do you know the answer to above?
No, xapi is down. The easiest way to trade is to switch to websocket
I've contacted with X Open Hub, describing them my findings around XTB / X Open Hub and their APIs as written here: https://github.com/pawelkn/xapi-python/issues/12#issuecomment-2984795223
with my question to them:
Is there a way to buy and sell stocks (especially Polish stocks) on X Open Hub using API on REAL account? I accept that this account can be created either on XTB or through X Open Hub.
And that's the answer I've received from X Open Hub:
Dear Bartosz,
unfortunately, as they mentioned to you, XTB ended support for the API.
As X Open Hub, we provide services only to other brokerage houses which they use our platform.
As you already noticed, you can't login to the API with the institutional demo account as the way to login is different.
Login for XTB's accounts are e-mail and password, while on X Open Hub is account number and password, also they connect to a different server, therefore you can't use a XTB's account on the X Open Hub's API.
As institutional providers, we can't offer any retail accounts either, so, what you can test is only the Demo accounts created on our page.
Also, as institutional provider, we do not offer cash stocks like XTB does, but only CFDs and we have a very limited number of markets offered, and unfortunately the Polish Stock Market currently is not included.
I hope you find our answer exhaustive.
Kind regards
X Open Hub Support