Article
Schwab API Rate Limits and Token Expiry: What Actually Stops Your Requests
The Schwab Trader API's real constraints: roughly 120 requests a minute, HTTP 429 with two distinct codes, a 30-minute access token, and a hard 7-day refresh token that cannot be extended.
Most people searching for Schwab API rate limits are about to hit the wrong wall. The per-minute throttle is real, and it is generous enough that a normal charting or journalling workload never reaches it. The constraint that actually breaks Schwab integrations is the refresh token, which expires seven days after it was created and cannot be renewed. This guide covers both, and is explicit about which numbers Schwab publishes and which are observed. Independent educational guide, not affiliated with or endorsed by Charles Schwab.
Schwab does not publish a rate-limit page
This is the first thing worth saying plainly, because it explains why the answers you find vary. Schwab's developer documentation sits behind an authenticated portal and does not expose a public, canonical limits page the way many API providers do. The figures below are the ones consistently reported by maintained client libraries and by developers who have hit the ceilings in production.
Treat them as reliable working values rather than contractual guarantees. Build so that being wrong about the exact number costs you a retry, not a broken integration.
The per-minute ceiling, and why you probably will not reach it
The figure that circulates, and that client libraries pace themselves against, is roughly 120 requests per minute. Order submission is throttled separately and much lower, in the region of 2 to 4 requests per second, and Schwab exposes a per-account order throttle that can be configured anywhere from zero up to 120 per minute.
For context on what that means in practice: a charting or review workload pulling price history for a watchlist of fifty symbols, once, uses fifty requests. You would have to be polling aggressively, in a loop, across many symbols, to approach the ceiling. Streaming exists precisely so that live quotes do not have to be polled.
- Roughly 120 requests per minute for general data endpoints.
- Around 2 to 4 per second for order submission, throttled separately.
- A per-account order throttle configurable from 0 to 120 per minute.
- Level I and Level II streaming quotes for equities, options and futures, so live prices do not need polling. Historical bars are equities and ETFs only.
HTTP 429 has two meanings, and they want different responses
When you exceed a limit you get an HTTP 429, and the sub-code tells you which limit you crossed. 429-001 is the sustained rate: you have been asking for too much over the window. 429-005 is a burst: you fired too many requests too close together, even though your average would have been fine.
They call for different fixes. A sustained-rate rejection means your workload is genuinely too large and needs batching, caching, or streaming. A burst rejection usually means your code has no spacing between calls, and adding a small delay between requests in a loop resolves it without reducing what you fetch.
In both cases the practical advice is the same on the retry: back off for a full 60 seconds rather than retrying immediately. Retrying into a 429 is how a brief throttle becomes a sustained one.
The limit that actually breaks integrations: the 7-day refresh token
Schwab issues two tokens. The access token, which authorises your requests, is valid for thirty minutes; requests carrying one older than that are rejected. That part is routine, and every client library refreshes it for you.
The refresh token is the problem. It is valid for seven days from the moment it was created, and that clock cannot be reset. Refreshing your access token does not extend it. Once the seven days are up, requests for a new access token are rejected with an invalid_client error, and the only remedy is to complete the browser-based login again and generate a completely new token.
This is why so many Schwab integrations work perfectly and then stop, always about a week after they were set up, with an authentication error that looks like a credentials problem and is not. If your integration died after roughly seven days, this is almost certainly why, and no amount of checking your app key will find it.
- Access token: 30 minutes. Refreshed automatically by any competent client.
- Refresh token: 7 days from creation. Hard expiry, cannot be extended.
- Symptom: invalid_client on the token request, about a week after setup.
- Remedy: a fresh interactive login. There is no programmatic way around it.
Designing around both limits
The rate limit is an engineering problem with ordinary answers: cache what does not change, batch what can be batched, stream what needs to be live, and put a small delay between calls in any loop. None of that is Schwab-specific.
The token expiry is a product problem, and it deserves to be treated as one. Any tool built on the Schwab API needs to tell the user plainly that a re-authentication is due, before the integration fails rather than after. An integration that silently stops at day seven and reports invalid_client is a support ticket; one that says "your Schwab connection expires in two days, sign in again" is a routine task.
If you are weighing Schwab against other brokers on these grounds, Schwab Trader API Cost vs IBKR and Alpaca: What Retail Technical Traders Actually Pay covers the cost side and IBKR vs Schwab Trader API for Technical Traders compares the two directly. For what Schwab expects of an application at review time, see Schwab Trader API Commercial Approval: What the Review Evaluates.
FAQ
What is the Schwab API rate limit?
The figure consistently reported and used by client libraries is roughly 120 requests per minute for general data endpoints, with order submission throttled separately at around 2 to 4 per second. Schwab does not publish a public canonical limits page, so treat these as reliable working values rather than guarantees.
What does HTTP 429-005 mean on the Schwab API?
429-005 is a burst rejection: too many requests too close together, even if your average rate would have been acceptable. 429-001 is the sustained rate limit. A burst rejection is usually fixed by spacing calls inside a loop; a sustained one means the workload itself needs batching, caching or streaming.
Why does my Schwab API integration stop working after a week?
The refresh token expires seven days after it was created and cannot be extended, so refreshing your access token does not help. Requests then fail with invalid_client. The only remedy is to complete the browser login again and generate a new token. This is the single most common cause of a Schwab integration that worked and then stopped.
How long does a Schwab access token last?
Thirty minutes. Requests carrying an access token older than that are rejected. Any maintained client library refreshes it automatically, so this is rarely the thing that breaks; the seven-day refresh token is.
Sample Structured Chart-Data Exports
Review how chart drawings, annotations, OHLC, volume, and execution context become reusable structured data.

