sec-cli

module
v1.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 12, 2026 License: MIT

README

sec-cli

Extract structured data from SEC EDGAR filings for LLM consumption.

Status: v1.0, all 14 phases complete. sec-cli get, sec-cli diff, and the full parser pipeline (iXBRL fact stream, presentation linkbase, section extraction, three-layer diff, SQLite cache) are implemented and tested. See Roadmap.


Demo

# Fetch Apple's latest 10-K Risk Factors as clean Markdown
sec-cli get AAPL --section "Risk Factors" --output md | head -40

# Year-over-year diff of Nvidia's 10-K (2023 → 2024)
sec-cli diff NVDA --from 2023 --to 2024 --output md

What it does

sec-cli is a fast CLI (and importable Go library) that turns SEC EDGAR filings into clean, structured output you can pipe directly into a language model or analysis pipeline, no API key, no paid service, no runtime dependencies.

  • Fetches any 10-K, 10-Q, or 8-K by ticker + year with no API key required
  • Detects filing format (iXBRL, PartialIXBRL, PlainHTML, PlainText) and refuses non-iXBRL filings cleanly with a v1.1 pointer, never silently parses bad data
  • Extracts financial tables using the iXBRL fact stream + presentation linkbase as the authoritative source, the same data FactSet and Bloomberg read, not HTML table walking
  • Extracts free-text sections (Risk Factors, MD&A, Business Overview, 8-K items) using distribution-relative heading detection, structure derived from each document's own inline CSS, not fixed font-size thresholds
  • Outputs versioned, schema-stable JSON or Markdown with a calibrated confidence signal per table (schema_version: "1.0.0")
  • Diffs two years of the same filing at structural, lexical, or semantic granularity to surface what substantively changed, not every comma rephrasing

v1.0 supports iXBRL-era filings only (2019+ for large accelerated filers, 2021+ for all filers). Pre-iXBRL filings are detected and refused with a clear error and a v1.1 pointer, never parsed badly.


Requirements

  • Go 1.22+
  • SEC_CLI_USER_AGENT environment variable. EDGAR's Fair Access policy requires every request to identify the caller; without it EDGAR returns HTTP 403.
# macOS / Linux
export SEC_CLI_USER_AGENT="Your Name your-email@example.com"
# Windows PowerShell (current session)
$env:SEC_CLI_USER_AGENT = "Your Name your-email@example.com"
# persist for all future terminals:
[Environment]::SetEnvironmentVariable("SEC_CLI_USER_AGENT", "Your Name your-email@example.com", "User")

Build & run

# build binary to ./bin/
go build -o bin/sec-cli ./cmd/sec-cli        # bin/sec-cli.exe on Windows

# or run straight from source
go run ./cmd/sec-cli get AAPL

Available make targets:

make build       # compile binary to ./bin/
make test        # go test ./...
make accuracy    # run accuracy harness against internal corpus
make lint        # staticcheck + go vet

Commands

get, fetch, parse, and render a filing
sec-cli get AAPL                                   # latest 10-K, JSON
sec-cli get AAPL --year 2023 --output md           # 2023 10-K as Markdown
sec-cli get AAPL --section "Risk Factors"          # single section only
sec-cli get AAPL --section 1A                      # by item id
sec-cli get AAPL --type 10-Q --output text         # latest 10-Q as plain text
sec-cli get AAPL --accession 0000320193-24-000123  # pin to exact filing/amendment
sec-cli get AAPL --no-cache                        # bypass local SQLite cache

Flags: --type/-t (default 10-K), --year, --accession, --output/-o (json, md, text), --section/-s, --no-cache.

--section accepts an item ID (1A) or a title substring (risk). On a miss, exits 1 and lists all available sections. --accession overrides --year and handles amendments (10-K/A, 10-Q/A).

Every JSON response carries schema_version: "1.0.0". Every Markdown/text response opens with <!-- sec-cli v0.1.0 schema 1.0.0 -->.


diff, compare two filings year-over-year
sec-cli diff AAPL --from 2022 --to 2024                   # structural diff, JSON
sec-cli diff AAPL --from 2022 --to 2024 --output md       # Markdown report
sec-cli diff AAPL --from 2022 --to 2024 --layer lexical   # word-level diff
sec-cli diff MSFT --from 2021 --to 2023 --section "1A"    # Risk Factors only

Flags: --type/-t, --from, --to, --layer (structural, lexical, semantic), --output/-o (json, md).

Three diff layers:

Layer What it shows Status
structural (default) Subsection-grain: added / removed / modified / unchanged
lexical Word-level diffs within modified paragraphs, annotated [+..+] / [-..-]
semantic Embedding-distance ranking of modified paragraphs, surfaces substantive change, filters year-roll noise planned v1.0.1

Financial tables diff by aligning rows on GAAP concept (us-gaap:Revenues matches whether labeled "Net revenues" or "Net sales") and columns by period. Concept-based alignment is free from the iXBRL fact-stream path.

Both endpoints must be iXBRL filings, diffs that span the iXBRL boundary fail fast on the pre-iXBRL side, before the iXBRL filing is fetched.


sections, list detected item sections
sec-cli sections AAPL
sec-cli sections AAPL --year 2023 --type 10-Q
sec-cli sections MSFT --type 8-K

Lists all detected Item sections with resolved titles. Useful to know what --section accepts before calling get or diff.

8-K items use the dotted sub-numbering scheme (Item 1.01, Item 5.02); 10-K/10-Q items use the standard scheme (Item 1A, Item 7). Both are classified correctly.


accuracy, run the extraction harness
sec-cli accuracy internal/accuracy/testdata/corpus
# or via make:
make accuracy

Scores the pipeline against a corpus of hand-verified filings. Reports statement cell accuracy and section coverage per filing, split by confidence bucket. All fixtures are hermetic, no network calls.

Current corpus results (synthetic fixtures):

filing     ticker  format         exp   →got      stmt-acc   sect-cov
----------------------------------------------------------------------------
acme       ACME    IXBRL          medium→medium     100.0%     100.0%
globex     GLBX    IXBRL          high  →high       100.0%     100.0%
initech    INTH    IXBRL          medium→medium     100.0%     100.0%
umbrella   UMBR    PartialIXBRL   high  →high       100.0%     100.0%
----------------------------------------------------------------------------
overall: statement accuracy 100.0% (32/32), section coverage 100.0% (13/13)

confidence calibration: high 100.0% · medium 100.0%

The current corpus uses synthetic hermetic fixtures. Real-filing corpus expansion (AAPL, MSFT, JPM) is planned for v1.0.1, those numbers will be the honest headline once that corpus is in.


cache, inspect or clear the local SQLite cache
sec-cli cache path          # print the cache file location
sec-cli cache clear         # delete all cached raw bytes and parsed documents

Cache lives at %LOCALAPPDATA%\sec-cli\cache.db on Windows or ~/.cache/sec-cli/cache.db on Linux/macOS. Two-tier design:

Tier Key Invalidation
Raw (cik, accession) Never, EDGAR filings are immutable; amendments get new accession numbers
Parsed (accession, parser_version, schema_version) Automatic when either version bumps, raw bytes are reused, no re-fetch

Low-level commands

These pre-get commands expose individual pipeline stages and are useful for debugging.

sec-cli latest AAPL                            # accession of latest 10-K
sec-cli detect AAPL --year 2024                # IXBRL
sec-cli detect AAPL --year 2018                # PlainHTML → refused cleanly in v1.0
sec-cli fetch AAPL --year 2024                 # first 200 bytes of primary document
sec-cli facts AAPL --year 2024 --concept RevenueFromContract
sec-cli table AAPL --year 2024 --statement income
sec-cli table AAPL --year 2024 --layout --index 15   # narrative MD&A table by position
sec-cli text AAPL --year 2024 --section "Risk Factors"

Tests

The suite is hermetic, recorded fixtures and a fake http.RoundTripper mean no network calls and no SEC_CLI_USER_AGENT needed:

go test ./...                            # all packages
go test ./internal/diff/... -v           # diff + word-level lexical tests
go test ./internal/sections/... -v       # section classification (10-K + 8-K items)
go test ./internal/accuracy/... -v       # accuracy harness against synthetic corpus
go test ./internal/cache/... -v          # cache + schema-version migration

Windows note: the suite uses a fake http.RoundTripper rather than httptest.Server, which hangs on some Windows/Go builds due to a cancelIO defect in the standard library's listener teardown. go test -race requires a C toolchain (cgo); it runs in CI.


Python wrapper

A thin Python wrapper (pip install seccli) drives the binary via subprocess and deserializes canonical JSON into typed dataclasses. The JSON schema is the only contract, the Go and Python views never diverge.

import seccli

doc = seccli.get("AAPL")               # latest 10-K → Document
doc.metadata.company                    # "Apple Inc."
doc.tables[0].rows[0].values           # [391035000000, 383285000000, ...]

changes = seccli.diff("AAPL", frm=2022, to=2024)
for s in changes.sections:
    print(s.item, s.status)            # "1A", "modified"

# search() is planned for v1.1
seccli.search("revenue recognition")  # raises SecCliError (not yet implemented)

See python/README.md for full installation and API surface.


Distribution

Go install:

go install github.com/kritidutta01/sec-cli/cmd/sec-cli@latest

Homebrew (macOS / Linux):

brew tap kritidutta01/sec-cli
brew install sec-cli

Cross-compiled binaries for darwin/amd64, darwin/arm64, linux/amd64, and linux/arm64 are built via GoReleaser on GitHub Actions on each tagged release.


Architecture

CLI (cmd/)
  └─ edgar client       rate-limited (9 req/sec), User-Agent enforced
      └─ format router  classifies IXBRL / PartialIXBRL / PlainHTML / PlainText / Unknown
          └─ iXBRL parser
              ├─ fact-stream extractor   PRIMARY, financial statements
              │   fact stream + presentation linkbase → rows × columns × periods
              ├─ layout extractor        FALLBACK, untagged narrative tables
              │   5-phase HTML pipeline → rows × columns (lower accuracy ceiling)
              └─ section extractor       10-K/10-Q item patterns + 8-K dotted items
                  └─ normalized model    Document → Metadata, []Section, []Table
                      └─ cache           SQLite two-tier (raw + parsed)
                          └─ renderer    JSON / Markdown / text, schema-stamped

Pre-iXBRL paths (PlainHTML, PlainText) are detected and refused in v1.0. The layout extractor exists for narrative tables within iXBRL filings; whole pre-iXBRL support is v1.1.


Roadmap

Phase Scope Status
0 Scaffold (module, CI, Makefile) ✅ Done
1 EDGAR HTTP client (User-Agent, rate limit) ✅ Done
2 Submissions metadata & CIK lookup ✅ Done
3 Primary-document fetch ✅ Done
4 Format router (iXBRL / HTML / text detection) ✅ Done
5 iXBRL parser: fact stream & contexts ✅ Done
6 Presentation linkbase & table projection ✅ Done
7 Layout fallback for narrative tables ✅ Done
8 Section extraction: 10-K + 8-K item pattern tables ✅ Done
9 Normalized model + JSON / Markdown / text output ✅ Done
10 SQLite cache (two-tier, schema-version keyed) ✅ Done
11 get command, fetch → parse → render pipeline ✅ Done
12 diff command, structural + lexical layers; semantic stub ✅ Done
13 Accuracy harness + hermetic corpus ✅ Done
14 Python wrapper & Homebrew distribution template ✅ Done

v1.0.1 (next): --layer semantic embedding-distance diff; real-filing accuracy corpus (AAPL, MSFT, JPM).
v1.1: Whole plain-HTML filings (2010–2018 era). The layout extractor built for v1.0 narrative tables gets wired to handle whole pre-iXBRL filings.


License

MIT

Directories

Path Synopsis
cmd
sec-cli command
internal
accuracy
Package accuracy is the corpus scoring harness: it runs the full Phase 11 pipeline against a set of recorded filings (hermetic — the corpus ships the bytes, nothing touches the network) and scores the assembled documents against hand-verified baselines.
Package accuracy is the corpus scoring harness: it runs the full Phase 11 pipeline against a set of recorded filings (hermetic — the corpus ships the bytes, nothing touches the network) and scores the assembled documents against hand-verified baselines.
cache
Package cache is a two-layer, pure-Go SQLite store that makes repeated extraction cheap and keeps sec-cli a good EDGAR citizen by not re-fetching bytes it already has.
Package cache is a two-layer, pure-Go SQLite store that makes repeated extraction cheap and keeps sec-cli a good EDGAR citizen by not re-fetching bytes it already has.
diff
Package diff compares two normalized documents and emits a structured change set: numeric statement deltas matched by concept (labels drift, concepts are stable) and narrative changes matched by item id and diffed at paragraph granularity.
Package diff compares two normalized documents and emits a structured change set: numeric statement deltas matched by concept (labels drift, concepts are stable) and narrative changes matched by item id and diffed at paragraph granularity.
edgar
Package edgar is the HTTP client layer for SEC EDGAR.
Package edgar is the HTTP client layer for SEC EDGAR.
ixbrl
Package ixbrl parses inline-XBRL filings: the fact stream and its contexts (this file's siblings), and — in later phases — the presentation-linkbase projection, layout tables, and free-text sections.
Package ixbrl parses inline-XBRL filings: the fact stream and its contexts (this file's siblings), and — in later phases — the presentation-linkbase projection, layout tables, and free-text sections.
model
Package model is the normalized, source-agnostic output contract for a filing: the Document and the value types every other package produces, renders, caches, diffs, or scores.
Package model is the normalized, source-agnostic output contract for a filing: the Document and the value types every other package produces, renders, caches, diffs, or scores.
pipeline
Package pipeline is the conductor: the one place that knows the full ticker → fetch → detect → parse → project → partition → assemble sequence and returns a normalized *model.Document.
Package pipeline is the conductor: the one place that knows the full ticker → fetch → detect → parse → project → partition → assemble sequence and returns a normalized *model.Document.
render
Package render turns a model.Document into one of three serializations behind a single Renderer interface: canonical JSON (the schema-stable contract the cache and Python wrapper read), Markdown (the LLM- and human-facing view), and plain text.
Package render turns a model.Document into one of three serializations behind a single Renderer interface: canonical JSON (the schema-stable contract the cache and Python wrapper read), Markdown (the LLM- and human-facing view), and plain text.
router
Package router classifies a filing's raw bytes into one of the EDGAR format eras.
Package router classifies a filing's raw bytes into one of the EDGAR format eras.
sections
Package sections partitions a filing into its named 10-K item sections and renders clean free text for each, with all inline-XBRL scaffolding removed.
Package sections partitions a filing into its named 10-K item sections and renders clean free text for each, with all inline-XBRL scaffolding removed.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL