{"id":47066684,"url":"https://github.com/medelman17/edgar-ts","last_synced_at":"2026-04-02T14:05:20.109Z","repository":{"id":338712254,"uuid":"1158819922","full_name":"medelman17/edgar-ts","owner":"medelman17","description":"TypeScript library for SEC EDGAR filing discovery and contract exhibit acquisition with built-in rate limiting and compliance","archived":false,"fork":false,"pushed_at":"2026-03-24T18:45:28.000Z","size":896,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-25T23:52:18.387Z","etag":null,"topics":["bun","contracts","edgar","edgar-api","exhibits","filings","financial-data","legal","legal-tech","nodejs","rate-limiting","sec","sec-filings","securities","tree-shakeable","typescript","zero-dependencies"],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/medelman17.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-16T00:27:57.000Z","updated_at":"2026-03-24T18:44:58.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/medelman17/edgar-ts","commit_stats":null,"previous_names":["medelman17/edgar-ts"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/medelman17/edgar-ts","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/medelman17%2Fedgar-ts","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/medelman17%2Fedgar-ts/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/medelman17%2Fedgar-ts/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/medelman17%2Fedgar-ts/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/medelman17","download_url":"https://codeload.github.com/medelman17/edgar-ts/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/medelman17%2Fedgar-ts/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31307484,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-02T12:59:32.332Z","status":"ssl_error","status_checked_at":"2026-04-02T12:54:48.875Z","response_time":89,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bun","contracts","edgar","edgar-api","exhibits","filings","financial-data","legal","legal-tech","nodejs","rate-limiting","sec","sec-filings","securities","tree-shakeable","typescript","zero-dependencies"],"created_at":"2026-03-12T05:04:31.050Z","updated_at":"2026-04-02T14:05:20.104Z","avatar_url":"https://github.com/medelman17.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# edgar-ts\n\nTypeScript SEC EDGAR client for filing discovery, company data, XBRL financials, and full-text search.\n\n[![npm version](https://badge.fury.io/js/edgar-ts.svg)](https://www.npmjs.com/package/edgar-ts)\n[![CI](https://github.com/medelman17/edgar-ts/actions/workflows/ci.yml/badge.svg)](https://github.com/medelman17/edgar-ts/actions/workflows/ci.yml)\n\n## Installation\n\n```bash\nnpm install edgar-ts\n# or\npnpm add edgar-ts\n# or\nyarn add edgar-ts\n```\n\n## Requirements\n\n- **Node.js 20.0.0+** or **Bun 1.0+**\n- Zero runtime dependencies\n\n## Features\n\n- **Filing discovery** — Date-bounded search with optional CIK and form-type filtering\n- **Index file discovery** — Bulk discovery across all filers via SEC quarterly index files\n- **Company metadata** — Name, tickers, SIC code, entity type, state of incorporation\n- **Ticker/name lookup** — Resolve tickers or company names to CIKs\n- **Exhibit enumeration** — Normalized exhibit metadata from filing indices\n- **Contract filtering** — Built-in `EX-10*` contract exhibit isolation\n- **Raw download** — Exhibit bytes with MIME hints and SHA-256 integrity hash\n- **Bulk data** — Download SEC nightly archives (submissions.zip, companyfacts.zip)\n- **XBRL financials** — Company Facts, Company Concept, and Frames API access\n- **Full-text search** — Keyword search across all SEC filings via EFTS\n- **SEC-compliant** — Mandatory user-agent, rate limiting (8 req/s default), bounded retries\n- **Deterministic** — Canonical normalization, stable sort, deduplication\n- **Zero dependencies** — No runtime dependencies\n- **Dual runtime** — Node.js and Bun support\n\n## Quick Start\n\n```ts\nimport { EdgarClient } from \"edgar-ts\"\n\nconst client = new EdgarClient({\n  userAgent: \"AcmeLegalBot/1.0 (ops@acme.test)\",\n})\n\n// Look up a company by ticker\nconst matches = await client.lookupCompany(\"AAPL\")\nconst { cik } = matches[0] // \"0000320193\"\n\n// Get company metadata\nconst company = await client.getCompanyInfo(cik)\nconsole.log(company.name, company.sic, company.tickers)\n\n// Discover filings for a specific company\nconst filings = await client.discoverFilings({\n  cik,\n  from: \"2026-01-01\",\n  to: \"2026-12-31\",\n})\n\n// Or discover across ALL filers (uses quarterly index files)\nconst allFilings = await client.discoverFilings({\n  from: \"2026-01-01\",\n  to: \"2026-03-31\",\n  formTypes: [\"10-K\"],\n})\n\n// Get contract exhibits (EX-10*) for a filing\nfor (const filing of filings) {\n  const exhibits = await client.listContractExhibits(filing)\n  for (const exhibit of exhibits) {\n    const { bytes, sha256, sizeBytes } = await client.downloadExhibit(exhibit)\n    // Store bytes and metadata in your downstream system\n  }\n}\n```\n\n## API\n\n### `new EdgarClient(options)`\n\n| Option | Type | Default | Description |\n|--------|------|---------|-------------|\n| `userAgent` | `string` | **required** | Descriptive user-agent for SEC compliance |\n| `maxRequestsPerSecond` | `number` | `8` | Global request rate cap |\n| `timeoutMs` | `number` | `10000` | Per-request timeout |\n| `retries` | `RetryOptions` | `{ maxAttempts: 3, baseDelayMs: 250, maxDelayMs: 4000 }` | Retry configuration |\n| `telemetry` | `TelemetryOptions` | — | Optional request/retry hooks |\n\n### Methods\n\n#### Company Data\n\n| Method | Returns | Description |\n|--------|---------|-------------|\n| `getCompanyInfo(cik)` | `Promise\u003cCompanyInfo\u003e` | Company metadata (name, tickers, SIC, entity type, etc.) |\n| `lookupCompany(query)` | `Promise\u003cCompanyTicker[]\u003e` | Search by ticker (exact) or company name (substring) |\n\n#### Filing Discovery\n\n| Method | Returns | Description |\n|--------|---------|-------------|\n| `discoverFilings(input)` | `Promise\u003cFilingRef[]\u003e` | Date-bounded filing discovery. With CIK: uses Submissions API. Without CIK: uses quarterly index files. |\n\n#### Exhibits\n\n| Method | Returns | Description |\n|--------|---------|-------------|\n| `listExhibits(filing)` | `Promise\u003cExhibitRef[]\u003e` | All exhibits for a filing |\n| `listContractExhibits(filing)` | `Promise\u003cExhibitRef[]\u003e` | Contract exhibits only (EX-10*) |\n| `downloadExhibit(exhibit)` | `Promise\u003cDownloadedExhibit\u003e` | Raw bytes + metadata + SHA-256 |\n\n#### Bulk Data\n\n| Method | Returns | Description |\n|--------|---------|-------------|\n| `downloadSubmissionsBulk()` | `Promise\u003cBulkDownloadResult\u003e` | Download SEC nightly submissions.zip (~2GB) |\n| `downloadCompanyFactsBulk()` | `Promise\u003cBulkDownloadResult\u003e` | Download SEC nightly companyfacts.zip |\n\n#### XBRL Financials\n\n| Method | Returns | Description |\n|--------|---------|-------------|\n| `getCompanyFacts(cik)` | `Promise\u003cCompanyFacts\u003e` | All XBRL facts across all filings |\n| `getCompanyConcept(cik, taxonomy, tag)` | `Promise\u003cCompanyConcept\u003e` | Single concept time series (e.g., us-gaap/Revenue) |\n| `getFrame(taxonomy, tag, unit, period)` | `Promise\u003cFrame\u003e` | Cross-company comparison at a point in time |\n\n#### Full-Text Search\n\n| Method | Returns | Description |\n|--------|---------|-------------|\n| `searchFilings(query)` | `Promise\u003cSearchResult\u003e` | Keyword search with form type, date, and entity filters |\n\n\u003e **Note:** `searchFilings` wraps the SEC's EFTS Elasticsearch API, which is undocumented and could change without notice.\n\n### Examples\n\n#### Company lookup and metadata\n\n```typescript\n// Find a company by ticker\nconst results = await client.lookupCompany(\"MSFT\")\n// [{ cik: \"0000789019\", ticker: \"MSFT\", name: \"MICROSOFT CORP\", exchange: \"Nasdaq\" }]\n\n// Get full company metadata\nconst info = await client.getCompanyInfo(\"789019\")\n// { cik: \"0000789019\", name: \"MICROSOFT CORP\", tickers: [\"MSFT\"], sic: \"7372\", ... }\n```\n\n#### Filing discovery\n\n```typescript\n// Discover filings for a specific company\nconst filings = await client.discoverFilings({\n  cik: \"320193\",\n  from: \"2026-01-01\",\n  to: \"2026-12-31\",\n})\n\n// Discover across all filers (no CIK — uses index files)\nconst allFilings = await client.discoverFilings({\n  from: \"2026-01-01\",\n  to: \"2026-03-31\",\n  formTypes: [\"10-K\", \"10-Q\"],\n})\n\n// Custom form types for a specific company\nconst proxyFilings = await client.discoverFilings({\n  cik: \"320193\",\n  from: \"2026-01-01\",\n  to: \"2026-12-31\",\n  formTypes: [\"DEF 14A\"],\n})\n```\n\n#### XBRL financial data\n\n```typescript\n// Get all XBRL facts for Apple\nconst facts = await client.getCompanyFacts(\"320193\")\n\n// Get Revenue time series\nconst revenue = await client.getCompanyConcept(\"320193\", \"us-gaap\", \"Revenue\")\nfor (const val of revenue.units.USD) {\n  console.log(`FY${val.fy}: $${val.val}`)\n}\n\n// Compare Revenue across all companies for Q1 2024\nconst frame = await client.getFrame(\"us-gaap\", \"Revenue\", \"USD\", \"CY2024Q1\")\nconsole.log(`${frame.data.length} companies reported Revenue`)\n```\n\n#### Full-text search\n\n```typescript\n// Search for filings mentioning \"non-compete\"\nconst results = await client.searchFilings({\n  q: \"non-compete agreement\",\n  formTypes: [\"10-K\", \"8-K\"],\n  from: \"2024-01-01\",\n  to: \"2024-12-31\",\n})\n\nconsole.log(`${results.total} filings found`)\nfor (const hit of results.hits) {\n  console.log(`${hit.entityName} — ${hit.formType} (${hit.fileDate})`)\n}\n```\n\n#### Exhibits and downloads\n\n```typescript\nconst filing = filings[0]\nconst exhibits = await client.listExhibits(filing)\nconst contracts = await client.listContractExhibits(filing)\n\nconst downloaded = await client.downloadExhibit(contracts[0])\nconsole.log(`Downloaded ${downloaded.sizeBytes} bytes`)\nconsole.log(`SHA-256: ${downloaded.sha256}`)\nconsole.log(`MIME type: ${downloaded.mimeType || \"unknown\"}`)\n```\n\n### Type Exports\n\n```typescript\nimport type {\n  EdgarClientOptions,\n  FilingRef,\n  ExhibitRef,\n  DownloadedExhibit,\n  CompanyInfo,\n  CompanyTicker,\n} from \"edgar-ts\"\n```\n\n### Error Handling\n\n```typescript\nimport { EdgarError, ValidationError, TimeoutError } from \"edgar-ts\"\n\ntry {\n  await client.discoverFilings(input)\n} catch (err) {\n  if (err instanceof ValidationError) {\n    // Invalid input parameters\n  } else if (err instanceof TimeoutError) {\n    // Request exceeded timeout\n  }\n}\n```\n\n## Telemetry \u0026 Observability\n\nedgar-ts provides optional telemetry helpers for logging and metrics:\n\n### Console Logger\n\nHuman-readable colored output for development:\n\n```typescript\nimport { EdgarClient } from \"edgar-ts\"\nimport { createConsoleLogger } from \"edgar-ts/telemetry\"\n\nconst client = new EdgarClient({\n  userAgent: \"MyBot/1.0 (contact@example.com)\",\n  telemetry: createConsoleLogger()\n})\n\n// Outputs:\n// → GET https://data.sec.gov/submissions/... [discoverFilings] {abc12345}\n// ← 200 GET https://data.sec.gov/submissions/... 1234ms [discoverFilings]\n// ⟳ Retry 2/3 after 500ms: GET ... (TIMEOUT)\n```\n\n### Metrics Aggregator\n\nTrack request lifecycle and rate limiting metrics:\n\n```typescript\nimport { createMetricsAggregator } from \"edgar-ts/telemetry\"\n\nconst metrics = createMetricsAggregator()\nconst client = new EdgarClient({\n  userAgent: \"MyBot/1.0 (contact@example.com)\",\n  telemetry: metrics\n})\n\n// ... make requests ...\n\nconst snapshot = metrics.getSnapshot()\nconsole.log(snapshot.requestsTotal)       // 42\nconsole.log(snapshot.requestsSuccessful)  // 40\nconsole.log(snapshot.requestsFailed)      // 2\nconsole.log(snapshot.latencyByOperation)  // { discoverFilings: { avg: 250, min: 100, max: 1200 } }\nconsole.log(snapshot.rateLimitedRequests)  // 0\n```\n\n### Structured Logger\n\nJSON Lines output for log aggregation systems:\n\n```typescript\nimport { createStructuredLogger } from \"edgar-ts/telemetry\"\n\nconst client = new EdgarClient({\n  userAgent: \"MyBot/1.0 (contact@example.com)\",\n  telemetry: createStructuredLogger()\n})\n\n// Outputs JSON Lines:\n// {\"event\":\"request.start\",\"url\":\"...\",\"operation\":\"discoverFilings\",...}\n// {\"event\":\"request.end\",\"statusCode\":200,\"durationMs\":1234,...}\n// {\"event\":\"request.retry\",\"attempt\":2,\"error\":\"TIMEOUT\",...}\n```\n\n### Custom Telemetry\n\nImplement your own hooks for integration with observability platforms:\n\n```typescript\nconst client = new EdgarClient({\n  userAgent: \"MyBot/1.0 (contact@example.com)\",\n  telemetry: {\n    onRequestStart: (event) =\u003e {\n      console.log(`Starting ${event.operation} (${event.requestId})`)\n    },\n    onRequestEnd: (event) =\u003e {\n      console.log(`Completed in ${event.durationMs}ms`)\n    },\n    onRetry: (event) =\u003e {\n      console.log(`Retry ${event.attempt}/${event.maxAttempts}`)\n    }\n  }\n})\n```\n\n**Telemetry Event Fields:**\n- `requestId` - Unique ID for request correlation\n- `operation` - EdgarClient method (discoverFilings, listExhibits, getCompanyInfo, searchFilings, etc.)\n- `endpointClass` - SEC endpoint type (submissions, archive, full-index, xbrl, efts, files, bulk-data)\n- `runtime` - Detected runtime (node or bun)\n- `timestamp` - Event timestamp (milliseconds)\n- `url`, `method`, `statusCode`, `durationMs` - Request details\n\n## Development\n\n```bash\npnpm install        # Install dependencies\npnpm test:run       # Run tests\npnpm build          # Build (ESM + CJS)\npnpm lint           # Lint\npnpm typecheck      # Type check\n```\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmedelman17%2Fedgar-ts","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmedelman17%2Fedgar-ts","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmedelman17%2Fedgar-ts/lists"}