GuidesFINANCE APIS

EDGAR full-text search API from JavaScript

EDGAR full-text search has no documented API, but SEC's search page calls https://efts.sec.gov/LATEST/search-index, which returns JSON for any query across filings since 2001. On 28 October 2026 that endpoint sent CORS headers only to https://www.sec.gov, so fetch() from your own site is blocked; through https://proxy.cors.dev/ it returned 200 with up to 100 hits per request. The free tier needs no signup and handles GET requests at about 600 a minute per IP.

The endpoint behind the search page

SEC's full-text search page at https://www.sec.gov/edgar/search/ runs on a JSON endpoint: its JavaScript sends GET requests to https://efts.sec.gov/LATEST/search-index with the form fields as query parameters. The index covers the full text of all EDGAR filings submitted electronically since 2001, including attachments such as exhibits.

SEC documents the search page and its query syntax, but not this endpoint as an API, so parameters and fields can change without notice. The documented APIs live on data.sec.gov, covered in the SEC EDGAR API guide.

On 28 October 2026, efts.sec.gov sent Access-Control-Allow-Origin only to https://www.sec.gov, the search page's own origin. Requests from https://example.com and http://localhost:5173 got no CORS header, so the browser blocks the response. Through https://proxy.cors.dev/ the same URLs returned 200 with identical JSON: 39 KB for q="cors"&forms=10-K (66 hits).

Parameters that work

The search page rewrites some of its own URL fields before calling the endpoint, so copying them straight from the page URL does not always work. These are the results of sending each parameter directly:

EDGAR full-text search parameters, checked October 2026
ParameterExampleEffectResult
q"material weakness"Phrase search in all text; words are ANDedWorks
q operatorstariffs NEAR(5) chinaOR, NOT, -term, suffix *, NEAR(n)Works
forms10-K,8-KComma list; 10-K also matches 10-K/AWorks
startdt, enddt2026-10-01Filing date range, YYYY-MM-DDWorks
ciks0000320193Filer CIK, 10 digits; 320193 finds nothingWorks
entityNameAppleMatches words in the filer nameWorks
locationCodesCABusiness address stateWorks
from100Offset, in steps of 100, up to 9,900Works
page2The search page turns it into fromIgnored
size10Pages are always 100 hitsIgnored
dateRange30dPresets do nothing; send startdt and enddtIgnored
keysTypedappleCompany suggestions with CIK and tickerWorks

Search is case-insensitive and words are combined with AND. Quotes match an exact phrase, a capitalised OR needs one of the terms, and NOT or a leading hyphen excludes one; a trailing * matches word stems (not inside phrases), and NEAR(n) finds terms within n words, 10 by default. Counts for 10-K filings filed 1 January to 28 October 2026:

  • tariffs NEAR(5) china: 698
  • tariffs -china: 1,711
  • tariff*: 4,540
  • "supply chain" OR semiconductors: 4,469

Leave out q and send only forms plus dates to list filings newest first: 391 8-K filings dated 28 October 2026.

Paging past 100 results

Every request returns up to 100 hits, whatever you pass as size, so page with from=0, from=100 and so on up to from=9900. Totals stop at 10,000: a broad query such as tariffs reports value: 10000 with relation: "gte".

Asking for from=10000 still returns HTTP 200, but the body is {"errorType":"ResponseError","errorMessage":"search_phase_execution_exception: ... Result window is too large, from + size must be less than or equal to: [10000] ..."} instead of hits. Check for hits before reading it, and split bigger result sets by date range or form. This loop stops at a short page or at your maximum:

Page through up to 10,000 hits
// Runs in a module script or the DevTools console
const proxy = 'https://proxy.cors.dev/';

async function allHits(params, maxResults = 1000) {
  const hits = [];
  // Pages are fixed at 100 hits; from + 100 may not pass 10,000
  for (let from = 0; from < Math.min(maxResults, 10000); from += 100) {
    const query = new URLSearchParams({ ...params, from: String(from) });
    const response = await fetch(proxy + 'https://efts.sec.gov/LATEST/search-index?' + query);
    if (!response.ok) throw new Error(`Search failed: ${response.headers.get('X-Cors-Error') ?? response.status}`);

    const data = await response.json();
    if (!data.hits) throw new Error(data.errorMessage ?? 'Unexpected response');
    hits.push(...data.hits.hits);
    if (data.hits.hits.length < 100) break;
  }
  return hits;
}

const hits = await allHits({ q: '"going concern"', forms: '10-K', startdt: '2026-01-01', enddt: '2026-10-28' }, 500);
console.log(hits.length, hits.map((hit) => hit._source.display_names[0]).slice(0, 3));

Limits and which cors.dev tier fits

SEC's fair access rules cover every SEC.gov host: no more than 10 requests per second per user, and automated tools should declare a User-Agent with a company name and contact email. A sequential paging loop like the one above stays well under that.

  • cors.dev free: prefix the URL with https://proxy.cors.dev/. No signup, no key, GET and HEAD only, about 600 requests a minute per IP, no daily quota and responses up to 6 MiB; a full page of 100 hits was 55 to 68 KB. Nothing is cached.
  • cors.dev Pro: $5 a month for 500,000 requests, with a 7-day trial of 1,000 requests and no card. It allows 3,000 requests a minute per account and 100 in flight, and adds an optional GET cache with X-Cors-Cache: 60 (1 to 300 seconds; cache hits still count as requests), which suits a public page where many visitors load the same saved search. Start the trial.

Good questions.

Is there an official API for EDGAR full-text search?

No. SEC documents the search page and its query syntax, and the page itself calls https://efts.sec.gov/LATEST/search-index, which returns JSON. That endpoint is not documented as an API, so it can change without notice.

Why does my CIK filter return zero filings?

The ciks parameter needs the 10-digit CIK with leading zeros: ciks=0000320193 returned 140 hits for "supply chain", ciks=320193 returned none. Document URLs need the reverse, the CIK without leading zeros.

How far back does EDGAR full-text search go?

It covers filings submitted electronically since 2001, including exhibits and other attachments. For older filings, list a company's filings with the submissions API on data.sec.gov, which reaches back to 1994 for Apple.

Can I get more than 100 results from EDGAR full-text search?

Yes, by paging with from in steps of 100, up to from=9900. The endpoint never returns more than 10,000 hits for one query, so split larger searches by date range or form type.

Why do Form 4 links from full-text search open as raw XML?

Form 4 filings are XML documents. When a hit has an xsl value such as xslF345X06, insert it and a slash before the file name in the /Archives/edgar/data/ URL to get the rendered page.

How do I turn a company name into a CIK with the same endpoint?

Send keysTyped=apple instead of q, through the proxy. It returned 10 suggestions out of 230 matches, each with the CIK in _id, "Apple Inc. (AAPL)" in _source.entity and the ticker in _source.tickers.