Short answer: The Schwab API in Python means Charles Schwab's Trader API for individual developers, reached in this guide through the unofficial schwab-py wrapper (1.5.1, 30 June 2025, MIT). You need a Schwab brokerage account and one approved app with an exact callback URL. The design constraint is the token: community documentation says access tokens last about 30 minutes and refresh tokens about seven days, so an unattended program needs a human login roughly once a week.
Schwab's portal could only be partly read for this guide, on 5 November 2026. Its public user guides and app form text were readable; the Trader API product documentation, API specification and terms were not. So each statement below says whose it is: Schwab's, or the community wrapper's. No Schwab account, app or key was used, and the tests ran on fake tokens and local mocks.
This is not a recommendation of Schwab, and automated trading can lose money.
Does Charles Schwab have an API?
Yes, Schwab's portal lists Trader API products for individuals and for commercial use, and a Schwab article dated 14 August 2026 describes what traders do with them.
| Item | Schwab's description |
|---|---|
| Purpose | Connects thinkorswim's market data, account information and functionality to third-party AI applications; listed uses include order entry for stocks, options and ETFs |
| Individual role | Access to your own Schwab brokerage accounts, which you must already hold, and a limit of one app |
| Commercial offering | Traders can request that an external application be integrated; not all requests are allowed |
| Login | OAuth 2, with authorization_code as the only available flow |
schwab-py is not Schwab's: it labels itself unofficial and says it is not endorsed by or affiliated with Schwab. Calling api.schwabapi.com without a token returned HTTP 401 on 5 November 2026, which shows only that the host answers.
Schwab API key: register, create one app, wait for approval
Registration is a manual sequence whose last step, approval, is outside your control, so start it before you need the API.
- Register on the portal and add the Individual Developer role to your profile.
- Request access to the Trader API product; Schwab's general guide says most requests are reviewed within two business days, sometimes longer.
- Create the app with its name, callback URL, API products and order limit.
- Wait for the status
Ready For Use. The wrapper's docs warn thatApproved - Pendingis not ready yet and that approval can take a few days. - Copy the app key and secret, which Schwab calls highly confidential, into a secret store: see API key security.
| Field | Schwab's portal | schwab-py docs |
|---|---|---|
| Callback URL | HTTPS per the OAuth guide, while the callback guide says the scheme varies by line of business; 255 characters at most (one guide says 256); several separated by a comma without a space. The URL sent in the flow must be identical to a registered one | https://127.0.0.1:8182 with no trailing slash; its login flow accepts only that host |
| Order limit | 0 to 120 order requests per account per minute, covering new orders, replacements and cancels; applied automatically | Suggests 120 |
| API product | Not readable | Suggests Accounts and Trading Production; also lists Market Data |
The portal's status list also includes App Registration Error; the wrapper's docs tie that to a console bug reported on 21 July 2024 and say to email Schwab's API team.
Schwab access token and refresh token: what is documented where
The 30-minute and seven-day figures could not be confirmed on a Schwab page read for this guide, so treat them as community-documented and read expires_in at run time.
| Question | Schwab's public guides | schwab-py docs and source |
|---|---|---|
| Access token life | Returned in seconds as expires_in; no figure given | About 30 minutes |
| Refresh token life | No figure in the guides read | Seven days, with no way to extend it; may end sooner or later, since Schwab enforces it |
| Refresh early? | Common and fine if not excessive; it avoids 401 errors near expiry | Automatic, with a 300-second leeway in the source |
| Full login again when | Refresh token compromised, different accounts needed, user revokes access or changes credentials or two-factor setup, policy changes | Token expired: delete the file and create a new one |
| Proactive age limit | Not mentioned | easy_client defaults to max_token_age of 561,600 seconds, 6.5 days |
A person must complete Schwab's consent page to start a new token, so no unattended renewal path appears in these documents. Do not script that login with stored credentials; check Schwab's terms first.
Tested: how schwab-py behaves as tokens age
A stale token fails late and as an exception rather than an HTTP status, so code that only checks status_code never sees it.
| Test | Result |
|---|---|
Token file 9 days old, client_from_token_file | No error; token_age() reports 9.0 days. Only easy_client checks age |
| Access token valid | One GET with the existing token |
| Access token expired, refresh accepted | POST with grant_type=refresh_token, then the GET with the new token; file rewritten, creation_timestamp unchanged |
| 200 seconds left on the access token | Refreshed anyway, because of the leeway |
| Refresh refused by the mock | authlib.integrations.base_client.errors.OAuthError raised from the GET; file unchanged |
| Token file in an older format | ValueError asking you to delete it |
The refusal was synthetic, because Schwab's real error body needs an expired real token. Importing schwab-py also printed an Authlib deprecation warning about httpx (seen on 1.9.4 and on 1.9.5, released that day), a sign to pin versions for a wrapper last released in June 2025.
A token-age monitor for unattended Schwab programs
Run a small check outside the trading process, so a token problem reaches a person before it stops the program. creation_timestamp marks the login and does not move when the access token refreshes.
import json, time
REFRESH_DAYS = 7 # community-documented, Schwab can end it sooner
WARN_DAYS = 5.5 # your choice: leave time for a human
def token_status(path, now=None):
"""Classify a schwab-py token file. Reads timestamps only, never prints secrets."""
now = now or time.time()
try:
with open(path, encoding="utf-8") as f:
wrapped = json.load(f)
days = (now - wrapped["creation_timestamp"]) / 86400
access_left = wrapped["token"]["expires_at"] - now
except (OSError, ValueError, KeyError, TypeError) as err:
return "UNREADABLE", type(err).__name__
if days >= REFRESH_DAYS:
return "RELOGIN_REQUIRED", f"{days:.1f} days old"
if days >= WARN_DAYS:
return "RELOGIN_SOON", f"{REFRESH_DAYS - days:.1f} days left"
return "OK", f"{days:.1f} days old, access token {access_left / 60:.0f} min"On synthetic files with a fixed clock:
new OK 0.0 days old, access token 30 min
midweek OK 3.0 days old, access token -5 min
late RELOGIN_SOON 1.2 days left
day7 RELOGIN_REQUIRED 7.0 days old
old RELOGIN_REQUIRED 9.0 days old
missing UNREADABLE FileNotFoundError
legacy UNREADABLE KeyErrorHypothetical schedule. Log in every Sunday before the token turns seven days old, as the wrapper's docs suggest for weekday trading. Last Sunday's token is 5 days old on Friday, so a 5.5-day warning stays quiet through the trading week and fires by Saturday as the reminder. The thresholds are examples, not Schwab figures.
- Run it daily from a scheduler and send
RELOGIN_SOONto a person. - On
RELOGIN_REQUIREDorOAuthError, block new orders and alert. Decide in advance how open orders get handled while you are locked out. - Never share a token file between machines or programs: the wrapper's docs warn that one program then locks the other out.
Schwab API Python tutorial: first requests
Account calls take an account hash rather than the number, so fetch the hashes first.
import json, time, httpx
from schwab import auth
from schwab.orders.equities import equity_buy_limit
now = int(time.time())
with open("token.json", "w", encoding="utf-8") as f: # synthetic token
json.dump({"creation_timestamp": now - 3600,
"token": {"access_token": "FAKE", "refresh_token": "FAKE",
"token_type": "Bearer", "expires_in": 1800,
"expires_at": now + 1500, "scope": "api"}}, f)
client = auth.client_from_token_file("token.json", "FAKE_KEY", "FAKE_SECRET")
def fake_schwab(request): # test harness: nothing leaves this machine
print(request.method, request.url.path)
return httpx.Response(200, json={})
client.session._transport = httpx.MockTransport(fake_schwab) # delete this line for real use
client.get_quote("TEST")
client.get_price_history_every_day("TEST")
client.get_account_numbers() # gives the account hashes
client.place_order("ACCOUNT_HASH", equity_buy_limit("TEST", 1, "10.00").build())
print(client.token_age() // 60, "minutes since login")Output on 5 November 2026, with nothing sent to Schwab:
GET /marketdata/v1/TEST/quotes
GET /marketdata/v1/pricehistory
GET /trader/v1/accounts/accountNumbers
POST /trader/v1/accounts/ACCOUNT_HASH/orders
60 minutes since loginReal tokens come from the login flow, schwab-generate-token.py or easy_client, none of which could be run here. The wrapper's docs say a successful place_order usually returns no JSON, with the order ID in the response for Utils.extract_order_id to read, and its client sets a 30-second timeout by default. Check status_code yourself, since each method returns an HTTPX response. The wrapper also covers option chains, movers, market hours and instruments, plus a streaming client for quotes, charts, order book and account activity.
Schwab paper trading API and thinkorswim API
No public Schwab page read here confirms a paper endpoint for the individual Trader API, and schwab-py's README (opens in a new tab) says "Paper trading is not supported", so plan for none unless your own portal account shows one.
- Sandbox. The portal's general guide describes Sandbox apps with engineered test data and says each line of business sets its own rules. It does not say which applies to the Trader API. The wrapper's docs suggest a production product and do not mention a sandbox.
- paperMoney. Schwab calls it thinkorswim's virtual trading environment. Nothing read says API orders can reach it.
- thinkorswim API. No separate one was found; Schwab describes the Trader API as the connection to thinkorswim's data and functionality, while the wrapper's README calls the API unaffiliated with thinkorswim, trading the same accounts with only part of its functionality. Per the wrapper's docs, old TD Ameritrade apps are not valid, and saved orders and watchlists are gone.
Until your own portal account shows a test environment, exercise order code on a mock as above, and treat the first real order as a live action you approve by hand. How to structure those tests is in how to test trading bots.
Personal account use against software for others
The individual product covers your own accounts, while serving other people is a different product and a legal question.
The portal's sitemap lists Trader API terms (opens in a new tab), which could not be read for this guide, so nothing here paraphrases them; read them before building, especially on showing market data to others, covered in stock market data API. schwab-py's own guide says its login flows are not for apps you distribute or run on a server for other users. Use the API rather than scraping Schwab's site. Order state and reconciliation are in broker API integration.
Rules depend on your country, and account types and API access vary; no broker is recommended or ranked here. For readers in Turkey, the Capital Markets Board (SPK) said on 23 February 2023 that residents can trade leveraged forex only through institutions it has authorized, and Law No. 6362 provides access blocks for online offers from abroad (Article 99(4)) and criminal penalties for unauthorized activity (Article 109(2)).
Fincode quotes integration work through exchange API integration and claims no prior Schwab work.
Frequently asked questions
Does Charles Schwab have an API?
Yes. Its developer portal lists Trader API products for individuals and for commercial use. Individuals need a Schwab brokerage account and may create one app.
Is schwab-py an official Schwab library?
No. It describes itself as an unofficial wrapper, not endorsed by or affiliated with Charles Schwab. Version 1.5.1 was released on 30 June 2025 under the MIT license.
How long does a Schwab refresh token last?
schwab-py's documentation says seven days and that Schwab enforces it, so it may end sooner or later. The Schwab guides readable for this page give no figure.
Can I avoid the weekly Schwab login?
schwab-py's documentation says there is currently no way to make a refresh token last longer than seven days. Schedule the login and alert on token age.
Does Schwab have a paper trading API?
No Schwab page read here confirms one, and schwab-py's README says paper trading is not supported. The portal's general guide describes Sandbox apps set per line of business, and nothing public read here ties one to the individual Trader API.
Is there a thinkorswim API?
No separate one was found. A Schwab article dated 14 August 2026 describes the Trader API as connecting thinkorswim's market data, account information and functionality to third-party AI applications; schwab-py's README calls the API unaffiliated with thinkorswim.
Sources
- Charles Schwab: 4 Ways AI Trading Tools Help Increase Efficiency (14 August 2026) (opens in a new tab)
- Schwab Developer Portal: Authenticate with OAuth (opens in a new tab)
- Schwab Developer Portal: OAuth Restart vs. Refresh Token (opens in a new tab)
- Schwab Developer Portal: App Callback URL Requirements and Limitations (opens in a new tab)
- Schwab Developer Portal: About the Individual Developer Role (opens in a new tab)
- Schwab Developer Portal: Requesting Product Access (opens in a new tab)
- Schwab Developer Portal: Test in Sandbox (opens in a new tab)
- schwab-py documentation: Getting Started (community) (opens in a new tab)
- schwab-py documentation: Authentication and Client Creation (community) (opens in a new tab)
- schwab-py documentation: HTTP Client (community) (opens in a new tab)
- schwab-py documentation: Transitioning from TDAmeritrade (community) (opens in a new tab)
- PyPI: schwab-py 1.5.1 (unofficial, MIT) (opens in a new tab)
- SPK (Capital Markets Board): Notice on unauthorized leveraged transactions, 23 February 2023 (Turkish) (opens in a new tab)
- Capital Markets Law No. 6362, consolidated text, Articles 99 and 109 (Turkish) (opens in a new tab)
This guide is general information about software and markets, not investment, legal or tax advice. Automated trading can lose money, past or backtested performance does not guarantee future results, and the rules that apply depend on your jurisdiction and your exchange's terms. Sources, checks and corrections: see the editorial policy.