| Crates.io | brokerage-api |
| lib.rs | brokerage-api |
| version | 0.2.1 |
| created_at | 2025-08-20 05:43:55.170097+00 |
| updated_at | 2025-09-14 06:47:37.834667+00 |
| description | A library for communicating with various brokerage APIs, in order to fetch real-time and historical equity data. |
| homepage | |
| repository | https://github.com/SeanTolino/brokerage-api |
| max_upload_size | |
| id | 1802903 |
| size | 159,414 |
An unofficial, asynchronous Rust client for the Charles Schwab brokerage API, designed for both stock and options data.
This library provides strongly-typed models for API responses and a robust client that handles authentication, token refreshing, and real-time data streaming, all built on the tokio asynchronous runtime.
async/.await and tokio for high performance.This is an unofficial library and is not affiliated with, endorsed, or supported by Charles Schwab in any way. Use it at your own risk.
This library is currently under active development. The following components are implemented:
The Trading API (placing, modifying, and canceling orders) is not yet implemented.
Add the library to your Cargo.toml:
[dependencies]
schwab_api_rs = "0.2.1"
tokio = { version = "1", features = ["full"] }
anyhow = "1.0"
dotenv = "0.15"
Before you can use the API, you need your App Key (Client ID) and App Secret from your Schwab Developer Portal application.
SCHWAB_APP_KEY="YOUR_APP_KEY_HERE"
SCHWAB_APP_SECRET="YOUR_APP_SECRET_HERE"
// src/bin/auth.rs
use schwab_api_rs::SchwabAuth;
use dotenv::dotenv;
use std::env;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
dotenv().ok(); // Load .env file
let app_key = env::var("SCHWAB_APP_KEY")?;
let app_secret = env::var("SCHWAB_APP_SECRET")?;
let auth = SchwabAuth::default();
auth.authorize(&app_key, &app_secret).await?;
println!("Successfully created tokens.json!");
Ok(())
}
Run this with cargo run --bin auth. It will print a URL. Paste it into your browser, log in, grant access, and then paste the final URL from your browser's address bar back into the terminal. This will create a tokens.json file that the library will use from now on.
Here's how to create a client and fetch quotes for a few stocks. The client will automatically refresh your token if it has expired.
use schwab_api_rs::SchwabApi;
use dotenv::dotenv;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
dotenv().ok(); // Load .env file
// Create a new API client using credentials from environment variables
let api = SchwabApi::default().await?;
let symbols = vec!["AAPL".to_string(), "TSLA".to_string()];
println!("Fetching quotes for {:?}", symbols);
let quotes = api.get_quotes(symbols, None, None).await?;
for (symbol, quote_data) in quotes {
if let Some(quote) = quote_data.quote {
println!(
" - {}: Last Price: ${:.2}, Volume: {}",
symbol, quote.last_price, quote.total_volume
);
}
}
Ok(())
}
The streamer provides real-time data. You start() it to get a channel receiver, then send() subscription requests.
use schwab_api_rs::{
schwab_streamer::{Command, SchwabStreamer},
models::streamer::StreamerMessage,
};
use dotenv::dotenv;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
dotenv().ok(); // Load .env file
// Create the streamer. It uses the same SchwabApi client internally.
let streamer = SchwabStreamer::default().await?;
// Start the listener task and get the receiver for messages
let mut receiver = streamer.start().await?;
// Wait for the streamer to log in and become active
while !streamer.is_active().await {
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
}
println!("Streamer is active. Subscribing to SPY quotes...");
// 1. Build a typed subscription request
let equity_request = streamer.level_one_equities(
vec!["SPY".to_string()],
vec![], // Empty vec subscribes to all available fields
Command::Subs,
);
/// Example options request, using the provided utility method format_option_symbol
/// to make correct formatting easier
let options_request = streamer.level_one_options(
vec![util::format_option_symbol("NVDA", "250919", 'C', 177.5)],
vec![], // Empty vec for all fields
Command::Subs,
);
// 2. Send the request
streamer.send(vec![equity_request]).await?;
println!("Subscription sent. Waiting for messages...");
// 3. Listen for incoming messages on the channel
while let Some(message) = receiver.recv().await {
match message {
StreamerMessage::LevelOneEquity(equity_quote) => {
println!(
"EQUITY -> {}: Bid: {:?}, Ask: {:?}",
equity_quote.symbol,
equity_quote.bid_price,
equity_quote.ask_price
);
}
StreamerMessage::LevelOneOption(option_quote) => {
println!(
"OPTION -> {}: Mark Price: {:?}",
option_quote.symbol,
option_quote.mark_price
);
}
}
}
Ok(())
}
Implement a custom, specific Error type.
Implement Trading API endpoints (orders, accounts).
Add automatic resubscription on streamer reconnect.
Add more examples and documentation.
Contributions are welcome! Feel free to open an issue or submit a pull request.
This project is licensed under either of:
Apache License, Version 2.0, (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.