By the fastCRW team · Catalogue figures checked 2026-11-05 against GET /v1/data · Verify independently before relying on any figure.
To use the SEC EDGAR API from Python, you request a filer's submissions from data.sec.gov or search the full text of filings, and you follow the SEC's fair-access rules yourself. Pergamon exposes the same EDGAR records through one fastCRW key, and every response carries the source, the licence and the attribution. This guide shows the calls in Python and from an agent. Every call was run on 2026-11-05.
Nothing here is investment advice. The filings are public records, and what you conclude from them is your call.
The gotchas of raw EDGAR
The SEC publishes its own rules for automated access. It states "Current max request rate: 10 requests/second" and asks clients to "declare your user agent in request headers" (sec.gov). Third-party guides report a 403 response when the User-Agent header is missing (edgrapi.com). That detail comes from those guides, not from the SEC, so treat it as their claim.
Two more things trip people up:
- The CIK must be zero-padded to 10 digits. The data paths use it, for example
CIK0000320193.json. Pergamon needs the same form. Withcik=320193the upstream answers 404, and withcik=0000320193it answers 200. - The User-Agent is handled for you. The licence row for the full-text search source says: "SEC asks that automated access declare a contact in the User-Agent, which fastCRW does." The SEC's fair-access limits still apply upstream, so keep your own request volume modest.
Ticker or name to CIK
If you start from a company name or ticker, use the entity suggest capability. It is small and returns a handful of candidates:
curl "https://api.fastcrw.com/v1/data/secfts/entity-suggest?keysTyped=apple" \
-H "Authorization: Bearer $CRW_API_KEY"
Trimmed response, as returned on 2026-11-05:
{
"credits_used": 1,
"data": {
"took": 660,
"hits": {
"total": { "value": 230, "relation": "eq" },
"hits": [
{
"_id": "320193",
"_source": { "entity": "Apple Inc. (AAPL)", "tickers": "AAPL", "...": "..." }
}
]
}
}
}
The _id is the CIK without padding. Pad it yourself before you call the other SEC capabilities. The response is a list of hits, not a single answer, so check the entity name and tickers before you use the id.
The full company list, sec/company-tickers, is also available, but it is 797 KB over REST. Use it only when you need the whole table.
Filing history with sec/submissions
With a padded CIK, sec/submissions returns the issuer profile and the filing history. The call costs 1 credit.
from crw import CrwClient
client = CrwClient() # reads CRW_API_KEY
result = client.get_data("sec", "submissions", {"cik": "0000320193"})
print(result["provenance"]["attributionText"])
company = result["data"]
print(company["name"], company["tickers"])
print(list(company["filings"].keys()))
On 2026-11-05 the SDK call returned Apple Inc. with the ticker AAPL, and the filings object held the keys files and recent. The profile around it carries fields such as cik, exchanges, sic, sicDescription, fiscalYearEnd, stateOfIncorporation and formerNames. The REST response for the same CIK was 164 KB.
filings.recent is a set of parallel arrays, one array per field, with one entry per filing. For Apple it held 1,002 entries, with fields such as accessionNumber, filingDate, form, primaryDocument and reportDate. Older filings are listed in separate files, which the files block names. For Apple the first of those files is named CIK0000320193-submissions-001.json and covers 1,271 filings from 1994 to 2015. This post does not walk those files.
Zip the arrays together to get one row per filing. This function returns the most recent 10-K filings for a CIK:
from crw import CrwClient
client = CrwClient() # reads CRW_API_KEY
def recent_filings(cik: str, form: str = "10-K", limit: int = 5) -> list[dict]:
company = client.get_data("sec", "submissions", {"cik": cik})["data"]
recent = company["filings"]["recent"]
rows = zip(
recent["form"],
recent["filingDate"],
recent["accessionNumber"],
recent["primaryDocument"],
)
matches = [
{"form": f, "filed": d, "accession": a, "document": p}
for f, d, a, p in rows
if f == form
]
return matches[:limit]
for row in recent_filings("0000320193"):
print(row)
The filter is an exact match on the form string as the SEC files it. A filter of 10-K does not return a 10-K/A amendment, and the recent block also holds forms such as 8-K and 4. Pass the form string exactly as it appears in the array.
Output on 2026-11-05, first three rows of the five printed:
{'form': '10-K', 'filed': '2026-11-03', 'accession': '0000320193-26-000026', 'document': 'aapl-20260926.htm'}
{'form': '10-K', 'filed': '2025-10-31', 'accession': '0000320193-25-000079', 'document': 'aapl-20250927.htm'}
{'form': '10-K', 'filed': '2024-11-01', 'accession': '0000320193-24-000123', 'document': 'aapl-20240928.htm'}
Which capability to use
Four of the capabilities cover most research work. Pick by the question you are answering:
| Capability | Use it for | Size seen on 2026-11-05 |
|---|---|---|
secfts/entity-suggest | A name or ticker to candidate CIKs | Small, one call |
sec/submissions | One company's profile and filing history | 164 KB for Apple |
secfts/search | A phrase across the text of filings | About 60 KB for the query below |
sec/company-tickers | The full list of tickers and CIKs, for bulk mapping | 797 KB |
Every row costs 1 credit per call. Watch the response size as well: the full ticker list is cheap in credits but large in memory.
Full-text search across filings
The secfts/search capability searches the text of EDGAR filings and their exhibits. It takes a required q and optional filters such as forms, startdt and enddt. Put a phrase in inner double quotes so the search matches it as one unit. Pass the dates as YYYY-MM-DD, and use forms to keep results to one filing type, such as 10-K, so the list you read is short enough to review.
from crw import CrwClient
client = CrwClient() # reads CRW_API_KEY
hits = client.get_data("secfts", "search", {
"q": "\"artificial intelligence\"",
"forms": "10-K",
"startdt": "2026-01-01",
"enddt": "2026-06-30",
})
first = hits["data"]["hits"]["hits"][0]["_source"]
print(first["form"], first["file_date"], first["adsh"])
The same query returned about 60 KB on 2026-11-05. Each hit gives you an identity: the accession number (adsh), the entity names with tickers and CIKs, the form, the file date and the file type. It does not give you the sentence that matched. The catalogue description says the response carries the matching document's identity and facet counts, not a matched-text snippet. To quote the text, open the filing at its accession number.
The find output lists more optional filters: ciks, entityName, locationCodes, locationType, from and sort. Use ciks to limit a phrase search to one company after you have resolved its CIK.
From an agent
The MCP tools expose the same capabilities. An agent calls crw_find_data once to discover a capability, then crw_get_data with the options. The get call for the entity suggest looks like this:
{"name": "crw_get_data", "arguments": {"provider": "secfts", "capability": "entity-suggest", "options": {"keysTyped": "apple"}}}
The agent then passes the padded CIK to sec/submissions with the same tool. Over the hosted endpoint on 2026-11-05 the submissions result for Apple was about 164 KB of text, under the 256 KiB MCP cap, and it returned creditsUsed: 1.
The MCP tools return camelCase keys, such as creditsUsed and attributionText. The REST API returns credits_used. Use whichever surface your code calls, and do not mix the two in one parser.
Putting it together
A research agent that answers "what has this company said about AI in its 10-K filings?" can run the same three steps every time:
- Resolve the name with
secfts/entity-suggest, then pad the CIK to 10 digits. - Read the recent 10-K filings with
sec/submissionsand keep the accession numbers. - Run
secfts/searchwith the phrase, the form filter and theciksfilter, then open the matching filings to read the passages.
The first two steps return data you can check. The third returns identities that the agent must follow to the filing. Keep that split visible in the agent's output, so a reader can tell which sentences came from a filing the agent opened and which came from the search hit alone.
Errors you will see
upstream_errorwith "SEC EDGAR returned 404 for this request". Check the CIK padding first. This is the error a short CIK produces.upstream_timeout. The upstream did not answer in time, and the call is not billed. Retry after a pause, with the same options.invalid_options. A required option is missing, for exampleqon the search. The error names the option, so fix the call rather than retrying it.result_too_large. Only over MCP, see the limits below.
Limits
- EDGAR's own fair-access rules still apply upstream. Keep request rates within the SEC's published limit, and do not loop over thousands of CIKs without a pause.
- The MCP cap is 256 KiB. A larger result is refused with
result_too_largeand, for a server source, refunded. The full company list is 797 KB, so over MCP usesecfts/entity-suggestfor a lookup instead. - REST and the Python SDK return the whole body. They have no 256 KiB cap, so a large result comes back in full, and your code has to hold it in memory.
- Search returns identities, not passages. Plan for a second step that opens the filing.
- The older filing files are not walked here. The recent block covers the latest filings. Older history needs the files that the response names.
Sources
- SEC: Accessing EDGAR data
- edgrapi.com: SEC EDGAR API in Python (third-party guide)
- fastCRW data sources documentation
Related: Public data API for AI agents · Build a finance research agent · Financial research agents for investment work · SEC EDGAR on Pergamon · SEC full-text search on Pergamon
