stock_universe to build the symbol set, fetch the overview for covered rows, and deepen only the shortlist. The rate you run at is set by your plan’s per-minute limit.
Resolve coverage before you spend the batch
stock_universe {"coverage":"has_fundamentals"} returns the symbols eligible for a fundamentals fan-out without one resolution call per ticker. Page with next_offset; use the shape filter when a factor only applies to a statement family. Use stock_search only for user-entered names/tickers, and stock_reference {"catalog":"coverage"} for measured aggregate rates.
Concurrency follows the minute limit
Pick your in-flight count from the plan’s per-minute limit and leave headroom for retries.
Also mind the quota: Free is 100 requests per day, which a single 30-symbol watchlist refresh can exhaust once you add a resolve call and an overview call each. Read
X-Credits-Remaining after each authenticated tool call when it is present, and stop immediately on a quota_exhausted 429. See Plans and limits.
A bounded worker pool
Both versions cap in-flight requests and back off on a429.
- Python
- TypeScript
batch.py
reason separates a minute 429 from a quota 429; do not infer it from timing. For the full policy (attempt caps, total-wait budget), wrap each request with the loop in Errors and retries.
Watching for new data
Schema 2 successful200 responses include a composite ETag. Store the response and send the same request body later with If-None-Match. A 304 Not Modified has an empty body and means your cached response is still current for that exact tool, schema, params, fundamentals build, policy version, and market cursor.
For fundamentals, watch meta.as_of.sec, meta.published_at, meta.build.fundamentals, and meta.last_verified_at when present. For price-backed responses, also watch meta.as_of.market and the response freshness fields such as sessions_behind.
Use this polling shape for watchlists:
- Resolve the symbol once with
stock_search. - Fetch schema 2 data and persist the body plus
ETag. - Re-poll with the same body and
If-None-Match. - On
304, reuse your cached body. On200, replace it and store the newETag.
304s still pass authentication and rate limiting, but the quota credit is refunded at the API edge. Do not expect a response body on 304.
Keep SEC-backed tools out of a tight loop
stock_filings, stock_insider, and stock_ownership read SEC data and can be slow cold, so pull them only for the shortlist a user actually opens — in a background job, cached hard.
Where batches go wrong
- Fanning out
stock_overviewover symbols you never checked withstock_search, spending quota oningestion_pendingtickers. - Continuing to send after a
429instead of waiting. - Looping on
quota_exhaustedinstead of stopping until the window resets. - Rendering a refused field (
unavailableis present) as0. Carry the refusal token through.
Production patterns
Caching, key safety, and SEC loading states around the loop.