DEV Community

Ken-Mutisya
Ken-Mutisya

Posted on

Search Every SEC Filing by Keyword With the Keyless EDGAR Full Text API

If you have ever wanted to find every SEC filing that mentions a phrase, a person, or a legal term, EDGAR has a full text search index that covers every filing since 2001. It is a plain JSON API, no key and no login, and most people never touch it directly.

The endpoint

GET https://efts.sec.gov/LATEST/search-index?q=%22material+weakness%22&forms=10-K&startdt=2026-01-01&enddt=2026-06-30
Enter fullscreen mode Exit fullscreen mode

Parameters:

  • q is the query. Wrap a phrase in double quotes for an exact match.
  • forms is an optional comma list of form types, e.g. 8-K,10-K.
  • startdt and enddt scope the filing date (with dateRange=custom).
  • ciks restricts to one or more companies by CIK number.
  • from is the paging offset.

EDGAR asks for a descriptive User-Agent with contact info. Send one and you are fine:

const res = await fetch(url, {
  headers: {
    Accept: 'application/json',
    'User-Agent': 'YourApp contact@example.com',
  },
});
Enter fullscreen mode Exit fullscreen mode

The response shape

Results come back Elasticsearch style under hits.hits. Each hit has an _id and a _source:

{
  "_id": "0001493152-26-015257:form10-ka.htm",
  "_source": {
    "display_names": ["Where Food Comes From, Inc.  (WFCF)  (CIK 0001360565)"],
    "ciks": ["0001360565"],
    "form": "10-K/A",
    "file_date": "2026-04-06",
    "adsh": "0001493152-26-015257"
  }
}
Enter fullscreen mode Exit fullscreen mode

Two useful tricks:

The display_names string packs the company, ticker, and CIK together. A small regex pulls them apart:

const m = name.match(/^(.*?)\s*\(([^)]+)\)\s*\(CIK\s*\d+\)\s*$/);
const companyName = m ? m[1].trim() : name;
const ticker = m ? m[2].trim() : null;
Enter fullscreen mode Exit fullscreen mode

The _id is accession:filename, and adsh is the accession number. Combine them into a direct link to the document:

const filename = id.slice(id.indexOf(':') + 1);
const nodash = adsh.replace(/-/g, '');
const cik = String(Number(ciks[0])); // strip leading zeros
const url = `https://www.sec.gov/Archives/edgar/data/${cik}/${nodash}/${filename}`;
Enter fullscreen mode Exit fullscreen mode

Paging

Each request returns up to 100 hits. Advance from in steps of 100 until you have what you need. EDGAR caps deep paging at a 10,000 result window per query, so read the reported hits.total.value and stop there.

let from = 0;
while (from < 10000) {
  const data = await getJson(buildUrl(from));
  const hits = data.hits.hits;
  if (hits.length === 0) break;
  // ...process hits...
  if (hits.length < 100) break;
  from += 100;
}
Enter fullscreen mode Exit fullscreen mode

Why it is handy

Full text search across every filer is a different job from watching one company or one form. You can catch risk language like "going concern" or "material weakness" wherever it lands, follow an executive across filers, or track a product mention over time. And because it is a light JSON GET, a run costs almost nothing.

If you want this packaged instead of maintained by hand, I run it as a keyless pay per use actor on Apify. Give it a query and it returns one JSON row per filing with a direct link. The first rows of every run are free so you can check the output.

https://apify.com/scrapemint/sec-filing-fulltext-scraper

Top comments (0)