Find recent SEC filings from a ticker with JavaScript

Build a two-request Node.js workflow: resolve a ticker to a CIK, filter recent SEC filings by form, and retain the original document links and source metadata.

WHAT YOU WILL BUILD

Resolve an exact ticker match from the company directory, then pass its CIK to the filings endpoint. The example prints recent 10-K metadata and original SEC document links without pretending to retrieve the full historical archive.

Requirements: Node.js 22 or later; no extra packages. An API key from your dashboard, stored in the SEC_API_KEY environment variable.

Run the workflow

Save this as workflow.mjs and run node workflow.mjs. The production base URL is used by default. Keep credentials out of source control.

// Node.js 22+. Set SEC_API_KEY before running.
const key = process.env.SEC_API_KEY;
if (!key) throw new Error("Set SEC_API_KEY first");
const base = process.env.SEC_API_BASE_URL || "https://sec-api.dev";
async function request(path) {
  const response = await fetch(base + path, {
    headers: { Authorization: "Bearer " + key }
  });
  if (!response.ok) {
    throw new Error("HTTP " + response.status + ": " + await response.text());
  }
  return response.json();
}

const ticker = "AAPL";
const lookup = await request("/api/v1/companies?" + new URLSearchParams({
  query: ticker, limit: "20"
}));
const company = lookup.data.find(row => row.ticker.toUpperCase() === ticker);
if (!company) throw new Error("No exact ticker match in the current directory");

const filings = await request("/api/v1/filings?" + new URLSearchParams({
  cik: company.cik, form: "10-K", limit: "5"
}));
console.log(JSON.stringify({
  company,
  filings: filings.data.map(row => ({
    accessionNumber: row.accessionNumber,
    filingDate: row.filingDate,
    reportDate: row.reportDate,
    documentUrl: row.documentUrl
  })),
  mappingSource: lookup.meta,
  filingSource: filings.meta,
  pagination: filings.pagination
}, null, 2));

Choose an exact match before making the second request

The first response is a search result, not a single authoritative answer to every ticker-like input. The example normalizes its own requested ticker and checks the returned ticker explicitly. If there is no exact match, it stops instead of silently using a company whose name happens to contain the same letters. This is a useful boundary for a ticker-specific application. For an interactive name search, show the candidate list to your user and let them choose the entity instead. Preserve the returned padded CIK as a string for the next request.

Filter before presenting a filing list

The second call asks for the selected issuer’s 10-K records. A 10-K/A amendment is a different exact form value and is not automatically included. This tutorial deliberately prints the report date beside the filing date so the two are not confused. It also retains the accession number: the document URL is convenient for a reader, while the accession remains useful when reconciling stored records. A null document URL should stay visibly unavailable rather than being replaced with a guessed path.

Show the response boundary

The example retrieves one page of the default recent dataset. Inspect pagination and the historyCoverage field instead of labeling five returned rows as the company’s entire reporting history. If you add pagination, follow nextOffset until null and deduplicate accession numbers. A source refresh can shift offsets between requests. Choose a history retrieval mode only after reviewing its documented scope and limits; the two-call example intentionally keeps the default behavior so its result is easy to understand.

Turn the output into a useful interface

Display the company name, form, filing date, and a link to the original document. Keep mappingSource and filingSource in your application’s diagnostics, because the ticker lookup and submissions list are separate source resources with separate retrieval times. Show a stale notice when the source metadata says the cached response is stale. Catch non-success status codes before rendering rows. A failed request is not an empty filing list, and the absence of one form from the current result should not be presented as proof that the issuer never filed it.

Sources and further reading

The underlying SEC dataset behavior is documented in the official SEC API reference and SEC technical specifications. Response-field names shown here belong to the sec-api.dev endpoint contract. The scripts print actual results and do not embed fabricated filing or financial data.

All tutorials ←