npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

sec-filings-api

v0.3.0

Published

SEC EDGAR filings, XBRL financials, full-text search and Form 4 insider transactions as clean JSON. Works with no API key and no hosted service: talks straight to EDGAR. Form 4 transaction codes are classified, so tax withholdings (F) and option exercises

Readme

sec-filings-api

SEC EDGAR filings, XBRL financials, full-text search, and Form 4 insider transactions with the transaction codes actually classified — as clean JSON.

  • No API key. No account. No hosted service required. By default it talks straight to SEC EDGAR from your process.
  • Zero dependencies. Nothing enters your lockfile.
  • Node 18+, browsers, Cloudflare Workers, Deno, Bun. Full TypeScript types.
npm install sec-filings-api
import { FilingsClient } from "sec-filings-api";

// The SEC requires a declared User-Agent. Use your own app name and contact.
const sec = new FilingsClient({
  userAgent: "MyApp (contact: [email protected])",
});

const co = await sec.company("AAPL");
console.log(co.name, co.cik);           // Apple Inc. 0000320193

const fin = await sec.financials("MSFT");
console.log(fin.financials.revenue.value); // 331839000000

const ins = await sec.insiders("MSFT", { limit: 10, openMarketOnly: true });
console.log(ins.summary.openMarketSellValueUsd);

Command line

The package ships a sec-filings binary, so it is usable without writing any code. No install needed to try it:

export SEC_USER_AGENT="MyApp (contact: [email protected])"

npx sec-filings company AAPL
npx sec-filings filings NVDA --form 10-K --limit 3
npx sec-filings financials MSFT
npx sec-filings insiders MSFT --limit 10 --open-market
npx sec-filings search "material weakness" --forms 8-K --limit 5
MICROSOFT CORP  (MSFT)   3 filing(s), 0 unreadable

  open-market BUY  (P):        $0
  open-market SELL (S):        $43,388,532.40
  non-discretionary (F/M/...): $12,609,373.27

DATE        OWNER             CODE  MEANING                                            SHARES
----------  ----------------  ----  -------------------------------------------------  ----------
2026-09-01  Nadella Satya     S *   Open-market or private sale                        24,723
2026-08-31  SMITH BRADFORD L  A     Grant, award or other acquisition from the issuer  27,688
2026-08-31  SMITH BRADFORD L  F     Shares withheld by issuer to pay tax on vesting     17,036

* = open-market (discretionary) trade. Unmarked rows are not decisions to buy or sell.

Add --json to any command for raw JSON instead of the table, so the same binary works interactively and in a pipe:

npx sec-filings insiders TSLA --limit 25 --json | jq '.summary'

--ua overrides $SEC_USER_AGENT for a single call. Errors go to stderr with a non-zero exit code; missing or generic User-Agents fail with an explanation rather than a stack trace.


The part that matters: Form 4 transaction codes

EDGAR publishes Form 4 only as raw XML. There is no JSON representation anywhere in EDGAR, so every consumer either pays a vendor or writes the parser.

But parsing is the easy half. Classifying the transaction code is the half that is usually wrong. "Insider sold shares" is the headline most free trackers show, and it frequently is not a sale at all:

| Code | Meaning | Discretionary trade? | |------|---------|----------------------| | P | Open-market or private purchase | ✅ yes | | S | Open-market or private sale | ✅ yes | | F | Shares withheld by the issuer to pay tax on vesting | ❌ no | | M | Exercise or conversion of a derivative held | ❌ no | | A | Grant or award from the issuer | ❌ no | | G | Bona fide gift | ❌ no |

F is a payroll mechanic. M is an option exercise. Neither is a decision to sell into the market, and adding them to "insider selling" inflates it.

Every transaction carries openMarket, which is true for P and S only.

const { summary } = await sec.insiders("MSFT", { limit: 25 });
summary.openMarketBuyValueUsd;      // code P only
summary.openMarketSellValueUsd;     // code S only
summary.nonDiscretionaryValueUsd;   // F, M, A, G, ...

How much does the distinction matter?

Measured across 476 real transactions from 8 large issuers:

| | USD | |---|---| | Genuine open-market selling (S) | $512.18M | | Non-discretionary disposals (F, M, …) | $160.61M | | Naive total reported as "insider selling" | $672.79M |

Conflating them overstates open-market selling by 31% in aggregate, and by 2.35× for a single issuer in that sample. All 8 codes observed were recognised; none fell through unclassified.

Every row also exposes rule10b5_1Plan, so trades pre-scheduled under a 10b5-1 plan can be separated from fresh discretionary decisions.


API

All methods return promises. Both modes return identical shapes.

company(ticker)

Registrant profile — CIK, SIC, entity type, exchanges, state of incorporation, fiscal year end — plus the 5 most recent filings with resolved document URLs.

filings(ticker, { form, limit })

Recent filings, optionally filtered by form type. limit caps at 100.

await sec.filings("NVDA", { form: "10-K", limit: 3 });

financials(ticker)

Latest annual XBRL values for 8 headline concepts: revenue, netIncome, assets, liabilities, stockholdersEquity, cashAndEquivalents, operatingCashFlow, eps.

Several concepts have historical tag aliases. Each result reports which us-gaap tag actually answered, or an explicit reason when nothing did — so a missing value never silently reads as zero.

const f = await sec.financials("AAPL");
f.financials.revenue.value; // number
f.financials.revenue.tag;   // "RevenueFromContractWithCustomerExcludingAssessedTax"
f.financials.revenue.end;   // period end date

insiders(ticker, { limit, openMarketOnly })

Parsed Form 4 filings. limit caps at 25. See above.

A filing that cannot be read appears as { accessionNumber, unavailable } and is counted in unavailableCount — visible, rather than silently dropped from the totals.

search(q, { forms, from, to, limit })

Full-text search across filing documents.

await sec.search('"climate risk"', { forms: "10-K", limit: 10 });

Coverage caveat, stated in every response: EDGAR's full-text index covers 2001 onward, not the full 1993+ archive. Earlier filings exist but are not full-text searchable.


Direct mode vs hosted mode

// default: straight to EDGAR. No key, no service, no cost.
new FilingsClient({ userAgent: "MyApp (contact: [email protected])" });

// optional: through a Filings API deployment.
new FilingsClient({ mode: "hosted", baseUrl: "https://...", apiKey: "..." });

Identical response shapes, and the parsing/classification code is literally the same module in both cases — so switching is a constructor change and nothing else.

When direct mode is enough: scripts, notebooks, batch jobs, side projects, anything where you control request volume.

When hosted mode earns its keep: the SEC's fair-access limit is roughly 10 requests/second, and breaching it IP-bans you for 10 minutes. There is also no uptime guarantee on the government endpoint. Hosted mode puts an edge cache in front of both problems, which matters once real users are hitting your product rather than you hitting a script.


About the User-Agent requirement

Direct mode requires userAgent, and there is deliberately no default.

The SEC's fair-access policy requires a declared User-Agent identifying the requester; a generic one is rejected outright (verified live — this is not a documentation nicety). A shared default baked into this package would make every installation look like one identity to the SEC and get the whole user base throttled together. So you declare your own.

Format: "YourAppName (contact: [email protected])".


Data, licensing and honesty

The underlying data is SEC EDGAR, public domain (17 U.S.C. §105). This package claims no ownership of it and does not pretend it is proprietary — you can fetch all of it yourself for free.

What this package provides is the part that costs time: CIK resolution, the Form 4 XML parser, transaction-code classification, XBRL tag-alias handling, URL resolution, and honest error semantics.

Not affiliated with or endorsed by the U.S. Securities and Exchange Commission.

Not investment advice. Insider transaction data is filed by registrants and may be amended; 4/A amendments appear as separate filings.

MIT licensed.


Changelog

0.3.0

  • New: sec-filings CLI. Every method is reachable from the command line, with a table view by default and --json for pipes. npx sec-filings ... needs no install.
  • New: formType on filing objects. It is an alias of form and always identical to it. insiders().filings[].formType already used that name for the same concept, and the inconsistency was a trap. form remains the primary field and is not deprecated; nothing was renamed, so this is additive and breaks nothing.

0.2.0

  • Dual mode: direct (straight to EDGAR, no hosted service) and hosted.
  • Form 4 parsing with transaction-code classification.
  • Full-text search, XBRL financials, filings, company profile.