This page documents the JSON endpoint behind SEC EDGAR's full-text search, efts.sec.gov/LATEST/search-index. Every parameter, response field, and behavior described below was tested against the live endpoint on 2026-09-08 with a compliant User-Agent header and a spaced-out request pattern. Where the SEC's own documentation confirms something, that's noted; where it doesn't, that's noted too. If you want the beginner walkthrough of EDGAR filing types and CIK lookups first, see our SEC EDGAR tutorial.
What EFTS Is and What It Covers
EFTS is the Elasticsearch-backed service that powers the search box at sec.gov/edgar/search. It indexes the full text of EDGAR filings -- not just their metadata -- so you can search for a phrase like "material weakness" and get back every filing that contains it, rather than having to already know which company or filing type to look in.
Coverage does not go back to the start of EDGAR. EDGAR itself has filings back into the 1990s, but the SEC's own search page states the full-text index covers filings "since 2001." We confirmed this two ways: a query restricted to 1996-01-01–2000-12-31 returned an internal server error rather than results, while the same query restricted to all of 2001 returned normal hits starting January 2001. If you need pre-2001 filings, you have to fall back to EDGAR's metadata-only browse endpoints (sec.gov/cgi-bin/browse-edgar) or the data.sec.gov submissions API -- full-text search simply doesn't reach that far back.
No schema commitment. The SEC does not publish a formal OpenAPI/Swagger spec or a versioning policy for EFTS. The LATEST in the URL is a hint, not a promise -- field names, aggregation buckets, and error formats below are what the endpoint returned at the time of writing and could change without notice. Treat this as a reference for a live, informally-maintained service, not a stable public contract.
The Endpoint: UI vs. JSON API
There are two distinct surfaces:
- Human UI:
https://www.sec.gov/edgar/search/-- a search box and results page meant for browsers. It calls the JSON API behind the scenes and renders the results with highlighted snippets. - JSON API:
https://efts.sec.gov/LATEST/search-index-- a plainGETendpoint that takes query-string parameters and returns Elasticsearch-shaped JSON. This is what you actually want for scripts. It is unauthenticated (no API key), but it does require a descriptiveUser-Agentheader, same as every other sec.gov endpoint.
Every example below hits the JSON endpoint directly.
Parameter Reference
Tested against live traffic on 2026-09-08. "Documented" means the SEC's own EDGAR search UI exposes an equivalent control (date presets, form filters, entity search) -- the SEC does not publish a parameter list for developers, so every row here was confirmed by observing actual responses.
| Parameter | Type | Example | What it does | Confirmed |
|---|---|---|---|---|
q | string | q=%22material+weakness%22 | Full-text query. Quoted text becomes an exact-phrase match (match_phrase against the internal doc_text field). Optional -- omitting it returns filings sorted by date instead of relevance (no _score). | Verified |
forms | string | forms=10-K or forms=8-K,10-K | Restricts results to one or more root form types, comma-separated. Filters on the underlying root_forms field, so 10-K also matches 10-K/A. | Verified |
dateRange | string | dateRange=custom or dateRange=10y | Selects a preset window (matches the UI's "Last 10 years / 5 years / 1 year / 30 days" options) or switches to a custom range read from startdt/enddt. | Verified (custom and 10y) |
startdt / enddt | date (YYYY-MM-DD) | startdt=2026-01-01&enddt=2026-03-31 | Inclusive filing-date bounds, only applied when dateRange=custom. | Verified |
ciks | string | ciks=0000320193 | Filters to one or more CIKs, comma-separated. Must be zero-padded to 10 digits -- an unpadded CIK (ciks=320193) silently returns zero hits instead of erroring. | Verified |
entityName | string | entityName=Apple | Fuzzy-matches company/person name, independent of ciks. Useful when you don't have a CIK on hand yet. | Verified |
from | integer | from=100 | Pagination offset in hits, not pages. Combine with the fixed page size (see Pagination below). | Verified |
category | string | category=form-type | Appears in the UI's own outgoing requests as a way to scope which form-type group is being browsed. | Not independently confirmed -- request succeeded but we couldn't isolate its effect from forms |
locationCode | string | — | Referenced in the UI's location-filter sidebar (state/country of incorporation or business office). | Not tested -- omitted from examples below |
Two things worth calling out because they're easy to get wrong: the endpoint is case-sensitive about path casing in some environments (use efts.sec.gov/LATEST/search-index exactly), and an unrecognized or malformed parameter is silently ignored rather than rejected -- if a query "isn't filtering," check for a typo in the parameter name before assuming the API is broken.
Response Schema
The response is raw Elasticsearch output -- there's no SEC-specific wrapper. Here's a trimmed real response for q=%22material+weakness%22&forms=10-K&ciks=0000320193 (21 real hits, one shown):
{
"took": 276,
"timed_out": false,
"hits": {
"total": { "value": 21, "relation": "eq" },
"max_score": 1.8883753,
"hits": [
{
"_index": "edgar_file",
"_id": "0000320193-24-000123:aapl-20240928.htm",
"_score": 1.8883753,
"_source": {
"ciks": ["0000320193"],
"period_ending": "2024-09-28",
"display_names": ["Apple Inc. (AAPL) (CIK 0000320193)"],
"root_forms": ["10-K"],
"form": "10-K",
"file_date": "2024-11-01",
"adsh": "0000320193-24-000123",
"biz_locations": ["Cupertino, CA"],
"file_type": "10-K",
"sics": ["3571"]
}
}
]
},
"aggregations": {
"entity_filter": { "buckets": [
{ "key": "APPLE INC (AAPL) (CIK 0000320193)", "doc_count": 13 },
{ "key": "Apple Inc. (AAPL) (CIK 0000320193)", "doc_count": 7 }
]},
"form_filter": { "buckets": [{ "key": "10-K", "doc_count": 21 }] },
"sic_filter": { "buckets": [{ "key": "3571", "doc_count": 21 }] },
"biz_states_filter": { "buckets": [{ "key": "CA", "doc_count": 21 }] }
}
}
Field notes, from what we actually observed:
hits.total.value-- match count, but see the result-window section below: once it exceeds 10,000 the response returns{"value": 10000, "relation": "gte"}instead of an exact number.hits.hits[]._id-- format is{accession-number}:{primary-document-filename}, which is exactly what you need to build a filing URL (see Recipe 3 below)._source.adsh-- the accession number, dash-formatted (0000320193-24-000123)._source.ciksanddisplay_namesare arrays, because a single filing (e.g. a joint Schedule 13D) can list more than one filer.- No highlighted snippet is returned. The web UI shows matched text in context, but the raw API response we captured carries no
highlightblock and explicitly excludes the indexed text ("_source":{"exclude":["doc_text"]}appears in the echoed query). If you need the surrounding text, you have to fetch the filing document yourself and search it client-side. aggregations-- facet counts (entity, SIC code, business state, form type) computed over the current filter set, capped at 30 buckets each. This is what powers the sidebar filters in the UI; you can read it even if you only care about the counts, not the underlying hits.
Pagination and the Result-Window Limit
The page size is fixed at 100 hits per request (confirmed by counting returned _id entries) -- there is no documented size parameter to change it; use from to page through results in increments of 100.
Elasticsearch's standard result-window cap applies: from + size cannot exceed 10,000. Past that, the API returns HTTP 200 with an error payload embedded in the body, not an HTTP error status:
curl -s -H "User-Agent: AlphaSuite [email protected]" \
"https://efts.sec.gov/LATEST/search-index?q=%22material+weakness%22&forms=10-K&from=10000"
# {"errorType":"ResponseError","errorMessage":"search_phase_execution_exception:
# [illegal_argument_exception] Reason: Result window is too large,
# from + size must be less than or equal to: [10000] but was [10100]. ..."}
Because the error comes back as 200 OK, don't rely on the HTTP status code to detect failure -- check for an errorType key in the JSON body. Practically, this means any query matching more than 10,000 filings needs to be narrowed (by date range, form type, or CIK) before you can page through all of it; there's no scroll/cursor API exposed publicly.
Rate Limiting and the User-Agent Requirement
Like every sec.gov and *.sec.gov subdomain, EFTS enforces the SEC's site-wide policy: a rate limit of 10 requests per second, and a required User-Agent header naming your application and a contact address. Requests without a compliant header, or bursts over the limit, get throttled or blocked. There is no API key -- the User-Agent header is the identification mechanism.
curl -s -H "User-Agent: AlphaSuite research [email protected]" \
"https://efts.sec.gov/LATEST/search-index?q=%22material+weakness%22&forms=10-K"
import time
import requests
HEADERS = {"User-Agent": "AlphaSuite research [email protected]"}
def efts_search(q, forms=None, ciks=None, from_=0, **params):
params.update({"q": q, "from": from_})
if forms: params["forms"] = forms
if ciks: params["ciks"] = ciks
resp = requests.get(
"https://efts.sec.gov/LATEST/search-index",
headers=HEADERS, params=params, timeout=10
)
data = resp.json()
if "errorType" in data:
raise RuntimeError(data["errorMessage"])
return data
results = efts_search('"material weakness"', forms="10-K")
print(results["hits"]["total"])
time.sleep(0.15) # stay comfortably under 10 req/sec
Practical Recipes
1. Search a phrase across 10-Ks in a date window
GET https://efts.sec.gov/LATEST/search-index
?q=%22material+weakness%22
&forms=10-K
&dateRange=custom
&startdt=2026-01-01
&enddt=2026-03-31
This is the pattern for "screen all annual reports filed in a quarter for a given disclosure phrase" -- exactly the kind of thematic search that's impractical to do by browsing individual filings.
2. Filter by company and form type together
GET https://efts.sec.gov/LATEST/search-index
?q=%22special+dividend%22
&forms=8-K
&ciks=0000320193
Remember the zero-padding gotcha: ciks=320193 returns zero hits with no error; ciks=0000320193 is required.
3. Resolve a hit to the actual filing URL
The API doesn't hand you a document URL directly -- you build it from _id and _source.ciks. Given _id: "0000320193-24-000123:aapl-20240928.htm" and CIK 0000320193:
cik = "320193" # ciks[0] with leading zeros stripped
accession_no_dashes = "000032019324000123" # adsh with dashes removed
filename = "aapl-20240928.htm" # the part after the colon in _id
url = f"https://www.sec.gov/Archives/edgar/data/{cik}/{accession_no_dashes}/{filename}"
# -> https://www.sec.gov/Archives/edgar/data/320193/000032019324000123/aapl-20240928.htm
This URL pattern (www.sec.gov/Archives/edgar/data/{cik}/{accession-no-dashes}/{filename}) is the standard EDGAR document path and works for any filing, not just ones returned by EFTS.
Gotchas We Actually Hit
qis optional. Omit it and you get a plain filter-only listing (e.g. every 8-K in a date range), sorted by filing date withmax_score: nullinstead of by relevance.- Unpadded CIKs fail silently. Always zero-pad to 10 digits; there's no validation error to catch the mistake for you.
- Errors come back as HTTP 200. The result-window overflow and (in our testing) a pre-2001 date range both returned non-2xx-looking JSON bodies with a normal 200 status. Always parse the body and check for an error key.
- No relevance snippets in the API. If your use case needs "here's the sentence that matched," you have to fetch and search the filing document yourself -- the JSON response only gives you metadata, not matched text.
forms=10-Kalso returns10-K/Afilings because the filter runs against the parentroot_formsfield. If you need originals only, filter the response by the exactformfield client-side.- Full-text coverage starts in 2001, not with EDGAR's own 1990s launch. A 10-K from 1998 exists on EDGAR and is fetchable by direct URL, but it will never surface in an EFTS query.
Bottom line: EFTS is a thin, mostly-undocumented wrapper around an Elasticsearch index. It's reliable and fast for what it does, but treat every field name and parameter here as "observed to work," not "guaranteed to keep working" -- the SEC reserves the right to change it, and has no obligation to tell integrators when it does.
Skip the API Wrangling
Alpha Suite already parses EDGAR's Form 4 feed continuously and scores insider transactions by conviction, cluster intensity, and timing. Get the output, not the plumbing.
Get Started with Alpha SuiteReferences
- U.S. Securities and Exchange Commission. EDGAR Full-Text Search. Retrieved from sec.gov/edgar/search.
- U.S. Securities and Exchange Commission. EDGAR Filer Manual. Retrieved from sec.gov/edgar.
- U.S. Securities and Exchange Commission. Developer Resources -- EDGAR APIs. Retrieved from sec.gov/os/webmaster-faq.
- Live endpoint responses from efts.sec.gov/LATEST/search-index, captured 2026-09-08.