stonepy
Python client for the StoneX (CIAPI) v2 trading API.
📖 Documentation: https://aaronmgn.github.io/stonepy/
Features
- Typed models. Request and response DTO bodies are
Pydantic v2 models; some methods take primitive parameters or
return bare scalars or lists. The package ships a
py.typedmarker, so editors autocomplete fields and type checkers can check your calls. See the constructor typing notes for current limitations. - Sync and async. Identical APIs on
StoneXClientandAsyncStoneXClient. - Complete coverage. All 128 endpoints of the frozen catalog revision (
CATALOG_VERSION) across 19 resource groups, using the v2 variant of every endpoint that has one. - Batteries included. Automatic session refresh, configurable retries, client-side rate limiting, secret-field omission in configuration and model representations, redaction in request representations, and a clear exception hierarchy.
Project status:
stonepyis pre-1.0 (alpha). The public API may change between minor releases until 1.0; pin a version for production use.
When upgrading, migrate shared request DTOs to Request<Name> variants, replace entry-point
plugins with explicitly constructed resources, and update deprecated resource names. See the
release notes,
model guide, and
extensibility guide
for migration details.
Installation
pip install stonepy
Or with uv:
uv add stonepy
Requires Python >= 3.11 and pydantic >= 2.7 (< 3.0), with pydantic >= 2.12 on Python 3.14
and newer. stonepy ships type information (PEP 561 py.typed) that mypy and pyright
discover automatically; pyright CI is currently advisory.
Quickstart
from stonepy import ClientConfig, StoneXClient
from stonepy.models import ApiLogOnRequestDTO
config = ClientConfig(base_url="https://ciapi.cityindex.com/TradingAPI")
with StoneXClient(config) as client:
session = client.session.log_on(
ApiLogOnRequestDTO(
UserName="username",
Password="password",
AppKey="app-key",
AppVersion="stonepy",
AppComments="",
)
)
print(session.status_code)
Environment-based configuration is also available:
from stonepy import ClientConfig, StoneXClient
config = ClientConfig.from_env()
with StoneXClient(config) as client:
print(client.user_account.get_client_and_trading_account())
ClientConfig.from_env() reads STONEX_BASE_URL, STONEX_APP_KEY, STONEX_USERNAME,
and STONEX_PASSWORD. STONEX_BASE_URL is required unless base_url= is passed.
Authentication and Sessions
Calling client.session.log_on(...) establishes the authenticated session token. The client
attaches the current token to subsequent endpoints that use session authentication. Refresh can
replace the token, and client.session.delete_session(...) clears it.
Automatic refresh is enabled either by supplying app_key, username, and password on
ClientConfig (directly or via ClientConfig.from_env()), or by a successful manual log_on(),
which installs a replay refresh callable. Proactive refresh runs inline, immediately before the
request that needs it - synchronously in StoneXClient and awaited in AsyncStoneXClient, with
no background task - once the stored token reaches ClientConfig.proactive_refresh_seconds
(default 1080.0, i.e. 18 minutes). This threshold is based on token age; no server expiry
timestamp is consulted.
After HTTP 401 or ErrorCode 4011 (never 4010) on an authenticated endpoint, the client
refreshes the session once and replays the request once. This applies to every endpoint,
including non-idempotent order calls, because this authentication rejection means the server did
not process the request. Transport, 5xx, and 429 retries remain idempotency-gated. If
proactive refresh fails with a stonepy error, the client logs a warning and tries the request
with the existing token so reactive authentication recovery can still run. The ErrorCode 4011
envelope is recognised only on non-2xx responses.
config = ClientConfig(
base_url="https://ciapi.cityindex.com/TradingAPI",
app_key="app-key",
username="username",
password="password",
) # credentials present -> automatic proactive session refresh
Async Usage
from stonepy import AsyncStoneXClient, ClientConfig
from stonepy.models import ApiLogOnRequestDTO
config = ClientConfig(base_url="https://ciapi.cityindex.com/TradingAPI")
async with AsyncStoneXClient(config) as client:
session = await client.session.log_on(
ApiLogOnRequestDTO(
UserName="username",
Password="password",
AppKey="app-key",
AppVersion="stonepy",
AppComments="",
)
)
print(session.status_code)
Use aclose() for async clients when not using async with; use close() for sync clients.
Error Handling
StoneXError is the base of the public runtime error hierarchy. Configuration validation
can also raise builtin TypeError or ValueError exceptions.
from stonepy import (
ClientConfig,
RateLimitError,
StoneXAPIError,
StoneXClient,
StoneXError,
)
from stonepy.models import ApiLogOnRequestDTO
config = ClientConfig(base_url="https://ciapi.cityindex.com/TradingAPI")
try:
with StoneXClient(config) as client:
client.session.log_on(
ApiLogOnRequestDTO(
UserName="username",
Password="password",
AppKey="app-key",
AppVersion="stonepy",
AppComments="",
)
)
except RateLimitError as exc:
print(exc.retry_after)
except StoneXAPIError as exc:
print(exc.http_status, exc.error_code, exc.error_message)
except StoneXError as exc:
print(exc)
Important subclasses include AuthenticationError, RateLimitError,
OrderRejectedError, OrderStatusUnknownError, ResponseParseError, StoneXAPIError, and
TransportError.
Unknown fields in nested request DTOs raise Pydantic ValidationError before sending an order.
With the default status checks, an order acknowledgement without a usable status raises
OrderStatusUnknownError; verify the order state before resubmitting.
Pagination
Paginated API methods return the page DTO documented by StoneX. For example,
client.market.list_market_search_paginated(...) accepts page, page_size, and
order_by keyword arguments and returns ListMarketSearchPaginatedResponseDTO:
page = client.market.list_market_search_paginated(
"gold",
search_by_market_code=False,
search_by_market_name=True,
spread_product_type=True,
cfd_product_type=True,
binary_product_type=False,
ascending_order=True,
include_options=False,
client_account_id=12345,
page=0,
page_size=100,
)
print(page.total_number_of_results)
API Reference
Full documentation - the guides and a complete API reference - is published at https://aaronmgn.github.io/stonepy/.
Development
uv venv
uv sync --extra dev
uv run pytest -q
uv run ruff check .
uv run ruff format --check .
uv run mypy
See CONTRIBUTING.md for the full contributor guide and CODE_OF_CONDUCT.md, and the changelog for release notes.
Support
- Questions and bug reports: GitHub issues.
- Security: please report vulnerabilities privately - see SECURITY.md.
AI Use Disclaimer
Portions of this project, including the generated API bindings, DTO models, and documentation, were produced with the assistance of AI tooling and reviewed by a human maintainer. The library is tested against the StoneX CIAPI v2 contract but is provided "as is", without warranty of any kind (see LICENSE).
stonepy is unofficial and is not affiliated with, endorsed by, or supported by StoneX,
City Index, or GAIN Capital. Trading carries financial risk; validate all behaviour against the
official API documentation before using it with a live
account.
Metadata
Release files for stonepy 0.5.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| stonepy-0.5.0.tar.gz | 222.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| stonepy-0.5.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 732.6 kB
Release files / stonepy-0.5.0.tar.gz
| Download URL | stonepy-0.5.0.tar.gz |
|---|---|
| Size | 222.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
6662a6b93111eaeee61b550efc04ebdaf15ad0538a193c48605c8fc35a3ee8c3
|
|
BLAKE2b-256 checksum How to use checksums |
d0f6a9b52277b132d504d45bc27fc3cb0dda3784d4fb0c7642847bd51f405408
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 8, 2026.
Transparency logRelease files / stonepy-0.5.0-py3-none-any.whl
| Download URL | stonepy-0.5.0-py3-none-any.whl |
|---|---|
| Size | 510.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
cbbc98d3c65a9907018f426ad66c99f20da907ffbe8a64b47d9311275f1acdd6
|
|
BLAKE2b-256 checksum How to use checksums |
4096e4a04250673f6b299951011a87235d2d29f7f8e6adf54e2423fc061c02cc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 8, 2026.
Transparency log