schwabdev vs schwab-py
Both wrap the same Schwab API. The difference is what they make easy — and what they force you to own.
There is no official Charles Schwab Python SDK. The two projects that matter are schwabdev (Tyler Bowers) and schwab-py (Alex Golec). Both are MIT-licensed, both are unofficial, and both speak to api.schwabapi.com. Choosing between them is an architecture decision, not a loyalty test.
At a glance
| Dimension | schwabdev 4.0.0 | schwab-py 1.5.1 |
|---|---|---|
| Design | Batteries included: client, token manager, streamer | Thin, unopinionated endpoint wrapper |
| Python | 3.11+ | 3.10+ |
| HTTP | requests (sync), aiohttp (async) | httpx (sync/async) |
| Token storage | SQLite at ~/.schwabdev/tokens.db | JSON token file |
| Encryption | Optional Fernet encryption | Custom access functions; encrypt the file yourself |
| Refresh | Automatic access refresh; refresh-token renewal flow | Automatic refresh via authlib; 6.5-day proactive token age |
| Order construction | Raw Schwab order dictionaries | Equity/options builders, enums, generic builder |
| Streaming | Stream/StreamAsync with reconnect/backoff and start_auto() | Async StreamClient with per-service handlers |
| Type safety | Runtime validation in method wrappers | Strict enum enforcement by default |
| Paper trading | Not supported by Schwab | Not supported by Schwab |
| Historical options | Not provided by API | Not provided by API |
Authentication and token lifecycle
schwabdev is the lower-friction option. It creates a SQLite database, coordinates refresh across multiple client instances with an exclusive transaction, and can encrypt tokens with a Fernet key. The access token is treated as a 30-minute credential and the refresh token as a seven-day credential; the client refreshes the access token near expiry and renews the refresh token through the authorization flow when it is close to its deadline.
schwab-py is the more transparent option. easy_client() loads or creates a JSON token file, the token includes creation metadata, and the client refreshes it through authlib. The default max_token_age is 6.5 days, so a token close to the seven-day boundary is proactively replaced. For headless deployments, client_from_token_file() is the clean path; for serverless/custom storage, client_from_access_functions() lets you plug in a database or secret manager.
Market data and options
Both expose the same endpoints: quotes, price history, option chains, expiration chains, movers, market hours and instruments. schwabdev returns a requests.Response; schwab-py returns an httpx.Response. In both cases you call .json() and handle the status yourself (schwab-py users usually call raise_for_status()).
schwab-py adds convenience methods such as get_price_history_every_minute(), get_price_history_every_five_minutes() and get_price_history_every_day(). schwabdev uses a single price_history() with explicit period/frequency arguments. Neither provides historical option pricing; collect forward snapshots if you need an options dataset.
Orders
This is where the libraries diverge most.
# schwab-py: typed builder
from schwab.orders.equities import equity_buy_limit
order = equity_buy_limit("AAPL", 1, 100.00)
client.place_order(account_hash, order)
# schwabdev: raw Schwab payload
order = {
"orderType": "LIMIT", "session": "NORMAL", "duration": "DAY",
"orderStrategyType": "SINGLE", "price": "100.00",
"orderLegCollection": [{
"instruction": "BUY", "quantity": 1,
"instrument": {"symbol": "AAPL", "assetType": "EQUITY"},
}],
}
client.place_order(account_hash, order)
schwab-py wins for order-heavy systems: builders, enums, vertical spreads and a generic builder for complex orders. schwabdev wins when you already generate the exact Schwab JSON or need to pass fields the builder does not expose. In both cases: preview, persist the order ID, reconcile after timeouts, never blind-retry a POST.
Streaming
schwabdev gives you a managed streamer: Stream(client), start(handler), send(level_one_equities(...)), reconnect/backoff, and start_auto() around market hours. It supports level-one equities/options/futures/forex, books, charts, screeners and account activity.
schwab-py gives you an async protocol client: StreamClient(client, account_id=...), one handler per service, await login(), subscription methods, and a handle_message() loop you control. That is better when the stream is one component of a larger asyncio service.
Which should you choose?
| If you are building… | Choose |
|---|---|
| A quick quote/option scanner or dashboard | schwabdev |
| A long-running streamer with reconnect logic | schwabdev |
| Async fan-out over many symbols | schwabdev ClientAsync or schwab-py async client |
| A typed order engine with options spreads | schwab-py |
| Custom token storage in a secret manager | schwab-py access functions |
Migrating from tda-api | schwab-py |
| Raw endpoint access with minimal abstraction | schwab-py |
A practical pattern is to use schwab-py for typed order construction and schwabdev for the streamer, but running two token managers against the same app adds refresh complexity. Pick one primary wrapper unless you have a clear reason to split.
FAQ
Is one more reliable?
Both are thin over the same API. Reliability comes from your retry, reconciliation and token handling, not from the wrapper name. Pin versions and test upgrades.
Which has better documentation?
schwab-py has ReadTheDocs and a migration guide from tda-api. schwabdev has a setup guide, examples, a Discord and a YouTube series. Both are usable; the choice depends on learning style.
Do both support options orders?
Yes, but schwab-py provides explicit vertical-spread builders and enum validation, while schwabdev expects the raw order payload.
Schwab API Python series: Hub | Full tutorial | Setup & OAuth | schwabdev | schwab-py | Comparison
Need a Schwab or Multi-Broker Architecture?
We build Python execution, data and streaming layers for Schwab, Interactive Brokers and MT5 — including token security, reconciliation and risk controls.
Discuss Your BuildSources: schwabdev and schwab-py. Versions referenced: schwabdev 4.0.0, schwab-py 1.5.1. Both are unofficial, MIT-licensed and not affiliated with Charles Schwab. Not financial advice.
Go from article to implementation
Read the proof, then test the idea with calculators, free tools, case studies and a fixed-price build path.
Go deeper before you build, download or automate
Hand-picked engineering articles, indicator breakdowns and trading-system tools so visitors keep exploring instead of leaving after one page.
Pine Script to MT4/MT5 Conversion Planner
Analyze Pine strategies, repainting risk, TradingView alert bridges, MTF synchronization and the correct MQL4/MQL5 build path before paying for conversion.
Open planner
3-in-1 MT5 Indicator Bundle
12H Bias Engine, Smart Money Institutional Flow Dashboard and D1/H1 pending-signal logic with MQ5 + EX5 source included via Etsy.
MT5 Bank Orders Institutional Flow Dashboard
Supply/demand zones, BOS/CHoCH, liquidity levels, FVG, equal highs/lows and confirmed trade-plan logic explained with EX5 download.
MT5 Asia London NY Liquidity Engine
Asia range, London raid and New York continuation/reversal planning with internal session backtest stats and entry/SL/TP dashboard.
Interactive Brokers RSI Pair Trader
Tkinter desktop trading workflow, TWS API connection handling, RSI pair logic, risk controls and custom IBKR development positioning.
XAUUSD M30 CSV Data For Backtesting
Use clean MT5 historical data for Python backtests, strategy validation, spread checks and realistic gold EA research workflows.