API Conventions
Request correlation
- every response includes
Request-Id - callers can provide
x-request-idto make local logs and repeatable tests easier to compare
Metering
- API responses include
secapi-meter-class - this is the canonical hint for billing and quota classification in logs
- hosted MCP
tools/callusage rows also persistmcp_tool_name;intelligence.queryandsignals.dilution.enhanced.getare the current MCP tools that count towardai_queries
MCP JSON-RPC quota envelope
Successful quota-counted MCP tool calls include top-level_secapi.quota metadata with family, mcpToolName, limit, used, remaining, resetAt, and plan context. MCP quota exhaustion returns HTTP 429 with JSON-RPC error code -32005 and error.data.code: "ai_query_quota_exceeded".
-32004 means mcp_tool_timeout and may be retried with bounded backoff. -32005 means quota exhaustion and should not be retried until the reset window or a plan/quota change. -32006 means mcp_tool_circuit_open; treat it as temporary service protection for that tool and retry only after backoff, not as quota exhaustion.
Cost and token headers
- every
/v1/*response includessecapi-token-countandsecapi-estimated-costso agents can budget natively without re-reading the pricing page secapi-token-countis an approximate output-token count computed with theo200k_basetokenizer (gpt-4o / o1 / modern-frontier encoding) as a portable proxy; consumer-model token usage may vary 10–30%secapi-estimated-costis a dollar string at 4-decimal precision (e.g.$0.0042) reflecting the plan-discounted per-call rate pre-grant-offset; it is emitted on every/v1/*2xx regardless of token-count opt-in- grant and credit balance are communicated separately via
secapi-free-grant-remainingandsecapi-budget-used; the final billed amount reflects grant offsets on top of the per-call estimate secapi-token-count-estimated: trueis emitted wheneversecapi-token-countfalls back to aceil(bytes / 4)approximation, either because the response body exceeded 512 KB or because the response is a non-JSON billable payload (filing downloads, csv/markdown/pdf exports). Absence of this header means the count is an exact tokenizer result from the JSON body- counts flagged as estimated are advisory for budget planning only; do not use them for exact tokenizer-equivalence math
- error responses (4xx, 5xx), rate-limit 429, billing-required 402, and budget-gate 402 all emit
secapi-token-count: 0andsecapi-estimated-cost: $0.0000; agents should treat failed requests as free
Opting in to exact token counts
Response-body tokenization is opt-in by default to keep p50 latency on hot paths low. Clients that want exact counts on every response add the request header:| Mode | secapi-token-count | secapi-token-count-source | secapi-estimated-cost |
|---|---|---|---|
| Opt-in absent (default), cache miss | 0 | opt-in-required | rate-card |
| Opt-in absent (default), cache hit | exact (from cached body) | omitted; secapi-cache-hit: true | rate-card |
| Opt-in present, cache miss | exact (from body tokenize) | omitted | rate-card |
| Opt-in present, cache hit | exact (from cached body) | omitted; secapi-cache-hit: true | rate-card |
| Error (4xx/5xx) | 0 | (omitted) | $0.0000 |
/v1/* GETs) carry an exact token count regardless of opt-in, because the count is precomputed once at cache-write time and stored alongside the body. The secapi-cache-hit: true header makes that visible to clients.
The secapi-token-count-source: opt-in-required advisory header marks the default path: if your application needs an exact count for cost reconciliation, re-issue the request with secapi-compute-headers: token-count.
Response formats
- every response-shape endpoint accepts
?view=default | compact | agentdefault(implicit): human-readable shape with full provenance, freshness, materialization, and validation metadatacompact: trimmed subset of top-level fields — suitable for UI consumers that only need identifiers + datesagent: agent-optimized shape that keeps essentials and citation pointers (e.g.filingUrl,accessionNumber, offsets) and drops provenance chains, freshness metadata, and pagination context not needed for retrieval
?view=agentand?view=compactcoexist as distinct shapes; agent is not a strict subset of compact — it ships the richer “essentials + citations” shape while compact ships a UI-friendly top-level skim- token-count and cost headers naturally shrink in agent mode (agents pay less for compact responses). No client changes are required — header values reflect the final serialized body
- agent-mode responses preserve camelCase field names — agent mode is a projection of the default shape, not a rename. See the per-endpoint API reference for the exact agent-mode field set
- unknown values of
?view=fall back silently todefault, so accidental typos do not return a 4xx
Factor response modes
Factor, portfolio-factor, and model-factor endpoints useresponse_mode=compact | standard | verbose plus optional include controls:
compact: chart, table, SDK, and agent handoff shape with identifiers, dates, ranks, contribution rows, and compact trust metadatastandard: default application shape with the fields most dashboards and portfolio workflows needverbose: expanded methodology, freshness, and materialization context for audits and debugging
response_mode=compact for /v1/factors/history/{factorKey}, /v1/factors/sparklines, /v1/factors/extreme-moves, /v1/factors/extreme-pairs, /v1/factors/valuations, /v1/factors/valuations/stocks, /v1/portfolio/attribution, /v1/portfolio/hedge, and /v1/models/factor-analysis when the next consumer is an LLM, UI table, or chart. Preserve requestId, traceparent, freshness, and methodology metadata even when summarizing compact responses.
Endpoints that support ?view=agent
Agent mode is live on 15 endpoints. Expect 30–95% byte-size reduction depending on endpoint shape; endpoints with large prose summaries see the biggest reductions.
-
GET /v1/filings/latest— single filing essentials +filingUrl -
GET /v1/entities/resolve— entity with primary identifiers + match metadata -
GET /v1/statements— statement with rows/periods, provenance dropped -
GET /v1/facts— fact point items + liftedaccessionNumber/filingUrl -
GET /v1/sections/search— section items + liftedstartOffset/endOffset -
GET /v1/insiders— insider trade items with trade essentials, ownership flags dropped -
GET /v1/forms/144— Form 144 filings with citation pointers -
GET /v1/offerings— offering records (S-1 / 424) with prospectus URL -
GET /v1/events/ma— M&A events with deal structure + counterparty -
GET /v1/events/enforcement— enforcement actions with release metadata -
GET /v1/events/voting-results— voting results events with proposals array (shaped) -
GET /v1/compensation— executive compensation breakdown -
GET /v1/owners/13f— 13F report with nestedholdings[](shaped) -
GET /v1/funds/nport/holdings— N-PORT holdings with nestedholdings[](shaped) -
GET /v1/companies/subsidiaries— subsidiary list with citation envelope (API-consistency, byte-neutral)
Citation + char-range spans
Two endpoints emit machine-verifiable citations on every result row:GET /v1/sections/searchGET /v1/search/semantic
| Field | Type | Description |
|---|---|---|
accession | string | null | SEC accession number (e.g. 0000950170-24-012345) |
section_key | string | null | Canonical section identifier (e.g. item_1a, item_7, risk_factors) |
char_start | int | null | Inclusive start offset into the section text (see reference frame below) |
char_end | int | null | Exclusive end offset — 0 ≤ char_start < char_end ≤ len(section_text) |
highlighted_snippet | string | null | ±150 char window around the match, with query terms wrapped in **…**, sentence-boundary truncated, capped at 320 chars |
source_url | string | null | Public SEC.gov filing URL — usable directly in the browser |
ticker | string | null | Issuer ticker, when known |
Reference frame
char_start and char_end index into the section markdown text (the canonical plaintext extraction stored in section_snippets.content_md), not the raw filing HTML. This frame is stable across reparses and meaningful without fetching the multi-MB filing HTML body.
To re-fetch the original section text outside the response, request the section by (accession, section_key) from GET /v1/filings/{accession_number}/sections/{section_key}, and slice the returned contentMd from char_start to char_end.
Null-fallback semantics
When the validator can’t produce a span (no section text available for the row or offsets out of bounds), the response dropschar_start, char_end, and highlighted_snippet to null but keeps accession, section_key, source_url, and ticker populated. A _citation_degraded field records the reason ("no_section_text" | "offsets_invalid"). Agents should switch on char_start === null rather than "char_start" in result.
?view=agent interaction
Citation fields are emitted on every result row regardless of ?view=. In agent mode (?view=agent), score and retrievalMode are dropped from /v1/search/semantic results; citation fields remain available.
REST vs MCP shape conventions
REST and MCP serve different consumer profiles, and SEC API optimizes for each:- REST endpoints support
?view={default,compact,agent}. Theagentview is the wire-byte-optimized projection — large prose drops out, the dilutionverificationblock compresses to the canonical{ confidence, crossValidationsPassed, sourceSpanResolved, modelVersion }summary, and the response shrinks 30–95% depending on the endpoint. Use it from SDK clients that want low-latency / low-token responses to forward to a UI or downstream agent. - Factor REST endpoints use
response_mode={compact,standard,verbose}for factor-specific payload shaping, because factor workflows need distinct chart/table, application, and audit shapes. - MCP tools always return the full payload (verification block, full provenance chain, all citation pointers). MCP context is already an agent context; returning the evidence up front avoids a second fetch. Agents reasoning over MCP decide what to show to the user.
GET /v1/dilution/events?view=agent→ returnssummarizeVerification(row.verification)(the contract frompackages/contracts/src/verification.ts).dilution.events.listMCP tool → returns the full row including the unmodifiedverificationblock. Noviewargument is accepted.
Substring-match heuristic
The match span is anchored by the longest query token, falling back to the snippet’s leading words and finally to a section-head citation. Multi-occurrence ambiguity is resolved by earliest occurrence. Thehighlighted_snippet window is stable for display; the precise [char_start, char_end) may move slightly as upstream rerankers improve. Use the bolded snippet for display and the offsets only for programmatic slicing.
Versioning
- production clients should send
secapi-versionand pin to a dated release - additive fields can ship within a pinned version without requiring a client rebuild
- breaking behavior changes should land behind a new dated version and be called out in
/docs/changelog - migration guides should be updated in the same change when compatibility behavior changes
Error model
Error responses use a stable JSON shape:Auth conventions
- machine access uses
x-api-key - MCP protected-resource metadata is publicly available
- OAuth authorization-server metadata returns an explicit
503 authorization_server_unavailableif authorization metadata is not available