# noxstock agent context
noxstock is a read-only US-listed stock and ETF context API over REST and hosted MCP. REST routes are authenticated GET /v2/* calls. Hosted MCP is at https://api.noxstock.com/mcp and uses X-API-Key.
Agent rules: preserve envelopes, freshness, as_of, market_status, units, signs, unavailable states, and calls/tools used. Treat freshness: "unsupported" as a fact. Do not invent ETF holdings, ownership/13F, consensus, price targets, transcripts, news, options, execution, or investment advice. Retry only when error.retryable is true.
# Core concepts (/docs/concepts)
noxstock is deliberately small: authenticated `GET /v2/*` routes, one response envelope, explicit freshness, and fields with documented units. A client that respects four contracts can handle every current route without per-endpoint special cases. Each contract has one owner page that states it in full.
Success is exactly `{ "data": ... }`; failure is exactly `{ "error": { "code", "message", "retryable" } }`. Never parse a raw object at the top level.
`freshness`, `as_of`, and `market_status` tell you how current a response is before you show or cache it.
`_usd` is whole U.S. dollars, `_pct` is a percentage rather than a decimal, and signs are meaningful.
5,119 US-listed symbols, plus which data families do not apply to foreign filers and ETFs.
Two design choices follow from those contracts and surprise people often enough to call out here.
## Unsupported is not an error [#unsupported-is-not-an-error]
When a route does not apply to a symbol or asset type, you get HTTP 200 with a normal `data` envelope and `freshness: "unsupported"`, not an error. For example, `/v2/earnings` on an ETF returns 200 with an `unsupported` reason rather than a `4xx`. Branch on `freshness` before you branch on HTTP status.
## Nulls and omissions are intentional [#nulls-and-omissions-are-intentional]
noxstock does not fill unknowns with fake precision. A field that cannot be sourced for a given symbol does not become a guessed number. Instead, structured fields that go missing come back as a small envelope of their own:
```json title="GET /v2/snapshot?symbol=SPY (one field, trimmed)"
{
"pe_ttm": {
"available": false,
"reason": "etf_no_earnings"
}
}
```
That shape is the live SPY snapshot's `fundamentals_quick.pe_ttm`; an ETF has no earnings, so the multiple carries a machine-readable `reason` instead of a value. The same pattern guards financial-company metrics, where cash-flow and EV ratios mislead: snapshot suppresses a bank's FCF yield with `reason: "fcf_yield_suppressed_for_financials"`, while valuation and fundamentals suppress EV multiples, FCF yield, and interest coverage with `reason: "financial_company_metric_not_meaningful"`. Branch on the envelope shape, not on a specific reason string — reasons are endpoint-specific.
Asset type also decides which routes carry data at all. ETFs answer market-data routes like `/v2/snapshot`, `/v2/technicals`, `/v2/price-action`, and `/v2/history`, and return `unsupported` for stock-only routes like `/v2/earnings`, `/v2/fundamentals`, and `/v2/insider`. ETF profiles return fund facts; holdings are not part of v2.
## REST and MCP share one source of truth [#rest-and-mcp-share-one-source-of-truth]
MCP tools are read-only REST passthroughs. A successful tool call returns the REST `data` envelope unchanged; a failure renders as `[CODE] message (retryable: true|false)`. When the REST/OpenAPI contract and an MCP display disagree, the REST contract wins.
# noxstock docs (/docs)
noxstock is a read-only API for compact US-listed stock and ETF context. The same data is available over REST and over a hosted MCP server, and every response is JSON shaped to be parsed by an app or an agent without endpoint-specific guessing.
Reach for it when you need a fast answer to: what is this symbol, how is it trading, how expensive is it, what do its filings or insiders say, and how fresh is that data.
## Start here [#start-here]
Pick the surface that matches how you call APIs. Both authenticate with the same `nxs_` key in the `X-API-Key` header.
Create a key, call `GET /v2/snapshot?symbol=AAPL`, and read the response. Five minutes from zero to a parsed snapshot.
Point an MCP client at `https://api.noxstock.com/mcp` and call the read-only `noxstock_*` tools that wrap the same routes.
## Coverage [#coverage]
5,119 US-listed symbols on NYSE and NASDAQ: 5,062 stocks and 57 ETFs. Every stock resolves to an SEC filer; daily price history runs back up to 5 years. Some data families do not apply to every symbol, so check [coverage and gaps](/docs/reference/coverage-and-gaps) before you commit to a route.
## What to call for what [#what-to-call-for-what]
Start narrow with `/v2/search`, `/v2/coverage`, and `/v2/snapshot`, then add depth only when the question needs it. Routes beyond the four Free ones need Starter or Builder. The [endpoint guide](/docs/guides/choose-endpoints) maps jobs to the smallest route set.
| Need | Routes |
| -------------------------- | ------------------------------------------------------------------------------------------------- |
| Discovery and events | `/v2/search`, `/v2/coverage`, `/v2/calendar` |
| First-pass context | `/v2/snapshot`, `/v2/profile` |
| Valuation and fundamentals | `/v2/valuation`, `/v2/fundamentals`, `/v2/earnings`, `/v2/dividends` |
| Price behavior | `/v2/technicals`, `/v2/price-action`, `/v2/history`, `/v2/compare` |
| SEC and insider context | `/v2/filings`, `/v2/filings/{accession}`, `/v2/filings/{accession}/section/{name}`, `/v2/insider` |
Free keys can call `/v2/search`, `/v2/coverage`, `/v2/snapshot`, and `/v2/valuation`. Starter and Builder reach every route. See [plans and limits](/docs/reference/plans-and-limits) for rate limits and quotas.
## What it is not [#what-it-is-not]
noxstock does not trade or execute, and it is not:
* a real-time exchange-grade market data feed
* options, L2/order-book, or global-equities coverage
* analyst consensus, price targets, transcripts, news, or ownership/13F data
* a screener or full cross-symbol database
* investment advice
## Next step [#next-step]
New to the API? Start with the [REST quickstart](/docs/quickstart). If you already know the shape of the data, the [API reference](/docs/api-reference/overview) lists every route, param, and field.
# Quickstart (/docs/quickstart)
Make your first REST call from a server. By the end you have a key in your environment, a working `GET /v2/snapshot?symbol=AAPL`, and code that reads both the success and error envelopes.
### Create an API key [#create-an-api-key]
Open the keys page at `https://noxstock.com/dashboard/keys`, create a key, and copy it before you close the dialog. The raw secret is shown once. Keys start with `nxs_` and travel in the `X-API-Key` header. New keys start on the Free plan — `search`, `coverage`, `snapshot`, and `valuation`; see [plans and limits](/docs/reference/plans-and-limits) for the rest.
Keep noxstock keys server-side only. Never put them in frontend code, browser bundles, public repos, logs, screenshots, support chats, or prompts.
### Set your environment [#set-your-environment]
```bash
export NOXSTOCK_API_BASE_URL="https://api.noxstock.com"
export NOXSTOCK_API_KEY=""
```
### Make the first call [#make-the-first-call]
`/v2/snapshot` is the best first call for most apps: it returns quote, performance, 52-week range, quick valuation, sector, and industry in one response.
```bash
curl -sS "$NOXSTOCK_API_BASE_URL/v2/snapshot?symbol=AAPL" \
-H "X-API-Key: $NOXSTOCK_API_KEY"
```
### Read the success envelope [#read-the-success-envelope]
Success is always wrapped in `data`. The response below is trimmed; the live payload also carries a full `performance` block, a `range_52w` block, and roughly a dozen more `fundamentals_quick` fields. The [response model](/docs/reference/response-model) documents the wrapper in full.
```json title="GET /v2/snapshot?symbol=AAPL (trimmed)"
{
"data": {
"symbol": "AAPL",
"name": "Apple Inc",
"currency": "USD",
"as_of": "2026-06-05T14:19:06Z",
"freshness": "intraday",
"market_status": "open",
"cache_age_seconds": 0,
"quote": {
"price": 314.86,
"change_abs": 3.63,
"change_pct": 1.17,
"previous_close": 311.23,
"volume": 235657
},
"asset_type": "stock",
"fundamentals_quick": {
"market_cap_usd": 4569211202200,
"pe_ttm": 37.28,
"fcf_yield_pct": 2.83,
"dividend_yield_pct": 0.34
},
"sector": "Technology",
"industry": "Consumer Electronics"
}
}
```
Read these before you show or cache the result:
* `data.symbol` is the resolved ticker.
* `data.freshness` is one of `intraday`, `end_of_day`, `stale`, `degraded`, or `unsupported`. See [freshness](/docs/reference/freshness).
* `data.as_of` is the upstream timestamp when available, otherwise generation time, always UTC ISO-8601.
* `cache_age_seconds` is how long the served data has been cached; `0` means freshly computed.
`_usd` fields are whole U.S. dollars and `_pct` fields are percentages, not decimals; [units and fields](/docs/reference/units-and-fields) covers every convention.
### Handle the error envelope [#handle-the-error-envelope]
Failures are always wrapped in `error`. Retry only when `error.retryable` is `true`.
```json
{
"error": {
"code": "RATE_LIMITED",
"message": "Rate limit exceeded. Please slow request rate and retry.",
"retryable": true
}
}
```
The four errors you hit first all map to a distinct HTTP status, and none of them are retryable. Fix the cause before you call again:
| Code | HTTP | Fix |
| ----------------------- | ---- | ------------------------------------- |
| `UNAUTHORIZED` | 401 | Set a valid key in `X-API-Key`. |
| `PLAN_UPGRADE_REQUIRED` | 403 | The route needs Starter or Builder. |
| `SYMBOL_INVALID` | 400 | Symbol must match the ticker grammar. |
| `SYMBOL_UNKNOWN` | 404 | Resolve the ticker with `/v2/search`. |
Successful responses carry `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset`; a `429` carries them too, plus `Retry-After` in seconds. Other error responses do not include them, so read your budget from successes. The full table and retry policy live on the [error codes](/docs/reference/error-codes) page.
## Optional fit checks [#optional-fit-checks]
Run these before you store a user-entered symbol or kick off batch work.
```bash
# Resolve a company name to symbols.
curl -sS "$NOXSTOCK_API_BASE_URL/v2/search?query=apple" \
-H "X-API-Key: $NOXSTOCK_API_KEY"
# Confirm the symbol is supported and see its asset type.
curl -sS "$NOXSTOCK_API_BASE_URL/v2/coverage?symbol=AAPL" \
-H "X-API-Key: $NOXSTOCK_API_KEY"
```
## Call it from code [#call-it-from-code]
Both clients check the envelope before the HTTP status, because a `4xx`/`5xx` still carries a structured `error`. Keep the key server-side; never ship it to the browser.
```python title="snapshot.py"
import os
import httpx
base_url = os.environ.get("NOXSTOCK_API_BASE_URL", "https://api.noxstock.com")
api_key = os.environ["NOXSTOCK_API_KEY"]
with httpx.Client(
base_url=base_url,
headers={"X-API-Key": api_key},
timeout=10.0,
) as client:
response = client.get("/v2/snapshot", params={"symbol": "AAPL"})
body = response.json()
if "error" in body:
error = body["error"]
raise RuntimeError(
f"{error['code']}: {error['message']} (retryable={error['retryable']})"
)
response.raise_for_status()
data = body["data"]
print(data["symbol"], data["freshness"], data["as_of"], data["quote"]["price"])
```
```ts title="snapshot.ts"
type NoxstockEnvelope =
| { data: T }
| { error: { code: string; message: string; retryable: boolean } };
type Snapshot = {
symbol: string;
freshness: string;
as_of: string;
quote?: { price?: number };
};
const baseUrl = process.env.NOXSTOCK_API_BASE_URL ?? "https://api.noxstock.com";
const apiKey = process.env.NOXSTOCK_API_KEY;
if (!apiKey) {
throw new Error("Missing NOXSTOCK_API_KEY");
}
async function noxstockGet(path: string, params: Record) {
const url = new URL(path, baseUrl);
url.search = new URLSearchParams(params).toString();
const response = await fetch(url, {
headers: { "X-API-Key": apiKey },
signal: AbortSignal.timeout(10_000),
cache: "no-store",
});
const body = (await response.json()) as NoxstockEnvelope;
if ("error" in body) {
const error = new Error(`${body.error.code}: ${body.error.message}`) as Error & {
retryable?: boolean;
};
error.retryable = body.error.retryable;
throw error;
}
if (!response.ok) {
throw new Error(`HTTP ${response.status} without a noxstock error envelope`);
}
return body.data;
}
async function main() {
const snapshot = await noxstockGet("/v2/snapshot", { symbol: "AAPL" });
console.log(snapshot.symbol, snapshot.freshness, snapshot.as_of, snapshot.quote?.price);
}
main();
```
## Next steps [#next-steps]
Most apps call `GET /v2/valuation?symbol=AAPL` next; it pairs with snapshot and is available on Free keys. If your question is not valuation, the [endpoint guide](/docs/guides/choose-endpoints) maps jobs to routes.
Pick the smallest route set for your job.
Build retry behavior around `error.retryable`.
Connect MCP if you are building agents.
# Agent instructions (/docs/agents/instructions)
Paste this block into the system or developer prompt of any agent that can reach noxstock over REST or MCP. It is the one place the operating rules live; recipes and the skill file point back here. Set `NOXSTOCK_API_KEY` in the agent's environment first ([create a key](https://noxstock.com/dashboard/keys)).
```text title="noxstock-system-prompt.txt"
You use noxstock for US-listed stock and ETF context. It covers NYSE and NASDAQ
listings only: no options, futures, crypto, FX, or non-US equities.
Connection
- Hosted MCP: connect to https://api.noxstock.com/mcp and send the header
X-API-Key: $NOXSTOCK_API_KEY. Confirm the noxstock_* tools loaded, then
smoke-test noxstock_snapshot with symbol "AAPL". MCP is read-only and calls REST.
- REST: base URL https://api.noxstock.com, same X-API-Key header on every request.
When MCP and REST disagree, REST/OpenAPI is the source of truth.
Envelope
- Success is {"data": ...}. Failure is {"error": {"code", "message", "retryable"}}.
- A 200 with freshness "unsupported" plus a machine-readable reason is a real
answer, not an error. Report it as "not available from noxstock", do not retry it.
Default call sequence
- If the user names a company or fund, call search first to resolve the symbol.
- Call coverage before you store, batch, or deeply analyze a symbol.
- Call snapshot for most single-symbol questions, then add only what the question
needs: valuation, fundamentals, compare (2-4 tickers), filings list -> filing ->
filing section for SEC text, insider for Form 4 activity, technicals + price-action
for trend and timing, history only when you need a time series.
Plan access
- A Free key calls search, coverage, snapshot, and valuation only. Anything else
returns PLAN_UPGRADE_REQUIRED (403, not retryable).
- Starter and Builder keys reach every route and tool.
Output rules
- Answer first. Then show the few facts that support it, then freshness, then the
calls you made.
- Preserve every value as returned: units, signs, percentages, fiscal periods,
accessions, filing dates, as_of timestamps, freshness, market_status, and any
notices or null/unavailable reasons. Never round away a sign or invent precision.
- Never invent data noxstock does not return. That includes ETF holdings, ownership
or 13F positions, analyst consensus or price targets, earnings-call transcripts,
options, news, and non-US equities. If a field is missing, say
"not available from noxstock" and stop.
- Summarize SEC risk, MD&A, business, or press-release content only from the filing
section text noxstock returns. Do not blend in news, consensus, the web, or memory.
- Report data; do not advise. No buy/sell/hold verdict, price target, or trade.
Retry rule
- Retry only when error.retryable is true, and honor Retry-After when present.
Do not retry UNAUTHORIZED, PLAN_UPGRADE_REQUIRED, SYMBOL_UNKNOWN, SYMBOL_INVALID,
PARAM_INVALID, or an unsupported data state without changing the request.
```
Two facts above are owned elsewhere and worth following: the full [error codes and retry policy](/docs/reference/error-codes), and which [plan reaches which route](/docs/reference/plans-and-limits).
See these rules applied: prompt, exact calls, and a filled answer from real fixtures.
# Agent recipes (/docs/agents/recipes)
Each recipe maps a class of user prompt to the smallest set of noxstock calls and an answer template. The output rules these templates assume (answer first, preserve freshness and signs, no advice) live once in the [agent instructions](/docs/agents/instructions) — paste that block into your agent and the templates below fall out of it.
REST paths and MCP tools are listed side by side. MCP arguments use native JSON types: lists are arrays (`["10-K","10-Q"]`), not comma-separated strings. Plan access matters here: a Free key covers the snapshot/valuation recipes in full; `insider`, `compare`, `filings`, `technicals`, and `price-action` need Starter or Builder ([plans and limits](/docs/reference/plans-and-limits)).
## One-symbol brief [#one-symbol-brief]
Serves prompts like "What's going on with AAPL?", "Give me a read on Apple", "Should I be paying attention to NVDA?" — a single ticker, no specific sub-topic.
Calls: snapshot is usually enough. Add insider (Starter+) only when the prompt hints at ownership or recent activity; add valuation when it hints at price-versus-worth. On a Free key, skip the activity line and say insider data was not consulted rather than guessing.
```text
GET /v2/snapshot?symbol=AAPL
GET /v2/insider?symbol=AAPL # only if activity is in scope
```
```text
noxstock_snapshot({ "symbol": "AAPL" })
noxstock_insider({ "symbol": "AAPL" }) # only if activity is in scope
```
Template:
```text
Answer:
Setup
-
-
Activity / caveats
-
Freshness
- Snapshot: , as_of , market_status
- Insider: , as_of
Calls used
-
```
Filled with the live AAPL `snapshot` and `insider` fixtures (numbers copied verbatim; the insider rollup returns ten recent transactions and a 90-day summary, trimmed here to the relevant lines):
```text title="aapl-brief.txt"
Answer: AAPL is trading at 314.86, up 1.17% on the day and up 54.06% over the past
year (+25.56% vs SPY), sitting near the top of its 52-week range.
Setup
- Price 314.86, day change +1.17%. 52-week range 194.30-316.94; position 95.3% of
range, 1.8% below the 52-week high.
- 1-year +54.06%, vs SPY +25.56%. Sector: Technology, Consumer Electronics.
- Trailing P/E 37.28, FCF yield 2.83%, dividend yield 0.34%.
Activity / caveats
- Insider 90-day Form 4 rollup: net direction "selling". 0 open-market buys vs 13
open-market sells, net_shares_market_only -397,759, net_value_usd -$111,705,105.08.
Largest sale: Arthur D Levinson (Director), 50,000 shares at 311.02 on 2026-05-27,
not under a 10b5-1 plan.
- Notice: recent_transactions_limited_to_10. The full rollup scans the latest 200
Form 4 filings; only the 10 most recent transactions are itemized.
Freshness
- Snapshot: intraday, as_of 2026-06-05T14:19:06Z, market_status open.
- Insider: end_of_day, as_of 2026-06-05T06:45:45Z.
Calls used
- snapshot, insider
```
Note the two `as_of` timestamps differ: the quote is intraday, the Form 4 rollup is end-of-day. Surface both rather than collapsing them into one.
## Valuation check [#valuation-check]
Serves "Is AAPL expensive?", "Is NVDA cheap right now?", "How does the multiple look?". Snapshot gives price context; valuation gives the multiples and their 5-year position. Skip fundamentals unless the user asks *why* the multiple is what it is.
```text
GET /v2/snapshot?symbol=NVDA
GET /v2/valuation?symbol=NVDA
```
```text
noxstock_snapshot({ "symbol": "NVDA" })
noxstock_valuation({ "symbol": "NVDA" })
```
Template:
```text
Answer:
Why
- valuation_context: anchor , value , percentile_5y , position