Check coverage before you spend the batch
1,125 of the 5,628 covered symbols are foreign private issuers that file 20-F/40-F/6-K, so/v2/filings and /v2/insider return unsupported for them; ETFs return unsupported for earnings and other stock-only families. Resolve those gaps with one cheap /v2/coverage call per symbol before you fan out the expensive routes (see coverage and gaps for the full universe). Dropping a symbol you know cannot answer is the cheapest request you never make.
For a 2–12 symbol comparison, do not hand-roll a batch at all. /v2/compare?symbols=AAPL,MSFT,NVDA does it in one call and returns fields_omitted_by_symbol for anything a symbol cannot supply.
Concurrency follows the minute limit
Pick your in-flight count from the plan’s per-minute limit, then leave headroom, because bursts and retries eat into it. The plans and limits page owns the exact numbers; the mapping for batches:| Plan | Minute limit | In-flight |
|---|---|---|
| Free | 30/min | 1–2 |
| Starter | 60/min | 2–3 |
| Builder | 300/min | 5 or more |
/v2/search, /v2/coverage, /v2/snapshot, and /v2/valuation are reachable. Fanning fundamentals, filings, or any other route over a Free key returns 403 PLAN_UPGRADE_REQUIRED per request, before quota ever matters. Then mind the daily quota: 100 requests a day disappears in one watchlist refresh of 30–40 symbols once you add a coverage call and a snapshot call each. Free batch work needs caching or a small list, and there is no header that counts the daily budget down: the X-RateLimit-* headers track the minute bucket, so they look healthy right up until the quota 429 lands. Count your own daily requests. A quota 429 is recognizable by its size: Retry-After jumps from seconds to hours (the time until the window resets). The quota math lives on plans and limits.
A bounded worker pool keeps you at that in-flight count. Both versions below read X-RateLimit-Remaining to slow down early and honor Retry-After on a 429 instead of hammering through it.
- Python
- TypeScript
batch.py
retryable: true), wrap each request with that loop. The minimal Retry-After handling above keeps a batch from drowning, but it is not the complete policy.
SEC routes do not belong in a tight loop
Filing and insider routes read from EDGAR and can be slow cold, so they are the wrong thing to fan out across a list. Run them in a background job, cache filing metadata and section text hard (it does not change), and only pull them for the shortlist a user actually opened. The same applies to/v2/insider, which scans the latest 200 Form 4 filings per symbol.
Where batches go wrong
- Calling
fundamentals,filings, andinsiderfor every watchlist row instead of snapshot-first, then shortlisting. - Treating
PLAN_UPGRADE_REQUIREDas retryable and looping on it mid-batch. - Continuing to send after a 429 instead of waiting
Retry-After. - Retrying
unsupportedETF or foreign-issuer states; they are stable200s, not failures. - Making every missing field look like a zero. Carry the
reasonthrough.
Production patterns
Caching, key safety, and SEC loading states around the loop.
Compare basket
The one-call scorecard for 2–12 symbols.