# Disclosery API v1 Contract version: 1.2, M15 transport and MCP compatibility extensions to the M12 contract on October 5, 2026. Read this document at `/llms-api.txt`. Human connection help is at `/docs`. The JSON API and MCP server read the site's stored SEC ownership data. They make no EDGAR request during a call. Both use the same query results as the pages. The public endpoint is `https://disclosery.com`. Coverage depends on the imported filings. Read `/data` for recorded historical imports and recent feed activity before interpreting empty results. ## Transport and authentication Use the configured site origin, followed by the paths below. MCP uses `/mcp` with Streamable HTTP. The `mcp` binary serves MCP and JSON API routes together. It uses the official Go SDK v1.8.0, pinned in `go.mod`, in stateless mode. It exposes no legacy `/sse` endpoint and no stdio transport. Streamable HTTP may use an SSE response, which does not make it the older HTTP+SSE transport. See the [pinned SDK source](https://github.com/modelcontextprotocol/go-sdk/tree/v1.8.0). All API data routes use GET. Send `Authorization: Bearer YOUR_API_KEY` for keyed access. Account keys are created at `/account/keys`, stored as hashes, and revocable. Suspended accounts and invalid or revoked keys receive an error, without falling back to keyless access. Browser cookies are ignored. Keys in query strings are not supported. With no Authorization header, the service uses the keyless IP allowance. Only configured trusted proxies may supply the client identity header. An arbitrary forwarded header does not select an IP bucket. Successful authenticated data responses have `Cache-Control: private, no-store`. An IP admission gate allows 120 requests per UTC minute before key validation, including MCP initialization and discovery. It is separate from the daily data-call allowance. MCP request bodies are bounded at 64 KiB. Key storage lookups have a three-second deadline. Each data query has a 15-second deadline and inherits request cancellation. Storage timeouts return `unavailable`, HTTP 503 or an MCP tool error, without extending an SDK session deadline. ## Frozen route and tool inventory The table is checked against the actual HTTP router and MCP registry. Required arguments are path parameters except `q`, which is a query parameter. MCP calls pass the listed arguments as properties of an input object. The MCP argument `cusip_or_ticker` occupies the HTTP `{security}` path segment. Optional HTTP arguments are query parameters. | Tool | Method | Route | Required | Optional | |---|---|---|---|---| | `fund_portfolio` | GET | `/api/v1/funds/{cik}/portfolio` | `cik` | `quarter,table,page,view,limit,sort,fields,format,columns` | | `fund_timeline` | GET | `/api/v1/funds/{cik}/timeline/{security}` | `cik,cusip_or_ticker` | none | | `fund_filings` | GET | `/api/v1/funds/{cik}/filings` | `cik` | `since,limit` | | `group` | GET | `/api/v1/groups/{slug}` | `slug` | `limit` | | `stock_holders` | GET | `/api/v1/stocks/{ticker}/holders` | `ticker` | `quarter,page,view,limit,sort,fields` | | `stock_events` | GET | `/api/v1/stocks/{ticker}/events` | `ticker` | `since,limit` | | `consensus` | GET | `/api/v1/consensus/{kind}` | `kind` | `quarter` | | `search` | GET | `/api/v1/search` | `q` | `limit` | | `filing` | GET | `/api/v1/filings/{accession}` | `accession` | `page` | The API additionally registers one HTTP-only route. It is not an MCP tool. | API operation | Method | Route | Required | Optional | |---|---|---|---|---| | `latest` | GET | `/api/v1/latest` | none | `before,after,limit,scope,key,form` | `format` and `columns` on the portfolio route are HTTP-only parameters. The nine MCP tools remain registered. Their summary arguments are shared with HTTP. `format=json` is the default. `format=csv` requires a current paid API key and streams the shared web CSV table without pagination. `page` is rejected for CSV. `columns` selects unique zero-based column indexes in comma-separated order and is rejected for JSON. Options and principal tables have five columns, holdings seven, and exits four. Invalid selection is rejected before a daily quota unit is taken. CSV uses one unit and exposes `X-Quota-Limit`, `X-Quota-Used`, `X-Quota-Remaining` and `X-Quota-Reset`. The `X-Export-Status` trailer is `complete` or `incomplete`. Clients must reject incomplete streams. CSV authorization is checked afresh on every request. `latest` returns the same JSON envelope. Its data has `name`, `path`, `filings`, `cursor` and optional `next_cursor`, with normal API citation URLs. Filing rows include actual `filing_id`, accession, form, filer CIK, filing date, acceptance and ingest times. `before` and `after` are positive filing IDs. `limit` defaults to 100 and is at most 100. Results are ordered by descending committed filing ID. Free and anonymous access reads unfiltered arrivals from the last seven days. Paid access may read all arrivals and use `scope=latest|funds|groups|stocks|insiders`, `key` for entity scopes, and a known SEC `form`. Entity scope and form filters require the paid plan. Both cursor bounds can be supplied when `before` is greater than `after`. For watch, keep the original `after` fixed while fetching each `next_cursor` as `before`. Save the first page's `cursor` only after all pages have been processed. This avoids skipping bursts of more than 100 arrivals. An empty result has `filings=[]`, `cursor=0` and no `next_cursor`. Preserve the previous saved cursor on empty responses. Every MCP tool has a human title, description, `readOnlyHint: true` and `openWorldHint: false`. The tools query a bounded local filing database. They do not perform web searches, make trades, change accounts, or offer a bulk identifier mapping download. ## Arguments - `cik`: a positive CIK, fund slug returned by search, or relative `/funds/{slug}` path, for example `1922318` or `/funds/pale-fire-capital-se-1922318`. - `cusip_or_ticker` / `{security}`: a ticker or a single nine-character CUSIP. The query resolves one security and also accepts a relative `/stocks/{ticker}` path. A fund timeline requires both the fund and security. It does not return a CUSIP-to-ticker table. - `ticker`: stock ticker, one CUSIP, or relative `/stocks/{ticker}` path, for example `LSPD` or `/stocks/LSPD`. - `slug`: filer-group slug, as returned by search. - `accession`: a valid SEC accession, dashed or normalized without dashes. - `quarter`: `YYYYqN`, for example `2026q2`. Omit it to use the query's latest quarter. - `since`: ISO calendar date `YYYY-MM-DD`. Fund filings and stock events filter filing dates (including the given day), rather than the event date reported inside a filing. - `page`: integer 1 through 10,000, default 1. Each paged result provides `page`, `pages`, `page_size` and `total`. Portfolio tables other than the selected table have bounded previews. - `table`: portfolio table selector. Omit for holdings. Supported selectors are `options`, `principal`, or `exits`. The empty selector means holdings. - `limit`: integer 1 through 100, default 50, for filings, groups and stock events. Search uses 1 through 50, default 10. - `q`: required search text. Empty or whitespace-only text is rejected. - `kind`: `buys`, `sells`, `new`, `consensus-buys`, `consensus-sells`, `new-positions`, `most-held`, `largest`, or `most-followed`. The short names map to the matching rankings. Unknown or repeated HTTP query parameters are rejected. Invalid arguments are rejected before taking a quota unit. A valid query that finds no entity or fails after admission uses one unit. History limits are enforced by the server, regardless of client. Free and anonymous access is limited to eight quarters on portfolios, holders and timelines. Consensus rankings allow only the latest quarter for free and anonymous clients. Earlier ranking quarters require paid access. Individual filings remain readable across history. ### Optional bounded summaries `fund_portfolio` and `stock_holders` accept `view=summary`. Omit it to retain the full representation unchanged. Summary `limit` is 1 through 100, default 10, per page. `sort` is `value` (default), `shares`, or signed `change`, descending, with unknown values last and stable identity ties. Sorting runs over the whole eligible portfolio table or manager-collapsed holder universe before pagination. `page` retains its normal meaning. Free history rules and current-key authorization apply before summary projection. Summary selectors are rejected for CSV. `fields` is a comma-separated unique allowlist of summary row fields. Portfolio fields: `ticker,name,amount,value_usd,pct_portfolio,change,change_kind,path,latest_event`. Holder fields: `name,cik,kind,amount,value_usd,pct_portfolio,change,change_kind,path`. Omit `fields` to include the listed defaults except portfolio `latest_event`, which is opt-in. Unknown or repeated fields are rejected before quota consumption. `source_url`, `edgar_url`, amount/class/type labels, warning flags and notes are mandatory and cannot be hidden. Partial-quarter, principal-amount and no-base metadata also remain visible. Explicit holder filing references retain accession, acceptance and ingest times and citations. Portfolio quarter source filings remain cited once. Summary results retain entity, quarter, summary, paging, table totals and the selected row array. They omit secondary preview tables and unrelated event arrays. A bounded row view is not a statement that other positions, events or history do not exist. `limit`, `sort` and `fields` require `view=summary`. The MCP registry still has nine tools, and the API inventory still has ten routes. Only optional arguments and identifier acceptance expand. ## Response envelope and allowance A successful JSON response is an object: ```json { "tier": "anonymous", "quota": { "limit": 20, "used": 1, "remaining": 19, "reset_at": "2026-10-06T00:00:00Z" }, "data": {} } ``` The example describes the envelope only. `data` contains the query result described below. MCP structured results carry the same envelope. `tier` is `anonymous`, `free`, or `paid`. The normal daily limits are 20 per IP, 100 per free key, and 2,000 per paid key. An operator quota override can change a key's effective limit, reflected in the response. One admitted data call consumes one unit across both transports. MCP initialization and tool discovery do not consume data-call units. Daily counters reset at midnight UTC. The quota store retains counters under memory pressure. If it cannot admit a call, the service returns 503 rather than allowing uncounted calls. `used` and `remaining` include the admitted call. Requests that exceed the quota are refused. Shared NAT addresses share a keyless allowance. Quota and pagination metadata are control objects and do not carry filing citations. ## Data results | Tool | Result contents | |---|---| | `fund_portfolio` | Filer, quarter and available quarters, summary, source filings, holdings, options, principal amounts, exits, largest changes and post-period stakes. `paging`, `totals` and `table` describe the selected table. | | `fund_timeline` | Fund and security, merged filing steps and estimate with its basis and provenance. Accepted and ingested times remain separate. | | `fund_filings` | Filer and filing rows, filtered by `since`, bounded by `limit`. | | `group` | Group identity, members, grouping evidence and filing union, bounded by `limit`. | | `stock_holders` | Security, quarter, manager-collapsed holders and paging. | | `stock_events` | Security and ownership event rows, filtered by `since`, bounded by `limit`. | | `consensus` | Ranking kind, quarter, coverage universe, rows, paging and computation time. | | `search` | Search classification and separate funds, groups, stocks, insiders and filings arrays, with name, detail and page path. | | `filing` | Filer and source filing, amendment chain, plus holdings, stake facts or insider facts according to the form. Holdings may be paged. | Numeric shares and dollars retain the query's types and units. Nullable values mean the filing or calculation does not supply a value, not zero. Decimal quantities are JSON numbers. Clients should preserve their decimal precision when parsing, especially share counts and transaction prices. Days use `YYYY-MM-DD`. Times use RFC 3339 UTC. Reported facts and estimates remain separate. A bulk filing may have no acceptance time. Coverage can be incomplete. Read `/data` and `/methods` before treating a result as a full market universe or a live feed. ### Citations Domain/result objects carry `source_url`, an absolute page URL, and `edgar_url`, an SEC filing URL or null. Entity aggregates without one source filing, such as search entities, group summaries and ranking rows, use null. A nested entity does not inherit a filing merely because its surrounding result mentions one. A filing fact names its actual filing. Do not substitute a convenient filing for a manager-wide aggregate. For JSON, page URLs carry `utm_source=api&utm_medium=tool&utm_campaign={tool}`. For MCP they carry `utm_source=mcp&utm_medium=tool&utm_campaign={tool}`. SEC links remain SEC links, without Disclosery traffic tags. Metadata envelopes such as quota and paging need no citations. Cite the page for methods and estimates, and EDGAR for reported facts. ## Errors JSON errors have this shape: ```json {"error":{"code":"invalid_argument","message":"quarter must be YYYYqN."}} ``` | HTTP status | Code | Meaning | |---|---|---| | 400 | `invalid_argument` | Required, malformed, unsupported or repeated argument. | | 403 | `paid_required` | CSV or latest filters require paid access. | | 403 | `history_requires_paid` | Requested history exceeds the free window for this query. | | 401 | `unauthorized` | Invalid, revoked or suspended key, or unavailable client identity. | | 404 | `not_found` | Entity not found. | | 410 | `gone` | Filer group removed. | | 429 | `rate_limited` | IP request rate exceeded. Includes `Retry-After: 60`, without a daily reset field. | | 429 | `quota_exceeded` | Daily allowance reached. Includes `reset_at` and `Retry-After`. | | 503 | `unavailable` | Database, key store, quota store or another service failure. | MCP query failures use `isError: true` with the same error detail. HTTP authentication failures occur before the MCP handler and return an HTTP error. Never treat an error as an empty successful result. The exact quota message for every tier is: > Daily free allowance of {n} calls reached, resets 00:00 UTC. Members on the $5 plan have 2,000. Plan details: {plans URL}. `{n}` is the effective limit. The plan URL is the configured origin plus `/plans`, never a checkout link. ## Example ```sh curl 'https://YOUR_SITE/api/v1/funds/1922318/timeline/LSPD' curl -H "Authorization: Bearer YOUR_API_KEY" \ 'https://YOUR_SITE/api/v1/funds/1922318/portfolio?quarter=2026q2&page=1' ``` Replace `https://YOUR_SITE` with `https://disclosery.com` or your configured origin. Example filings may not be loaded. When results are available, compare their reported facts across the page, JSON, MCP and linked SEC filings before relying on them. An empty result does not establish that the underlying filing or position does not exist.