Cliente ligero para API de Schwab: OAuth, REST y Streaming WebSocket
Project description
Schwab SDK (Python)
Lightweight client for the Schwab API: OAuth, REST (Trader/Market Data), and WebSocket Streaming.
- Focus: thin wrappers, no heavy validation; robust token handling, refresh, and retries.
- Coverage: Accounts, Orders, Market Data, and Streaming (Level One, Book, Chart, Screener, Account Activity).
Installation
Unofficial PyPI package (distribution name):
pip install schwab_sdk_unofficial
Import in code (module):
from schwab_sdk import Client
Note: The package name on PyPI is
schwab_sdk_unofficial, but the import remainsschwab_sdkfor a clean API.
Table of Contents
- Requirements
- Configuration
- Quick Start
- Authentication (OAuth)
- Request & Error Handling (REST)
- Accounts (
accounts.py) - Orders (
orders.py) - Market Data (
market.py) - WebSocket Streaming (
streaming.py) - Enums Reference (
enums.py) - Advanced Troubleshooting
- Contributions
- Disclaimer
- License
Requirements
- Python 3.9+
- Dependencies (installed automatically with
pip install schwab_sdk_unofficial):
| Package | Purpose |
|---|---|
httpx |
HTTP client (REST + OAuth) |
fastapi |
OAuth callback server |
uvicorn |
ASGI server for callback |
cryptography |
Adhoc TLS certs & token encryption |
python-multipart |
Form POST parsing in callback |
websocket-client |
Streaming WebSocket |
websockets |
Async streaming |
Configuration
Create .env (or export environment variables):
SCHWAB_CLIENT_ID=your_client_id
SCHWAB_CLIENT_SECRET=your_client_secret
SCHWAB_REDIRECT_URI=https://127.0.0.1:8080/callback
Tokens are saved in schwab_tokens.json by default and rotate automatically (access ~29 min; re-login notice when refresh expires ~7 d). Set persist=False to keep tokens only in memory (useful for DB-backed storage).
Quick Start
from schwab_sdk import Client
import os
client = Client(
os.environ['SCHWAB_CLIENT_ID'],
os.environ['SCHWAB_CLIENT_SECRET'],
os.environ.get('SCHWAB_REDIRECT_URI', 'https://127.0.0.1:8080/callback'),
token_path='my_tokens.json', # Optional: custom token file path
persist=True, # Optional: False to keep tokens only in memory
)
# First use: OAuth login (opens the browser)
client.login()
# REST
quotes = client.market.get_quotes(["AAPL", "MSFT"]) # Market Data
accounts = client.account.get_accounts() # Accounts
# Streaming (Level One equities)
ws = client.streaming
ws.on_data(lambda f: print("DATA", f))
ws.connect(); ws.login()
ws.equities_subscribe(["AAPL"])
Authentication (OAuth)
client.login(timeout=300, auto_open_browser=True, callback_port=None)- Handy:
client.has_valid_token(),client.refresh_token_now(),client.logout() - Manual flow:
client.get_auth_url()+client.exchange_code(code)(sync) orawait client.exchange_code(code)(AsyncClient) - Internals: adhoc HTTPS callback server (FastAPI + uvicorn), code-for-token exchange, auto-refresh and notice when refresh expires.
Token storage options
By default tokens are persisted to a JSON file (schwab_tokens.json). You can customize this:
# Custom file path
client = Client(client_id, client_secret, redirect_uri, token_path="tokens/my_tokens.json")
# Encrypted at rest (AES-256-GCM)
client = Client(client_id, client_secret, redirect_uri, encryption_key="my-secret")
# In-memory only (no file written) — useful for DB-backed storage
client = Client(client_id, client_secret, redirect_uri, persist=False)
# After login, read tokens to store in your DB:
handler = client.token_handler
db.save(
access_token=handler.access_token,
refresh_token=handler.refresh_token,
access_expires=handler.access_token_expires_at,
refresh_expires=handler.refresh_token_expires_at,
)
Client constructor parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
client_id |
str |
required | Schwab application Client ID |
client_secret |
str |
required | Schwab application Client Secret |
redirect_uri |
str |
https://localhost:8080/callback |
OAuth redirect URI |
token_path |
str | None |
"schwab_tokens.json" |
Path to token file |
encryption_key |
str | None |
None |
Passphrase for AES-256-GCM token encryption |
persist |
bool |
True |
False to keep tokens only in memory |
Request & Error Handling (REST)
All REST calls go through Client._request() which provides automatic error recovery:
- Authorization: Bearer token header injected automatically.
- 401 Unauthorized: refreshes the access token once and retries immediately.
- 429 / 5xx: retries up to 3 times with exponential backoff (
0.5 * 2^attemptseconds). - Network errors (
httpx.RequestError): same retry logic as 429/5xx.
Exception hierarchy
HTTP errors raise a subclass of SchwabAPIError. Each exception carries:
| Attribute | Type | Description |
|---|---|---|
status_code |
int |
HTTP status code |
message |
str |
Human-readable error from response body |
correl_id |
str | None |
Schwab-Client-CorrelId header (for Schwab support) |
errors |
list[dict] |
Raw error dicts from the response body |
response |
httpx.Response | None |
Raw response for advanced inspection |
| Exception | HTTP Status | When |
|---|---|---|
SchwabValidationError |
400 | Invalid parameters or malformed request body |
SchwabAuthenticationError |
401 | Access token missing, invalid, or expired |
SchwabForbiddenError |
403 | Valid credentials but insufficient permissions |
SchwabNotFoundError |
404 | Requested resource does not exist |
SchwabRateLimitError |
429 | API rate limit exceeded (after retries) |
SchwabServerError |
500/502/503/504 | Schwab-side failure (after retries) |
SchwabTokenExpiredError |
(subclass of 401) | Refresh token expired; full re-login required |
SchwabParameterError |
(client-side) | ValueError subclass for parameter validation |
Error handling example
from schwab_sdk import (
Client,
SchwabAPIError,
SchwabNotFoundError,
SchwabRateLimitError,
SchwabAuthenticationError,
)
try:
account = client.account.get_account_by_id("invalid-hash")
except SchwabNotFoundError as e:
print(f"Not found: {e.message}")
print(f"Correlation ID: {e.correl_id}") # for Schwab support
except SchwabRateLimitError as e:
print(f"Rate limited after retries: {e.message}")
except SchwabAuthenticationError as e:
print("Token expired, re-login needed")
client.login()
except SchwabAPIError as e:
print(f"API error {e.status_code}: {e.message}")
Schwab error response format
{
"errors": [
{
"id": "uuid",
"status": "400",
"title": "Bad Request",
"detail": "Missing header",
"source": {"header": "Authorization"}
}
]
}
The SDK extracts detail, title, or message from each error and joins them into e.message. The Schwab-Client-CorrelId response header is captured in e.correl_id.
Factory function
raise_for_schwab_status(response) inspects an httpx.Response and raises the appropriate exception for non-2xx status codes. Used internally by each endpoint module (accounts, orders, market) after calling _request(), and also available for custom flows:
from schwab_sdk import raise_for_schwab_status
raise_for_schwab_status(response) # raises SchwabValidationError, SchwabNotFoundError, etc.
Accounts (accounts.py)
Possible exceptions: SchwabAuthenticationError (401), SchwabForbiddenError (403), SchwabNotFoundError (404), SchwabServerError (5xx)
Related enums: AccountField, TransactionType, AccountInstrumentType, TransactionStatus
get_account_numbers() -> list[dict]
- GET
/accounts/accountNumbers - Returns
accountNumberandhashValuepairs. - Example response:
[
{"accountNumber":"12345678","hashValue":"827C...AC12"}
]
get_accounts(fields=None, params=None) -> dict
-
GET
/accounts -
Parameters:
fields(optional):List[str]— use["positions"]or[AccountField.POSITIONS]to include positions.params(optional): additional query parameters.
get_account_by_id(account_hash, fields=None, params=None) -> dict
-
GET
/accounts/{accountNumber} -
account_hash: encrypted account identifier (hashValue). -
Raises
SchwabNotFoundErrorifaccount_hashis invalid. -
Parameters:
fields(optional):List[str]—["positions"]or[AccountField.POSITIONS]to include positions.params(optional): additional query parameters.
find_account(last_4_digits: str) -> dict|None
- Helper that uses
get_account_numbers()and filters by the last 4 digits, then callsget_account_by_id.
get_transactions(account_hash, from_date=None, to_date=None, transaction_types=None, symbol=None, filters=None) -> dict
-
GET
/accounts/{accountHash}/transactions -
At least one date recommended: you may pass only
from_dateor onlyto_date. If you pass a single date, the SDK fills in the other for the same day. If both are omitted, the API may return an error or empty results:- Short format
YYYY-MM-DD: start →YYYY-MM-DDT00:00:00.000Z, end →YYYY-MM-DDT23:59:59.000Z - Full ISO UTC
YYYY-MM-DDTHH:MM:SS.ffffffZ: used as-is; if the other date is missing, it is derived with00:00:00.000Zor23:59:59.000Zof the same day.
- Short format
-
Parameters:
account_hash(required): encrypted account identifier (hashValue)from_date(optional): start date —YYYY-MM-DDorYYYY-MM-DDTHH:MM:SS.ffffffZto_date(optional): end date —YYYY-MM-DDorYYYY-MM-DDTHH:MM:SS.ffffffZtransaction_types(optional):List[str]ofTransactionTypevalues:TRADE,RECEIVE_AND_DELIVER,DIVIDEND_OR_INTEREST,ACH_RECEIPT,ACH_DISBURSEMENT,CASH_RECEIPT,CASH_DISBURSEMENT,ELECTRONIC_FUND,WIRE_OUT,WIRE_IN,JOURNAL,MEMORANDUM,MARGIN_CALL,MONEY_MARKET,SMA_ADJUSTMENTsymbol(optional): filter by specific symbolfilters(optional): additional query parameters dict
Correct example:
from datetime import datetime, timezone, timedelta
# Get hashValue
hash_value = client.account.get_account_numbers()[0]['hashValue']
# Create UTC dates
start = datetime.now(timezone.utc) - timedelta(days=7)
start = start.replace(hour=0, minute=0, second=0, microsecond=0)
end = datetime.now(timezone.utc).replace(hour=23, minute=59, second=59, microsecond=999999)
# Proper call
transactions = client.account.get_transactions(
account_hash=hash_value, # Use hashValue!
from_date=start.strftime('%Y-%m-%dT%H:%M:%S.%fZ'),
to_date=end.strftime('%Y-%m-%dT%H:%M:%S.%fZ'),
transaction_types=["TRADE", "DIVIDEND_OR_INTEREST"], # Optional
)
get_transaction(account_hash: str, transaction_id: str) -> dict
-
GET
/accounts/{accountHash}/transactions/{transactionId} -
Path parameters:
account_hash(required)transaction_id(required): numeric transaction ID
-
Returns details of a specific transaction.
get_user_preferences() -> dict
-
GET
/userPreference -
Returns user preferences and, when applicable, streamer information needed for WebSocket:
streamerSocketUrlschwabClientCustomerIdschwabClientCorrelIdSchwabClientChannelSchwabClientFunctionId
-
Useful to initialize
client.streaming(LOGIN and subscriptions).
Orders (orders.py)
Possible exceptions: SchwabValidationError (400), SchwabAuthenticationError (401), SchwabForbiddenError (403), SchwabNotFoundError (404), SchwabServerError (5xx)
Related enums: OrderType, Duration, Session, OrderStatus, EquityInstruction, OptionInstruction, AssetType, OrderStrategyType, ComplexOrderStrategyType, SpecialInstruction, RequestedDestination, PriceLinkBasis, PriceLinkType, StopType, TaxLotMethod, PositionEffect, QuantityType
All responses include HTTP metadata and the native data:
{
"status_code": 200,
"success": true,
"headers": {"...": "..."},
"url": "https://...",
"elapsed_seconds": 0.42,
"method": "GET|POST|PUT|DELETE",
"params": {"...": "..."},
"data": {},
"order_id": "..."
}
get_orders(account_hash, from_entered_time=None, to_entered_time=None, status=None, max_results=None) -> dict
- GET
/accounts/{accountNumber}/orders - API requirements:
fromEnteredTimeandtoEnteredTimeare mandatory (ISO-8601). The SDK defaults them to "last 60 days" if you omit them. status(case-insensitive):OrderStatusenum — normalized to uppercase. See OrderStatus for all values.maxResults(optional): record limit (API default 3000).- Datetime format: ISO-8601
YYYY-MM-DDTHH:MM:SS.000Z.
get_all_orders(from_entered_time=None, to_entered_time=None, status=None, max_results=None) -> dict
- GET
/orders - API requirements:
fromEnteredTimeandtoEnteredTimeare mandatory (ISO-8601). If you omit them, the SDK uses "last 60 days". - Filters identical to
get_orders(includingstatusnormalization). maxResults(optional): record limit (API default 3000).
place_order(account_hash: str, order_data: dict) -> dict
- POST
/accounts/{accountNumber}/orders - Extracts
order_idfrom theLocationheader when present.
get_order(account_hash: str, order_id: str) -> dict
- GET
/accounts/{accountNumber}/orders/{orderId}
cancel_order(account_hash: str, order_id: str) -> dict
- DELETE
/accounts/{accountNumber}/orders/{orderId} - The
order_idyou pass is included in the result dict asorder_id.
replace_order(account_hash: str, order_id: str, new_order_data: dict) -> dict
- PUT
/accounts/{accountNumber}/orders/{orderId} - Returns
original_order_id(the id you passed) and neworder_id(fromLocationheader) when the server provides it.
preview_order(account_hash: str, order_data: dict) -> dict
- POST
/accounts/{accountNumber}/previewOrder - Returns preview data; no order is placed.
Payload helpers
build_limit_order(symbol, quantity, price, instruction="BUY")build_market_order(symbol, quantity, instruction="BUY")build_bracket_order(symbol, quantity, entry_price, take_profit_price, stop_loss_price)
Example (preview):
acc = client.account.get_account_numbers()[0]['hashValue']
order = client.orders.build_limit_order("AAPL", 1, 100.00)
preview = client.orders.preview_order(acc, order)
Market Data (market.py)
Possible exceptions: SchwabValidationError (400), SchwabAuthenticationError (401), SchwabNotFoundError (404), SchwabServerError (5xx)
Related enums: ContractType, Strategy, StrikeRange, ExpirationMonth, OptionType, Entitlement, PeriodType, FrequencyType, QuoteField, MoverIndex, MoverSort, MoverFrequency, MarketID, Projection
Response enums: AssetMainType, AssetSubType, SecurityStatus, Exchange, MICId, ExchangeName, QuoteType
get_quotes(symbols, fields=None, indicative=None, params=None) -> dict
-
GET
/quotes?symbols=... -
Parameters:
symbols(required):strorList[str]. E.g.:"AAPL"or["AAPL", "AMZN", "$DJI"].fields(optional):strorList[str]—QuoteFieldvalues:quote,fundamental,extended,reference,regular,all. Default: all.indicative(optional):bool— include indicative symbol quotes for ETFs.params(optional): additional query parameters.
get_quote(symbol, fields=None, indicative=None, params=None) -> dict
-
GET
/{symbol}/quotes -
Raises
SchwabNotFoundErrorif the symbol does not exist. -
Parameters:
symbol(required): single symbol (e.g.,"TSLA").fields(optional):strorList[str]— sameQuoteFieldvalues asget_quotes.indicative(optional):bool— include indicative symbol quotes for ETFs.params(optional): additional query parameters.
get_option_chain(symbol: str, contract_type: str|None=None, strike_count: int|None=None, include_underlying_quote: bool|None=None, strategy: str|None=None, interval: float|None=None, strike: float|None=None, range_type: str|None=None, from_date: str|None=None, to_date: str|None=None, volatility: float|None=None, underlying_price: float|None=None, interest_rate: float|None=None, days_to_expiration: int|None=None, exp_month: str|None=None, option_type: str|None=None, entitlement: str|None=None, params: dict|None=None) -> dict
-
GET
/chains -
Parameters:
symbol(required): Underlying asset symbolcontract_type(optional):ContractType—CALL,PUT,ALLstrike_count(optional): Number of strikes above/below ATMinclude_underlying_quote(optional): Include underlying quotes (boolean)strategy(optional):Strategy—SINGLE(default),ANALYTICAL,COVERED,VERTICAL,CALENDAR,STRANGLE,STRADDLE,BUTTERFLY,CONDOR,DIAGONAL,COLLAR,ROLLinterval(optional): Strike interval for spread strategy chainsstrike(optional): Strike Pricerange_type(optional):StrikeRange—ITM,NTM,OTM,SAK,SBK,SNK,ALLfrom_date(optional): From date (yyyy-MM-dd)to_date(optional): To date (yyyy-MM-dd)volatility(optional): Volatility (ANALYTICAL strategy only)underlying_price(optional): Underlying price (ANALYTICAL strategy only)interest_rate(optional): Interest rate (ANALYTICAL strategy only)days_to_expiration(optional): Days to expiration (ANALYTICAL strategy only)exp_month(optional):ExpirationMonth—JAN..DEC,ALLoption_type(optional):OptionType—STANDARD,NON_STANDARD,ALLentitlement(optional):Entitlement—PN(NonPayingPro),NP(NonPro),PP(PayingPro)params(optional): Additional query parameters
get_expiration_chain(symbol: str, params: dict|None=None) -> dict
-
GET
/expirationchain -
Parameters:
symbol(required): Underlying asset symbolparams(optional): Additional query parameters
-
Returns: JSON response with option expiration dates for the symbol
get_price_history(symbol, periodType="month", period=1, frequencyType="daily", frequency=1, startDate=None, endDate=None, need_extended_hours_data=None, need_previous_close=None, params=None) -> dict
-
GET
/pricehistory -
Parameters:
symbol(required): instrument symbolperiodType:PeriodType—day,month,year,ytdperiod: int — number of periodsfrequencyType:FrequencyType—minute,daily,weekly,monthlyfrequency: int — specific frequencystartDate/endDate: epoch millis (int),datetime,date, or"YYYY-MM-DD"stringneed_extended_hours_data(optional):bool— include extended hours data (default: True)need_previous_close(optional):bool— include previous close price/dateparams(optional): additional query parameters
get_movers(symbol_id: str, sort: str|None=None, frequency: int|None=None, params: dict|None=None) -> dict
-
GET
/movers/{symbol_id}(e.g.,$DJI,$SPX,NASDAQ) -
Parameters:
symbol_id(required):MoverIndex—$DJI,$COMPX,$SPX,NYSE,NASDAQ,OTCBB,INDEX_ALL,EQUITY_ALL,OPTION_ALL,OPTION_PUT,OPTION_CALLsort(optional):MoverSort—VOLUME,TRADES,PERCENT_CHANGE_UP,PERCENT_CHANGE_DOWNfrequency(optional):MoverFrequency—0,1,5,10,30,60(minutes). Default0params(optional): Additional query parameters
get_markets(markets=None, date=None, params=None) -> dict
-
GET
/markets -
Parameters:
markets(optional):List[str]ofMarketIDvalues —equity,option,bond,future,forex. IfNone, returns all markets.date(optional):YYYY-MM-DD(if you send ISO, the SDK trims to date-only)params(optional): additional query parameters
get_market_hours(market_id, date=None, params=None) -> dict
-
GET
/markets/{market_id}—MarketID:equity,option,bond,future,forex -
Parameters:
market_id(required): validated againstMarketIDenumdate(optional):YYYY-MM-DD(if you send ISO, the SDK trims to date-only)params(optional): additional query parameters
get_instruments(symbols, projection="symbol-search", extra_params=None) -> dict
-
GET
/instruments -
Parameters:
symbols(required):strorList[str]— single symbol or list of symbolsprojection(optional):Projection—symbol-search(default),symbol-regex,desc-search,desc-regex,search,fundamentalextra_params(optional): additional query parameters
get_instrument_by_cusip(cusip_id, params=None) -> dict
-
GET
/instruments/{cusip_id} -
Parameters:
cusip_id(required): instrument CUSIP identifierparams(optional): additional query parameters
WebSocket Streaming (streaming.py)
Related enums: StreamService, StreamCommand, StreamResponseCode
Callbacks
on_data(fn)(data frames)on_response(fn)(command confirmations/errors)on_notify(fn)(heartbeats/notices)
Basic flow
ws = client.streaming
ws.on_data(lambda f: print("DATA", f))
ws.on_response(lambda f: print("RESP", f))
ws.on_notify(lambda f: print("NOTIFY", f))
ws.connect(); ws.login() # Authorization = access token without "Bearer"
ws.equities_subscribe(["AAPL","MSFT"]) # LEVELONE_EQUITIES
ws.options_subscribe(["AAPL 250926C00257500"]) # LEVELONE_OPTIONS
ws.nasdaq_book(["MSFT"]) # NASDAQ_BOOK
ws.chart_equity(["AAPL"]) # CHART_EQUITY
ws.screener_equity(["NYSE_VOLUME_5"]) # SCREENER_EQUITY
Key Formats (quick table)
| Type | Format | Example | Notes |
|---|---|---|---|
| Equities | Ticker | AAPL, MSFT |
Uppercase |
| Options | RRRRRRYYMMDDsWWWWWddd |
AAPL 251219C00200000 |
6-char symbol, YYMMDD, C/P, 5+3 strike |
| Futures | /<root><month><yy> |
/ESZ25 |
Root/month/year in uppercase |
| FuturesOptions | ./<root><month><year><C/P><strike> |
./OZCZ23C565 |
Depends on the feed |
| Forex | PAIR |
EUR/USD, USD/JPY |
/ separator |
| Screener | PREFIX_SORTFIELD_FREQUENCY |
NYSE_VOLUME_5 |
Prefix/criterion/frequency |
Service-by-Service Examples
Level One Options
ws.options_subscribe(["AAPL 250926C00257500"]) # standard option string
# Default fields: 0,2,3,4,8,16,17,18,20,28,29,30,31,37,44
Level One Futures
ws.futures_subscribe(["/ESZ25"]) # E-mini S&P 500 Dec 2025
# Default fields: 0,1,2,3,4,5,8,12,13,18,19,20,24,33
Level One Futures Options
ws.futures_options_subscribe(["./OZCZ23C565"])
# Default fields: 0,1,2,3,4,5,8,12,13,17,18,19,23,24,25,29
Level One Forex
ws.forex_subscribe(["EUR/USD","USD/JPY"])
# Default fields: 0,1,2,3,4,5,6,7,8,9,10,11,15,16,17,20,21,27,28,29
Book (Level II)
ws.nasdaq_book(["MSFT"]) # Also: ws.nyse_book, ws.options_book
# Default fields: 0 (Symbol), 1 (BookTime), 2 (Bids), 3 (Asks)
Chart (Series)
ws.chart_equity(["AAPL"]) # 0..7: key, open, high, low, close, volume, sequence, chartTime
ws.chart_futures(["/ESZ25"]) # 0..6
Screener
ws.screener_equity(["NYSE_VOLUME_5"])
ws.screener_options(["CBOE_VOLUME_5"])
Account Activity
ws.account_activity() # Gets accountHash and subscribes
Utilities
connect()— open the WebSocket connectionwait_until_connected(timeout=10.0)— block until the connection is readylogin(...)— authenticate the stream (sends ADMIN LOGIN)logout()— send ADMIN LOGOUTdisconnect()— close the WebSocket connection (AsyncStreaming only)subscribe(service, keys, fields=None)— generic SUBSadd(service, keys)— generic ADDunsubscribe(service, keys)/unsubscribe_service(service, keys)— generic UNSUBSview(service, fields)— generic VIEW
AsyncStreaming:
AsyncClient.streamingreturns anAsyncStreaminginstance. It mirrors allStreamingmethods asasync/await, addsdisconnect(), and is safe for use in async frameworks (FastAPI, etc.).
Option Symbol Helper
create_option_symbol(symbol, expiration, option_type, strike_price)- Creates Schwab option symbol from components
Example:
# Create option symbol from components
option_symbol = ws.create_option_symbol("AAPL", "2025-12-19", "C", 200.0)
# Returns: "AAPL 251219C00200000"
# Use in subscription
ws.options_subscribe([option_symbol])
Parameters:
symbol: Underlying symbol (e.g., "AAPL")expiration: Expiration date in "YYYY-MM-DD" format (e.g., "2025-10-03")option_type: "C" for Call or "P" for Putstrike_price: Strike price (e.g., 257.5)
Recommended Fields
- LEVELONE_EQUITIES:
0,1,2,3,4,5,8,10,18,42,33,34,35 - LEVELONE_OPTIONS:
0,2,3,4,8,16,17,18,20,28,29,30,31,37,44 - LEVELONE_FUTURES:
0,1,2,3,4,5,8,12,13,18,19,20,24,33 - CHART_EQUITY:
0,1,2,3,4,5,6,7
Quick fields table (copy/paste)
| Service | Fields CSV |
|---|---|
| LEVELONE_EQUITIES | 0,1,2,3,4,5,8,10,18,42,33,34,35 |
| LEVELONE_OPTIONS | 0,2,3,4,8,16,17,18,20,28,29,30,31,37,44 |
| LEVELONE_FUTURES | 0,1,2,3,4,5,8,12,13,18,19,20,24,33 |
| LEVELONE_FUTURES_OPTIONS | 0,1,2,3,4,5,8,12,13,17,18,19,23,24,25,29 |
| LEVELONE_FOREX | 0,1,2,3,4,5,6,7,8,9,10,11,15,16,17,20,21,27,28,29 |
| NASDAQ_BOOK | 0,1,2,3 |
| NYSE_BOOK | 0,1,2,3 |
| OPTIONS_BOOK | 0,1,2,3 |
| CHART_EQUITY | 0,1,2,3,4,5,6,7 |
| CHART_FUTURES | 0,1,2,3,4,5,6 |
| SCREENER_EQUITY | 0,1,2,3,4 |
| SCREENER_OPTION | 0,1,2,3,4 |
| ACCT_ACTIVITY | 0,1,2,3 |
SUBS / ADD / VIEW / UNSUBS examples by service
ws = client.streaming
ws.on_data(lambda f: print("DATA", f))
ws.on_response(lambda f: print("RESP", f))
ws.connect(); ws.login()
# LEVELONE_EQUITIES
ws.equities_subscribe(["AAPL","TSLA"], fields=[0,1,2,3,4,5,8,10,18,42,33,34,35])
ws.equities_add(["MSFT"]) # adds without replacing
ws.equities_view([0,1,2,3,5,8,18]) # changes fields
ws.equities_unsubscribe(["TSLA"]) # removes symbols
# LEVELONE_OPTIONS
ws.options_subscribe(["AAPL 250926C00257500"], fields=[0,2,3,4,8,16,17,18,20,28,29,30,31,37,44])
ws.options_add(["AAPL 250926P00257500"])
ws.options_view([0,2,3,4,8,16,17,20,28,29,30,31,37,44])
ws.options_unsubscribe(["AAPL 250926C00257500"])
# LEVELONE_FUTURES
ws.futures_subscribe(["/ESZ25"], fields=[0,1,2,3,4,5,8,12,13,18,19,20,24,33])
ws.futures_add(["/NQZ25"])
ws.futures_view([0,1,2,3,4,5,8,12,13,18,19,20,24,33])
ws.futures_unsubscribe(["/ESZ25"])
# LEVELONE_FUTURES_OPTIONS
ws.futures_options_subscribe(["./OZCZ23C565"], fields=[0,1,2,3,4,5,8,12,13,17,18,19,23,24,25,29])
ws.futures_options_add(["./OZCZ23P565"])
ws.futures_options_view([0,1,2,3,4,5,8,12,13,17,18,19,23,24,25,29])
ws.futures_options_unsubscribe(["./OZCZ23C565"])
# LEVELONE_FOREX
ws.forex_subscribe(["EUR/USD","USD/JPY"], fields=[0,1,2,3,4,5,6,7,8,9,10,11,15,16,17,20,21,27,28,29])
ws.forex_add(["GBP/USD"])
ws.forex_view([0,1,2,3,4,5,6,7,8,9,10,11,15,16,17,20,21,27,28,29])
ws.forex_unsubscribe(["USD/JPY"])
# BOOK (Level II)
ws.nasdaq_book(["MSFT"], fields=[0,1,2,3])
ws.add("NASDAQ_BOOK", ["AAPL"]) # generic ADD
ws.view("NASDAQ_BOOK", [0,1,2,3]) # generic VIEW
ws.unsubscribe_service("NASDAQ_BOOK", ["MSFT"]) # generic UNSUBS
# CHART (Series)
ws.chart_equity(["AAPL"], fields=[0,1,2,3,4,5,6,7])
ws.add("CHART_EQUITY", ["MSFT"]) # generic ADD
ws.view("CHART_EQUITY", [0,1,2,3,4,5,6,7]) # generic VIEW
ws.unsubscribe("CHART_EQUITY", ["AAPL"]) # generic UNSUBS
# SCREENER
ws.screener_equity(["EQUITY_ALL_VOLUME_5"], fields=[0,1,2,3,4])
ws.add("SCREENER_EQUITY", ["NYSE_TRADES_1"])
ws.view("SCREENER_EQUITY", [0,1,2,3,4])
ws.unsubscribe("SCREENER_EQUITY", ["EQUITY_ALL_VOLUME_5"])
# ACCT_ACTIVITY
ws.account_activity(fields=[0,1,2,3]) # subscribe account activity
# For UNSUBS you need the same key used in SUBS (account_hash)
account_hash = getattr(client, "_account_hash", None)
if account_hash:
ws.unsubscribe_service("ACCT_ACTIVITY", [account_hash])
Quick Field Guide (IDs → meaning)
Note: exact mappings may vary depending on entitlements/version. Below are practical equivalences observed in frames.
LEVELONE_EQUITIES
| ID | Field |
|---|---|
| 0 | symbol/key |
| 1 | bidPrice |
| 2 | askPrice |
| 3 | lastPrice |
| 4 | bidSize |
| 5 | askSize |
| 8 | totalVolume |
| 10 | referencePrice (open/mark) |
| 18 | netChange |
| 42 | percentChange |
LEVELONE_OPTIONS
| ID | Field |
|---|---|
| 0 | symbol/key |
| 2 | bidPrice |
| 3 | askPrice |
| 4 | lastPrice |
| 8 | totalVolume |
| 16 | openInterest |
| 17 | daysToExpiration |
| 20 | strikePrice |
| 28 | delta |
| 29 | gamma |
| 30 | theta |
| 31 | vega |
| 44 | impliedVolatility (if provided) |
LEVELONE_FUTURES
| ID | Field |
|---|---|
| 0 | symbol/key |
| 1 | bidPrice |
| 2 | askPrice |
| 3 | lastPrice |
| 4 | bidSize |
| 5 | askSize |
| 8 | totalVolume |
| 12 | openInterest |
| 13 | contractDepth/series info (per feed) |
| 18 | netChange |
| 19 | sessionChange (or days/indicator per feed) |
| 20 | percentChange/ratio (per feed) |
| 24 | lastSettlement/mark |
| 33 | priorSettle |
Frame Structure
- Confirmations (
response):{ "response": [ { "service":"ADMIN","command":"LOGIN","content":{"code":0,"msg":"..."}} ] } - Data (
data):{ "service":"LEVELONE_EQUITIES","timestamp":...,"command":"SUBS","content":[{"key":"AAPL",...}] } - Notifications (
notify):{ "notify": [ { "heartbeat": "..." } ] }
Streamer API Cheat Sheet (parameters and commands)
- Connection and prerequisites
- Auth: use the Access Token from the OAuth flow.
- Session IDs (from
GET /userPreference):schwabClientCustomerId,schwabClientCorrelId,SchwabClientChannel,SchwabClientFunctionId. - Transport: JSON WebSocket. One stream per user (if you open more: code 12 CLOSE_CONNECTION).
- Envelope of each command
-
Common fields:
service(req.):ADMIN,LEVELONE_EQUITIES,LEVELONE_OPTIONS,LEVELONE_FUTURES,LEVELONE_FUTURES_OPTIONS,LEVELONE_FOREX,NYSE_BOOK,NASDAQ_BOOK,OPTIONS_BOOK,CHART_EQUITY,CHART_FUTURES,SCREENER_EQUITY,SCREENER_OPTION,ACCT_ACTIVITY.command(req.):LOGIN,SUBS,ADD,UNSUBS,VIEW,LOGOUT.requestid(req.): unique request identifier.SchwabClientCustomerIdandSchwabClientCorrelId(recommended): fromuserPreference.parameters(optional): depends on service/command.
-
Notes:
SUBSoverwrites list;ADDappends;UNSUBSremoves;VIEWchangesfields.
- ADMIN (session)
-
LOGIN(service=ADMIN,command=LOGIN)- parameters:
Authorization(token without "Bearer"),SchwabClientChannel,SchwabClientFunctionId.
- parameters:
-
LOGOUT(service=ADMIN,command=LOGOUT)- parameters: empty.
- LEVEL ONE (L1 quotes)
- Common parameters:
keys(req., CSV list),fields(optional, indexes). LEVELONE_EQUITIES:keysuppercase tickers (e.g.,AAPL,TSLA).LEVELONE_OPTIONS:keysSchwab option formatRRRRRR YYMMDD[C/P]STRIKE.LEVELONE_FUTURES:keys/<root><monthCode><yearCode>(month codes: F,G,H,J,K,M,N,Q,U,V,X,Z; year two digits), e.g.,/ESZ25.LEVELONE_FUTURES_OPTIONS:keys./<root><month><yy><C|P><strike>, e.g.,./OZCZ23C565.LEVELONE_FOREX:keysBASE/QUOTEpairs CSV (e.g.,EUR/USD,USD/JPY).
- BOOK (Level II)
- Services:
NYSE_BOOK,NASDAQ_BOOK,OPTIONS_BOOK. - Parameters:
keys(req., tickers),fields(optional, level indexes).
- CHART (streaming series)
CHART_EQUITY:keysequities;fieldsindexes (OHLCV, time, seq).CHART_FUTURES:keysfutures (same format as L1 futures);fieldsindexes.
- SCREENER (gainers/losers/actives)
-
Services:
SCREENER_EQUITY,SCREENER_OPTION. -
keyspatternPREFIX_SORTFIELD_FREQUENCY, e.g.,EQUITY_ALL_VOLUME_5.PREFIXexamples:$COMPX,$DJI,$SPX,INDEX_ALL,NYSE,NASDAQ,OTCBB,EQUITY_ALL,OPTION_PUT,OPTION_CALL,OPTION_ALL.SORTFIELD:VOLUME,TRADES,PERCENT_CHANGE_UP,PERCENT_CHANGE_DOWN,AVERAGE_PERCENT_VOLUME.FREQUENCY:0,1,5,10,30,60(min;0= full day).
-
fields(optional): screener field indexes.
- ACCOUNT (account activity)
- Service:
ACCT_ACTIVITY(SUBS/UNSUBS). keys(req.): arbitrary identifier for your sub; if you send multiple, the first is used.fields(recommended):0(or0,1,2,3per example/need).
- Server responses (
StreamResponseCode)
- Types:
response(to your requests),notify(heartbeats),data(market flow).
| Code | Name | Meaning |
|---|---|---|
| 0 | SUCCESS |
Command succeeded |
| 3 | LOGIN_DENIED |
Invalid or expired token |
| 9 | UNKNOWN_FAILURE |
Unspecified server error |
| 11 | SERVICE_NOT_AVAILABLE |
Service unavailable for your entitlement |
| 12 | CLOSE_CONNECTION |
Only one stream per user allowed |
| 19 | REACHED_SYMBOL_LIMIT |
Too many symbols subscribed |
| 20 | STREAM_CONN_NOT_FOUND |
Stream connection not found |
| 21 | BAD_COMMAND_FORMAT |
Malformed command (check Auth format, keys) |
| 22 | FAILED_COMMAND_SUBS |
SUBS command failed |
| 23 | FAILED_COMMAND_UNSUBS |
UNSUBS command failed |
| 24 | FAILED_COMMAND_ADD |
ADD command failed |
| 25 | FAILED_COMMAND_VIEW |
VIEW command failed |
| 26 | SUCCEEDED_COMMAND_SUBS |
SUBS succeeded |
| 27 | SUCCEEDED_COMMAND_UNSUBS |
UNSUBS succeeded |
| 28 | SUCCEEDED_COMMAND_ADD |
ADD succeeded |
| 29 | SUCCEEDED_COMMAND_VIEW |
VIEW succeeded |
| 30 | STOP_STREAMING |
Server stopped streaming |
- Delivery Types
All Sequence: everything with sequence number.Change: only changed fields (conflated).Whole: full messages with throttling.
- Best practices
- Do
LOGINand wait forcode=0beforeSUBS/ADD. - To add symbols without losing existing ones, use
ADD(notSUBS). - Change
fieldswithVIEWfor performance. - Handle
notify(heartbeats) and reconnect if they are lost. - Reuse your
SchwabClientCorrelIdduring the session. - If you see
19(symbol limit), shard loads by service/session.
Enums Reference (enums.py)
All enums are StrEnum (or IntEnum) subclasses, so they can be passed directly where a str is expected. Import from the top-level package:
from schwab_sdk import ContractType, OrderType, StreamService
Market Data — Request Parameter Enums
ContractType
Option chain contract type filter.
| Value | Description |
|---|---|
CALL |
Call contracts only |
PUT |
Put contracts only |
ALL |
Both calls and puts |
Strategy
Option chain strategy.
| Value |
|---|
SINGLE, ANALYTICAL, COVERED, VERTICAL, CALENDAR, STRANGLE, STRADDLE, BUTTERFLY, CONDOR, DIAGONAL, COLLAR, ROLL |
StrikeRange
Option chain strike range filter.
| Value | API Code | Description |
|---|---|---|
IN_THE_MONEY |
ITM |
In the money |
NEAR_THE_MONEY |
NTM |
Near the money |
OUT_OF_THE_MONEY |
OTM |
Out of the money |
STRIKES_ABOVE_MARKET |
SAK |
Strikes above market |
STRIKES_BELOW_MARKET |
SBK |
Strikes below market |
STRIKES_NEAR_MARKET |
SNK |
Strikes near market |
ALL |
ALL |
All strikes |
ExpirationMonth
Option chain expiration month filter.
JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC, ALL
OptionType
Option type filter for chains.
| Value | Description |
|---|---|
STANDARD |
Standard contracts |
NON_STANDARD |
Non-standard (adjusted) contracts |
ALL |
Both |
Entitlement
Retail token entitlement level.
| Value | Description |
|---|---|
PN |
NonPayingPro |
NP |
NonPro |
PP |
PayingPro |
PeriodType
Price history period type.
day, month, year, ytd
FrequencyType
Price history frequency type.
minute, daily, weekly, monthly
QuoteField
Quote field groups for GET /quotes.
quote, fundamental, extended, reference, regular, all
MoverIndex
Index symbols for GET /movers.
$DJI, $COMPX, $SPX, NYSE, NASDAQ, OTCBB, INDEX_ALL, EQUITY_ALL, OPTION_ALL, OPTION_PUT, OPTION_CALL
MoverSort
Movers sort attribute.
VOLUME, TRADES, PERCENT_CHANGE_UP, PERCENT_CHANGE_DOWN, AVERAGE_PERCENT_VOLUME
MoverFrequency (IntEnum)
Movers frequency in minutes. 0 = real-time snapshot.
0, 1, 5, 10, 30, 60
MarketID
Market IDs for market hours endpoints.
equity, option, bond, future, forex
Projection
Instrument search projection type.
symbol-search, symbol-regex, desc-search, desc-regex, search, fundamental
Market Data — Response Enums
AssetMainType
Primary asset type returned in responses and streaming data.
BOND, EQUITY, ETF, EXTENDED, FOREX, FUTURE, FUTURE_OPTION, FUNDAMENTAL, INDEX, INDICATOR, MUTUAL_FUND, OPTION, UNKNOWN
AssetSubType
Asset sub-type in responses.
| Value | Description |
|---|---|
ADR |
American Depositary Receipt |
CEF |
Closed-End Fund |
COE |
Common Equity |
ETF |
Exchange-Traded Fund |
ETN |
Exchange-Traded Note |
GDR |
Global Depositary Receipt |
OEF |
Open-End Fund |
PRF |
Preferred Stock |
RGT |
Right |
UIT |
Unit Investment Trust |
WAR |
Warrant |
SecurityStatus
Security status in quote responses.
Normal, Unknown, Halted, Closed
Exchange
Exchange single-character codes (reference.exchange, streaming field 13).
| Value | Code | Exchange |
|---|---|---|
AMEX |
A |
NYSE American (AMEX) |
NASDAQ |
Q |
NASDAQ |
NYSE |
N |
NYSE |
NYSE_ARCA |
P |
NYSE Arca (Pacific) |
OPR |
o |
Options |
OTC_MARKETS |
9 |
Pink Sheets |
INDEX |
0 |
Index |
XCME |
@ |
CME Futures |
GFT |
T |
Forex |
MUTUAL_FUND |
3 |
Mutual Fund |
NASDAQ_OTCBB |
U |
OTCBB |
INDICATOR |
: |
Indicator (realtime) |
MICId
Market Identifier Codes (ISO 10383) in quote data (askMICId, bidMICId, lastMICId).
XNYS, XNAS, ARCX, XADF, MEMX, IEGX, EDGX, BATS, IEXG, EPRL
ExchangeName
Human-readable exchange names (reference.exchangeName).
NASDAQ, NYSE, NYSE Arca, OPR, OTC Markets, Index, XCME, GFT, Mutual Fund, Nasdaq OTCBB
OtcMarketTier
| Value | Code | Description |
|---|---|---|
OTCQX |
QX |
Best Market |
OTCQB |
QB |
Venture Market |
PINK_CURRENT |
PC |
Pink Current Information |
EXPERT_MARKET |
EM |
Expert Market |
Other Response Enums
| Enum | Values | Context |
|---|---|---|
QuoteType |
NBBO |
Quote type in equity/ETF quotes |
OptionContractChar |
C, P |
Contract type in option reference |
OptionSettlementType |
P (PM), A (AM) |
Settlement type |
OptionExpirationType |
S (Standard), W (Weekly), Q (Quarterly), M (Mini), R (Reduced Value) |
UV expiration type |
DividendFrequency |
0 (None), 1 (Annual), 2 (Semi-Annual), 4 (Quarterly), 12 (Monthly) |
divFreq in fundamentals |
FundStrategy |
A (Active), P (Passive) |
ETF fund strategy |
MoverDirection |
up, down |
Direction in movers response |
Orders — Enums
OrderType
Order type for placing and querying orders.
MARKET, LIMIT, STOP, STOP_LIMIT, TRAILING_STOP, CABINET, NON_MARKETABLE, MARKET_ON_CLOSE, EXERCISE, TRAILING_STOP_LIMIT, NET_DEBIT, NET_CREDIT, NET_ZERO, LIMIT_ON_CLOSE
Duration
Order duration / time-in-force.
| Value | Description |
|---|---|
DAY |
Day order |
GOOD_TILL_CANCEL |
GTC |
FILL_OR_KILL |
FOK |
IMMEDIATE_OR_CANCEL |
IOC |
END_OF_WEEK |
End of week |
END_OF_MONTH |
End of month |
NEXT_END_OF_MONTH |
Next end of month |
Session
Trading session for orders.
| Value | Description |
|---|---|
NORMAL |
Regular trading hours |
AM |
Pre-market |
PM |
After-hours |
SEAMLESS |
Extended hours (all sessions) |
OrderStatus
Order status values for filtering and responses.
AWAITING_PARENT_ORDER, AWAITING_CONDITION, AWAITING_STOP_CONDITION, AWAITING_MANUAL_REVIEW, ACCEPTED, AWAITING_UR_OUT, PENDING_ACTIVATION, QUEUED, WORKING, REJECTED, PENDING_CANCEL, CANCELED, PENDING_REPLACE, REPLACED, FILLED, EXPIRED, NEW, AWAITING_RELEASE_TIME, PENDING_ACKNOWLEDGEMENT, PENDING_RECALL, UNKNOWN
EquityInstruction
Instruction for equity order legs.
| Value | Description |
|---|---|
BUY |
Buy long |
SELL |
Sell long position |
SELL_SHORT |
Short sell |
BUY_TO_COVER |
Cover short position |
OptionInstruction
Instruction for option order legs.
| Value | Description |
|---|---|
BUY_TO_OPEN |
Open new long position |
SELL_TO_CLOSE |
Close existing long position |
SELL_TO_OPEN |
Open new short position |
BUY_TO_CLOSE |
Close existing short position |
AssetType
Asset type for order placement (instrument.assetType).
EQUITY, OPTION
OrderStrategyType
Order strategy type.
| Value | Description |
|---|---|
SINGLE |
Single order |
OCO |
One-cancels-other |
TRIGGER |
Triggered (bracket) order |
CANCEL |
Cancel order |
RECALL |
Recall order |
PAIR |
Pair trade |
FLATTEN |
Flatten position |
TWO_DAY_SWAP |
Two-day swap |
BLAST_ALL |
Blast all |
ComplexOrderStrategyType
Complex strategy type for multi-leg orders.
NONE, COVERED, VERTICAL, BACK_RATIO, CALENDAR, DIAGONAL, STRADDLE, STRANGLE, COLLAR_SYNTHETIC, BUTTERFLY, CONDOR, IRON_CONDOR, VERTICAL_ROLL, COLLAR_WITH_STOCK, DOUBLE_DIAGONAL, UNBALANCED_BUTTERFLY, UNBALANCED_VERTICAL_ROLL, UNBALANCED_CONDOR, CUSTOM
Other Order Enums
| Enum | Values | Context |
|---|---|---|
SpecialInstruction |
ALL_OR_NONE, DO_NOT_REDUCE, ALL_OR_NONE_DO_NOT_REDUCE |
Special order instructions |
RequestedDestination |
INET, ECN_ARCA, CBOE, AMEX, PHLX, ISE, BOX, NASDAQ, BATS, C2, AUTO |
Order routing |
PriceLinkBasis |
MANUAL, BASE, TRIGGER, LAST, BID, ASK, ASK_BID, MARK, AVERAGE |
Stop/limit price basis |
PriceLinkType |
VALUE, PERCENT, TICK |
Trailing stop type |
StopType |
STANDARD, BID, ASK, LAST, MARK |
Stop price type |
TaxLotMethod |
FIFO, LIFO, HIGH_COST, LOW_COST, AVERAGE_COST, SPECIFIC_LOT, LOSS_HARVESTER |
Tax lot selection |
OrderLegType |
EQUITY, OPTION, INDEX, MUTUAL_FUND, CASH_EQUIVALENT, FIXED_INCOME, CURRENCY |
Leg asset type |
PositionEffect |
OPENING, CLOSING, AUTOMATIC |
Option position effect |
QuantityType |
ALL_SHARES, DOLLARS, SHARES |
Quantity type |
DivCapGains |
REINVEST, PAYOUT |
Dividend reinvestment |
ActivityType |
EXECUTION, ORDER_ACTION |
Order activity type |
ExecutionType |
FILL |
Execution type |
AdvancedOrderType |
NONE |
Preview orders |
SettlementInstruction |
REGULAR |
Settlement type |
OrderValidationSeverity |
ACCEPT |
Validation severity |
CommissionFeeType |
COMMISSION |
Commission/fee type |
Accounts — Enums
AccountField
Additional fields for account queries.
| Value | Description |
|---|---|
POSITIONS |
"positions" — include positions in response |
TransactionType
Transaction types for filtering.
TRADE, RECEIVE_AND_DELIVER, DIVIDEND_OR_INTEREST, ACH_RECEIPT, ACH_DISBURSEMENT, CASH_RECEIPT, CASH_DISBURSEMENT, ELECTRONIC_FUND, WIRE_OUT, WIRE_IN, JOURNAL, MEMORANDUM, MARGIN_CALL, MONEY_MARKET, SMA_ADJUSTMENT
AccountInstrumentType
Instrument type in account position responses.
SWEEP_VEHICLE, CASH_EQUIVALENT, EQUITY, OPTION, MUTUAL_FUND, FIXED_INCOME, INDEX, CURRENCY, COLLECTIVE_INVESTMENT
Other Account Enums
| Enum | Values | Context |
|---|---|---|
TransactionStatus |
VALID |
Transaction status |
TransactionSubAccount |
CASH |
Sub-account type |
TransactionActivityType |
ACTIVITY_CORRECTION |
Transaction activity type |
UserType |
ADVISOR_USER |
User type in transactions |
Streaming — Enums
StreamService
WebSocket streaming service names.
ADMIN, LEVELONE_EQUITIES, LEVELONE_OPTIONS, LEVELONE_FUTURES, LEVELONE_FUTURES_OPTIONS, LEVELONE_FOREX, NASDAQ_BOOK, NYSE_BOOK, OPTIONS_BOOK, CHART_EQUITY, CHART_FUTURES, SCREENER_EQUITY, SCREENER_OPTION, ACCT_ACTIVITY
StreamCommand
WebSocket streaming commands.
| Value | Description |
|---|---|
LOGIN |
Authenticate the stream |
LOGOUT |
Close the stream session |
SUBS |
Subscribe (replaces existing keys) |
UNSUBS |
Unsubscribe keys |
ADD |
Add keys (appends to existing) |
VIEW |
Change fields for a service |
StreamResponseCode (IntEnum)
See the full response code table in the Streaming section above.
Advanced Troubleshooting
notify.code=12(Only one connection): close other active WebSocket sessions.response.content.code=3(Login denied): invalid/expired token →client.login().response.content.code=21(Bad command formatting): checkAuthorizationformat (withoutBearer) and keys (spacing for options, uppercase).- Persistent REST 401: delete
schwab_tokens.json(or clear your DB tokens if usingpersist=False) and re-runclient.login(). - High latency/lost frames: avoid parallel reconnects; use the SDK's auto-resubscription.
Contributions
Your contributions are welcome! Ideas, issues, and PRs help improve the SDK:
- Open an issue with clear details (environment, steps, expected/actual error).
- Propose endpoint coverage improvements and examples.
- Follow a clear style and add tests or minimal examples when possible.
If you want to hold working sessions or discuss the roadmap, open an issue labeled discussion.
Disclaimer
This project is unofficial and is not affiliated with, sponsored by, or endorsed by Charles Schwab & Co., Inc. "Schwab" and other trademarks are the property of their respective owners. Use of this SDK is subject to the terms and conditions of Schwab APIs and applicable regulations. Use at your own discretion and responsibility.
License
MIT (LICENSE)
Project details
Release history Release notifications | RSS feed
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 schwab_sdk_unofficial-0.3.2.tar.gz.
File metadata
- Download URL: schwab_sdk_unofficial-0.3.2.tar.gz
- Upload date:
- Size: 120.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f3e5fec9eb0e54ede566531f6ed80b507931943cc2dfe831f83343ee552cfa5f
|
|
| MD5 |
24c25660493314932fd3cc2fce0b14d9
|
|
| BLAKE2b-256 |
85bf44d3dec798550c238799762335a7474ed9788fdd25a66f3962866c2f4bf4
|
File details
Details for the file schwab_sdk_unofficial-0.3.2-py3-none-any.whl.
File metadata
- Download URL: schwab_sdk_unofficial-0.3.2-py3-none-any.whl
- Upload date:
- Size: 82.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2b196bc060aa02bf8e70f284f372e232751af88d9a4774a3c78905faad17be9c
|
|
| MD5 |
e251f919e504d5b18fb6122895d38be3
|
|
| BLAKE2b-256 |
1f446f8c3b51d4a48d2996ed8f98fd53a32f8c7256346998a1802e0714173eb3
|