Batch workflows
Run multi-symbol workflows without a bulk endpoint or quota burn.
Short answer: there is no bulk endpoint today. For batches, coverage-filter first, limit concurrency, call snapshot first, and deepen only shortlisted symbols.
Use this when
Use this for jobs like:
- Refreshing a watchlist.
- Scoring a list of user-entered tickers.
- Building a nightly cache for a small symbol set.
- Letting an agent analyze several names without calling every tool.
Do not use this for
- Market-wide screeners. noxstock is not a full cross-symbol database today.
- Fetching every endpoint for every symbol.
- SEC fan-out over many heavy filers without background jobs and cache.
- Ignoring rate limits because calls are read-only.
Route plan
REST batch plan:
# 1. Resolve names only when needed.
GET /v2/search?query=<name>
# 2. Coverage-filter the candidate symbols.
GET /v2/coverage?symbol=<symbol>
# 3. Fetch compact first-pass context for supported symbols.
GET /v2/snapshot?symbol=<symbol>
# 4. Deepen only shortlisted symbols.
GET /v2/valuation?symbol=<symbol>
GET /v2/fundamentals?symbol=<symbol>&period=quarter
GET /v2/technicals?symbol=<symbol>
GET /v2/price-action?symbol=<symbol>
GET /v2/insider?symbol=<symbol>&limit=10For a 2–4 symbol user comparison, do not build a manual batch. Use:
GET /v2/compare?symbols=AAPL,MSFT,NVDAFor more than 4 symbols, run your own loop with concurrency control and product-side cache.
MCP sequence
MCP has no bulk tool either. Keep loops narrow:
# Per candidate:
noxstock_coverage({ "symbol": "<symbol>" })
noxstock_snapshot({ "symbol": "<symbol>" })
# Only for shortlisted symbols:
noxstock_valuation({ "symbol": "<symbol>" })
noxstock_technicals({ "symbol": "<symbol>" })
noxstock_price_action({ "symbol": "<symbol>" })For a small basket:
noxstock_compare({ "symbols": ["AAPL", "MSFT", "NVDA"] })What to preserve in the answer
For each symbol, preserve:
- Coverage result:
asset_type, endpoint support, and unsupported reasons. - Snapshot
freshness,as_of,market_status, andasset_type. - Missing or unavailable states for any deep route.
- Rate-limit response headers and retryable errors at the job level.
- Calls skipped because coverage or shortlisting made them unnecessary.
For compare batches, preserve fields_requested and fields_omitted_by_symbol.
Concurrency and retry pattern
Start conservative:
- Normalize symbols and remove duplicates.
- Run coverage checks with low concurrency.
- Stop unsupported symbols before paid/deep calls.
- Fetch snapshots next.
- Shortlist by the facts your product needs.
- Call deeper routes only for the shortlist.
- On
RATE_LIMITED, stop new work for that key and waitRetry-Afterwhen present. - Resume with lower concurrency.
Plan limits matter:
| Plan | Minute limit | Batch implication |
|---|---|---|
| Free | 30/min | Keep batches small; only search, coverage, snapshot, valuation are available. |
| Starter | 60/min | Limit concurrency and cache watchlists. |
| Builder | 300/min | Still cache and avoid SEC fan-out. |
A practical starting point is 2–5 in-flight requests per key, then tune down if X-RateLimit-Remaining falls faster than expected.
SEC routes in batch jobs
SEC-backed routes can be slow cold:
GET /v2/filings?symbol=<symbol>&form=10-K,10-Q&limit=5
GET /v2/filings/{accession}
GET /v2/filings/{accession}/section/risk_factors
GET /v2/insider?symbol=<symbol>&limit=10Use background jobs, per-symbol loading states, and long-lived cache for filing metadata/sections. Do not fan out section text calls across a large list unless you control concurrency and have a clear user need.
Common mistakes
- Calling
/v2/fundamentals,/v2/filings, and/v2/insiderfor every watchlist row. - Treating
PLAN_UPGRADE_REQUIREDas retryable during batch runs. - Continuing to send requests after
RATE_LIMITED. - Retrying
unsupportedETF states. - Dropping unavailable reasons and making all missing fields look like zero.