Skip to main content
Reading a 10-K risk section is a three-call walk: list filings, pick the accession, fetch one section. You never download the whole filing, and you summarize from the text the section endpoint returns: nothing from news, memory, or consensus gets blended in.

The walk

1
List periodic filings and choose the form you actually want.
2
Read the section index on the chosen accession to see which sections exist and how large they are.
3
Fetch one section by name and summarize from that text.

1. List filings, pick the accession

/v2/filings defaults to form=10-K,10-Q,8-K and limit=20. Pass a tighter form CSV when you only want the annual report. Here, form=10-K returns just the 10-K rows for AAPL.
list_filings.py
import httpx

r = httpx.get(
    "https://api.stockcontext.com/v2/filings",
    params={"symbol": "AAPL", "form": "10-K", "limit": 5},
    headers={"X-API-Key": "sctx_..."},
)
filings = r.json()["data"]["filings"]
latest = filings[0]          # rows come back newest-first
accession = latest["accession"]
The latest 10-K row, copied from the live response (the full list also carries 10-Q and 8-K rows when you widen form):
filings.data.filings[…] (one row)
{
  "accession": "0000320193-25-000079",
  "form": "10-K",
  "filing_date": "2025-10-31",
  "period_of_report": "2025-09-27",
  "items": [],
  "items_text": [],
  "url": "https://www.sec.gov/Archives/edgar/data/320193/000032019325000079/aapl-20250927.htm",
  "size_bytes": 9392337,
  "sections": []
}
That size_bytes is 9.4 MB. You are not going to read it whole; sections is empty on the list because the index lives on the next call.
If the user asks for “the latest periodic filing” rather than the 10-K specifically, request form=10-K,10-Q, then pick by filing_date and tell the reader which form you used. Calling something “the latest 10-K” after selecting a 10-Q is the most common reporting error here.

2. Read the section index

GET /v2/filings/0000320193-25-000079 returns the section index with a char_count per section. That count is your budget: it tells you how much text the next call will return before you spend it.
filing-10k.data (trimmed)
{
  "symbol": "AAPL",
  "symbol_status": { "resolved": true },
  "accession": "0000320193-25-000079",
  "form": "10-K",
  "filing_date": "2025-10-31",
  "period_of_report": "2025-09-27",
  "sections": [
    { "name": "business", "char_count": 16004 },
    { "name": "risk_factors", "char_count": 68069 },
    { "name": "mda", "char_count": 21009 }
  ]
}
Section names are per-form, so check sections[].name before requesting one. A 10-K carries business, risk_factors, mda. A 10-Q carries mda, risk_factors, quantitative_qualitative_disclosures, controls_and_procedures; note it has no business section. Don’t assume a name exists on one form because it appeared on another. 8-K metadata has a different shape: no sections array at all. Instead it carries items (the disclosed item codes with titles, e.g. 2.02: Results of Operations and Financial Condition) and a press_release object with available and char_count. When press_release.available is true, fetch it with /section/press_release; the risk_factors-style names do not exist on an 8-K. items and items_text on list rows are 8-K item codes too, empty arrays on 10-K/10-Q rows. Form 4 metadata carries neither sections nor items, just the filing facts; its period_of_report is usually populated but can be null. The accession routes accept an optional ?symbol= cross-check. GET /v2/filings/0000320193-25-000079?symbol=AAPL confirms the accession belongs to AAPL; a mismatch returns PARAM_INVALID. A well-formed accession that matches no filing returns 400 PARAM_INVALID (Accession '…' was not found on SEC EDGAR…), and a malformed one is also 400 with a distinct is malformed message; neither is a 200. The symbol_status { "resolved": false, "reason": "symbol_unresolved_from_accession" } shape appears only for a real filing whose filer doesn’t map to a supported ticker, not for a missing accession. See error codes for the full set.

3. Fetch one section

GET /v2/filings/0000320193-25-000079/section/risk_factors returns the text and echoes the same char_count. Fetch only the section you intend to read.
filing-section-risk-factors.data (text trimmed)
{
  "symbol": "AAPL",
  "accession": "0000320193-25-000079",
  "form": "10-K",
  "section": "risk_factors",
  "char_count": 68069,
  "text": "Item 1A.    Risk Factors\nThe following summarizes factors that could have a material adverse effect on the Company's business... Macroeconomic and Industry Risks... Beginning in the second quarter of 2025, new tariffs were announced on imports to the U.S. ...",
  "freshness": "intraday",
  "market_status": "open",
  "cache_age_seconds": 12,
  "available": true
}
The text above is trimmed for the page; the live response returns the full 68,069 characters. Summarize from that text and nothing else. A risk summary built only from this section:
answer
Top risks in Apple's FY2025 10-K (filed 2025-10-31, risk_factors)
- Tariffs: new U.S. tariffs from Q2 2025 on China, India, Japan, South Korea,
  Taiwan, Vietnam, and the EU, plus a Section 232 semiconductor investigation;
  impact described as uncertain.
- Supply concentration: manufacturing is heavily outsourced to a small set of
  Asian partners, with single-source components.
- Antitrust and the DOJ/Google search case, where remedies on appeal could
  materially affect the search licensing revenue Apple earns from Google.

Source: accession 0000320193-25-000079, section risk_factors,
as_of 2026-06-05T14:19:24Z, freshness intraday.
Every bullet traces to a sentence in the returned text. That is the guardrail: a SEC risk summary is a summary of one section’s words. Do not add a headline, an analyst note, a price reaction, or a remembered fact: if it is not in the section text, it does not go in the answer. This is the anti-hallucination rule from the agent instructions, applied to filings.

MCP shape

The hosted tools take the same walk, with one argument difference: REST form is a CSV string, but the MCP forms argument is a JSON array.
stockcontext_filings_list({ "symbol": "AAPL", "forms": ["10-K"], "limit": 5 })
stockcontext_filings_get({ "accession": "0000320193-25-000079" })
stockcontext_filings_section({ "accession": "0000320193-25-000079", "name": "risk_factors" })

Cold first call

The first read of an accession pulls from EDGAR and can take up to ~20 seconds for a large 10-K; repeat reads of the same accession return in well under a second. Listing filings is fast either way; the cold cost is per accession. Don’t block a whole UI on it. See coverage and gaps for the latency note. Filing and insider data are also unsupported for the 1,125 foreign private issuers that file 20-F/40-F/6-K, and for ETFs; the row will carry a machine-readable reason rather than text.

Insider scan

Form 4 activity is a separate endpoint with its own rollup, not a filing section.