xcreener-sdk-python
Python client for the XCREENER XQL HTTP API. Strings in, typed objects out.
pip install xcreener # pip install 'xcreener[pandas]' for to_pandas()
Quickstart
from xcreener import Xcreener
xc = Xcreener() # reads XCREENER_API_KEY
query = """
market = "CRYPTO" # CRYPTO, FOREX, INDICES, COMMODITIES, METALS
timeframe = h1
let rsi14 = rsi(14)
columns = [rsi14, volume]
sort = volume desc
limit = 5
rsi14 < 30 and close > d::sma(200)
"""
for match in xc.run(query):
print(match.symbol, match["rsi14"], match["volume"])
Generate a key at xcreener.com/account/api-key. The raw key is shown once, and generating a new one invalidates the old one everywhere it is in use.
The API key
The client takes it from the constructor, or falls back to
$XCREENER_API_KEY.
Xcreener() # $XCREENER_API_KEY
Xcreener(api_key="...") # explicit, wins over the environment
The four calls
| Method | Endpoint | Metered | Returns |
|---|---|---|---|
xc.validate(q) |
POST /xql/validate |
no | ValidationResult |
xc.explain(q) |
POST /xql/explain |
no | Explanation |
xc.run(q) |
POST /xql/run |
yes | ResultSet |
xc.usage() |
GET /usage |
no | Quota |
Validation is a result, not an exception
Checking a query is the entire point of validate, so a failure comes back as a
falsy object rather than something you have to catch. run and explain raise
on the same 400, because there it really is a bug.
result = xc.validate('market = "CRYPTO"\ntimeframe = h1\nrsi(14)[1] < 30')
if not result:
print(result.message)
# Expected '-' (offset indexing uses the form [-N], e.g. [-1]) but found '1'
result.raise_for_error()
# xcreener.errors.XQLSyntaxError: line 3, column 9: Expected '-' (offset
# indexing uses the form [-N], e.g. [-1]) but found '1'
#
# rsi(14)[1] < 30
# ^
Errors
XcreenerError
├── AuthenticationError 401 key missing or unrecognized
├── QuotaExceeded 429 .reset_at
├── XQLError 400 no data was fetched
│ ├── XQLSyntaxError .line .column .offset, caret excerpt in str()
│ └── XQLPlanError .required_bars .max_bars .timeframe
├── UpstreamError 5xx retried before raising
└── TransportError the request never completed
Plan errors parse themselves so you do not have to match on message text:
try:
xc.run('market = "CRYPTO"\ntimeframe = h1\nclose > d::highest(high, 365)')
except XQLPlanError as exc:
if exc.is_lookback_ceiling:
print(exc.required_bars, exc.max_bars, exc.timeframe) # 366 300 d
Only 5xx and transport failures are retried. A 400 parses identically on a retry, and a 429 has not refilled.
Results
ResultSet is an ordered Sequence[Match]. The order is meaningful: it
reflects the query's own sort and limit. An empty result set means the query
is valid and nothing matches right now, which is not an error.
rs = xc.run(query)
rs.symbols # ['SOLUSDT', 'ADAUSDT']
rs.column("volume") # [512044.2, 118642.0]
rs.column_names # ['rsi14', 'volume']
rs[0]["rsi14"] # 24.8
rs.to_dicts()
rs.to_pandas() # indexed by symbol, needs the pandas extra
Columns are keyed by exactly what you wrote in the columns pragma: rsi14
for the let binding above, rsi(14) had you listed the expression itself. A
condition's own inputs are not surfaced automatically, so if match["rsi14"]
raises a KeyError, the fix is almost always adding it to columns. The error
message says so.
Quota
validate and explain are never metered. Only run counts against the daily
quota, so iterate for free and spend a run once the query does what you want.
xc.run(query, precheck=True) # free validate first; a bad query costs 0 quota
xc.rate_limit # from the last run's headers, no extra request
xc.usage() # free, and still answers at 429
Set Xcreener(precheck=True) to make that the default in development.
A query's syntax does not drift once it checks out, so there is nothing to
re-validate for a query that is fixed in your source — go straight to run.
It still raises XQLSyntaxError if the query is bad. All you trade away is the
guarantee that a malformed one costs zero quota, and you save a round trip.
Options
Xcreener(
api_key=None, # or $XCREENER_API_KEY
base_url="https://api.xcreener.com",
timeout=30.0, # seconds, per request
max_retries=2, # 5xx and transport only
precheck=False,
transport=None, # inject your own httpx.Client
)
The client is a context manager, and accepts anything with a to_xql() method
as well as a raw string, so a future expression or builder layer drops in
without changing the transport.
Contributing
Working on the SDK itself — running the test suite, or cutting a release? See CONTRIBUTING.md.
License
MIT
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 xcreener-0.1.0.tar.gz.
File metadata
- Download URL: xcreener-0.1.0.tar.gz
- Upload date:
- Size: 26.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
803ea50ba433d99aac677d5b2c1ef74b67f8e11fb3faa42475665450471926c4
|
|
| MD5 |
f270bc09c18afbe1c52a94315fd9273f
|
|
| BLAKE2b-256 |
853f4e6a3ba28dc2230b97bc0c67775f83c4a5e3d850c503b8d174debb03c3de
|
Provenance
The following attestation bundles were made for xcreener-0.1.0.tar.gz:
Publisher:
publish.yml on xcreener/xcreener-sdk-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
xcreener-0.1.0.tar.gz -
Subject digest:
803ea50ba433d99aac677d5b2c1ef74b67f8e11fb3faa42475665450471926c4 - Sigstore transparency entry: 2495285475
- Sigstore integration time:
-
Permalink:
xcreener/xcreener-sdk-python@dbb264024a446dc666f5fa5f8ef7ec9110fb848c -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/xcreener
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@dbb264024a446dc666f5fa5f8ef7ec9110fb848c -
Trigger Event:
push
-
Statement type:
File details
Details for the file xcreener-0.1.0-py3-none-any.whl.
File metadata
- Download URL: xcreener-0.1.0-py3-none-any.whl
- Upload date:
- Size: 17.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d7c223dd6892f9fca172946d01f67288d15a46fb072992d9b48d8b221944f93e
|
|
| MD5 |
69fc156b874bd2e5a496168ef0d78fd4
|
|
| BLAKE2b-256 |
84604234aed014404b8ede72d685b045368b1e6ef11c57fe27ccbd984b59354c
|
Provenance
The following attestation bundles were made for xcreener-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on xcreener/xcreener-sdk-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
xcreener-0.1.0-py3-none-any.whl -
Subject digest:
d7c223dd6892f9fca172946d01f67288d15a46fb072992d9b48d8b221944f93e - Sigstore transparency entry: 2495285480
- Sigstore integration time:
-
Permalink:
xcreener/xcreener-sdk-python@dbb264024a446dc666f5fa5f8ef7ec9110fb848c -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/xcreener
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@dbb264024a446dc666f5fa5f8ef7ec9110fb848c -
Trigger Event:
push
-
Statement type: