alpaca-sdk
Unofficial Rust SDK for the Alpaca trading, market data, and broker APIs — 251 of the 253 routes the published specs document, across REST, WebSocket and server-sent events.
It targets the API itself rather than any one SDK's reading of it. That distinction is the point of the crate, and what it means in practice is written down below.
Unofficial. Not affiliated with, endorsed by, or sponsored by Alpaca Securities LLC. See NOTICE.
Install
Trading and market data are on by default. The broker API, a synchronous
wrapper, and polars conversion are feature flags.
Quick start
Credentials come from APCA_API_KEY_ID and APCA_API_SECRET_KEY. The true
selects Alpaca's paper environment; pass false for a live account.
use TradingClient;
use ;
async
Placing an order
Every order shape has a constructor — market, limit, stop, stop_limit,
trailing_stop, multi_leg, and the bracket / oco / oto classes on top
of them.
use ;
use ;
async
Market data
Market data is a separate API with its own client and its own entitlements: the free plan reaches IEX, and SIP needs a paid one.
use ;
use ;
async
Live streams
Subscriptions are declared before the socket opens, and replayed on every reconnect — a dropped connection resubscribes itself.
use ;
use ;
use StreamExt as _;
async
examples/ has these as runnable programs — cargo run --example account,
historical_bars, or crypto_stream.
Feature flags
| Feature | Default | Enables |
|---|---|---|
trading |
✅ | Trading REST client and the trade-update stream |
data |
✅ | Historical and live market data |
broker |
Broker API, including its nine SSE event streams | |
blocking |
Synchronous wrapper over any async client | |
polars |
DataFrame conversion for market data collections |
|
rustls-tls |
✅ | TLS via rustls |
native-tls |
TLS via the platform library |
Exactly one TLS backend is required. None of trading, data or broker
implies one, so turning off default features without naming rustls-tls or
native-tls fails the build — rather than compiling into a client that fails
every HTTPS request at runtime.
Streams stay async even under blocking: a blocking iterator over a live feed
deadlocks as soon as the caller is slower than the socket's read buffer.
How the types behave
The decisions a caller actually runs into, and why each one is the way it is.
-
Money that crosses the wire as a string is
Decimal. Market data floats that arrive as JSON numbers stayf64. Reading a string price as a float loses precision on the one field where it matters. The crate re-exports both the type andrust_decimalitself, asalpaca_sdk::Decimalandalpaca_sdk::rust_decimal— reach for those rather than adding the dependency separately, or a version mismatch gives you two incompatibleDecimaltypes with the same name. -
Unknown enum values deserialize into
Unknownrather than failing. Alpaca adds values without warning, and a new order status should cost you a match arm rather than a decode error in production. -
Unknown response fields are ignored, for the same reason.
-
Paginated endpoints offer two methods.
get_xfetches one page;get_all_xwalks every page, with an optional cap. -
Request structs are
#[non_exhaustive]. Build one with the constructor the type provides —new,default, or a named one like theOrderRequest::limitin the example above — and assign fields:use GetOrdersRequest; let mut filter = default; filter.limit = Some;Alpaca documents new query parameters regularly. This is what lets one arrive as a new field instead of as a breaking change.
-
Retries follow Alpaca's own guidance: 429 and 504, three attempts after the first, ~1s doubling to a 30s ceiling with jitter. A response carrying
Retry-Afteroverrides that curve, clamped to the same ceiling.A request that acts is never replayed. A 504 means the gateway stopped waiting for the answer, not that nothing happened — so
submit_order,create_journal, and the position-closing routes are reported rather than retried, whatever the status list says. Only a 429 is replayed regardless, because the rate limiter refuses the request before anything acts on it. If you re-issue one of these yourself, that is the same hazard in your own hands. -
request_rawis the escape hatch for routes this crate does not wrap. The transport is public and hands back the body undecoded.
How this is verified
Alpaca publishes an API reference, vendors OpenAPI specs, and ships five SDKs —
and they do not always agree. This crate ranks its sources by how close each is
to the wire:
- a captured response beats a specification;
- a specification beats another SDK;
- only the published reference says whether a route is still current.
That order is not academic. Three event streams were in the specs, looked healthy from the crate's side, and had been switched off; the reference was the only source that said so.
fixtures/ holds 224 real API responses, and every model is checked against
them. just coverage, just parameters and just enums-drift diff this crate
against the specs and the reference by machine, because reading does not scale
to 251 routes — each of the three found something a careful read had missed.
Route results are checked in at COVERAGE.md.
What is not verified against a live server
Honest limits, and properties of what could be checked rather than of what was built:
- The broker routes have never met a real response. This account has no broker sandbox key, so all 153 are derived from captured payloads, the reference and the specs.
- The
CIP*models are probably unverifiable — the sandbox answers 404 for those routes. - Forex, indices and logos answer
403 insufficient grantseven on a plan that reaches SIP; they are per-product entitlements.
Treat a decode failure on a first real payload in those areas as expected work rather than a regression — and please report it with the raw body.
Minimum supported Rust version
1.88. Enabling polars raises it to 1.95, which is why that feature is off
by default — a convenience feature does not get to set the crate's floor.
Contributing
See CONTRIBUTING.md. The most useful contribution is usually a captured API response or a precise bug report rather than a large patch. Security issues: SECURITY.md.
Release notes are in CHANGELOG.md.
License
Apache-2.0. This crate derives from Apache-2.0 works; see NOTICE.