# SEC EDGAR Fundamentals Scraper (`scrapyx/sec-edgar-fundamentals-scraper`) Actor

Company profiles, COMPLETE filing history and XBRL financials from SEC EDGAR by ticker or CIK. Recovers the older filings capped out of SEC's main document (up to 79% of history) and drops restatement duplicates so each period has one canonical value.

- **URL**: https://apify.com/scrapyx/sec-edgar-fundamentals-scraper.md
- **Developed by:** [Ibnu Adzim](https://apify.com/scrapyx) (community)
- **Categories:** Business, Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.40 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## SEC EDGAR Fundamentals Scraper

Company profiles, **complete** filing history and XBRL financials from the
U.S. Securities and Exchange Commission's EDGAR system — by ticker or CIK.

Public U.S. government data. No API key, no login, no WAF, no proxy required.

***

### Why this one is different

Most EDGAR clients read SEC's `filings.recent` block and stop there. **That
block is capped at about 1,000 filings**, and everything older lives in
separate archive files that must be fetched explicitly. Measured on real
companies:

| Company | Filings in `recent` | Filings in archive | Silently lost |
|---|---|---|---|
| Apple | 1,001 (back to 2015) | 1,240 (back to 1994) | **55%** |
| Berkshire Hathaway | 1,001 (back to 2017) | 1,394 (back to 1998) | **58%** |
| General Electric | 1,002 (back to 2019) | 3,774 (back to 1994) | **79%** |

Turn on **`completeFilingHistory`** and you get all of it. Concretely: GE's
10-K annual reports come back as 26 filings spanning 1994→2026, of which
**18 exist only in the archive**.

Every run also tells you which case you are in — `filingHistoryComplete`,
`filingsInRecent`, `filingsInArchiveShards` and `archiveShardCount` are on the
summary row, so a truncated run can never be mistaken for an exhaustive one.

### The second thing it gets right: restatements

SEC reports the same accounting period repeatedly across filings, **sometimes
with different values**. Apple's total assets for period-end 2008-09-27 is
reported as both **$36.17B** and **$39.57B**; period-end 2009-09-26 appears
seven times. Dump every datapoint and you get duplicate, conflicting
financials; dedupe naively and you may keep the superseded number.

`canonicalFactsOnly` (default **on**) keeps only datapoints carrying SEC's own
canonical period marker — one value per calendar period — and reports how many
were dropped in `restatementPointsDropped`. On a 3-concept Apple run that is
580 of 822 datapoints. Turn it off to audit every reported revision, with
`form`, `filed` and `accessionNumber` on each row so you can see which filing
said what.

***

### Output

Four record types share one dataset, told apart by `recordType`:

- **`COMPANY`** — name, CIK, tickers, exchanges, SIC industry, EIN, LEI,
  fiscal year end, state of incorporation, business address, website, former
  names.
- **`FILING`** — form type, filing date, report period, accession number,
  document description, XBRL flags, plus ready-to-open `filingIndexUrl` and
  `primaryDocumentUrl`. `fromArchiveShard` marks rows a naive client misses.
- **`FINANCIAL_FACT`** — one datapoint: concept, unit, value, period start/end,
  fiscal year/period, the form it came from, and `isCanonical`.
- **`COMPANY_SUMMARY`** — the honesty row (see above), plus
  `conceptsFound` / `conceptsMissing`.
- **`ERROR`** — one per input that could not be resolved, so **every input maps
  to at least one row**.

***

### Input

```jsonc
{
  "tickers": ["AAPL", "MSFT"],
  "ciks": ["0000320193"],
  "includeFilings": true,
  "completeFilingHistory": true,
  "formTypes": ["10-K", "10-Q"],
  "filedFrom": "2015-01-01",
  "includeFinancials": true,
  "financialConcepts": ["Assets", "NetIncomeLoss", "StockholdersEquity"],
  "canonicalFactsOnly": true
}
```

Tickers and CIKs can be mixed. CIKs are zero-padded automatically, so both
`320193` and `0000320193` work.

***

### Known limits (measured, not guessed)

**Concept availability is industry-dependent.** Across five filers in five
industries: `GrossProfit` exists for 1 of 5, `OperatingExpenses` 1 of 5,
`OperatingIncomeLoss` 2 of 5. JPMorgan, as a bank, reports no
`RevenueFromContractWithCustomerExcludingAssessedTax` at all. Anything a filer
does not report is listed in `conceptsMissing` rather than failing the run or
returning silently empty.

**A concept being present does not make it the right series.** `Revenues`
exists for all five filers sampled, but Apple has only 11 datapoints under it,
the newest ending 2018 — Apple moved to
`RevenueFromContractWithCustomerExcludingAssessedTax` after ASC 606. Check the
datapoint count and period range before treating any single concept as a
company's revenue history.

**Several tickers can name one company.** Alphabet has GOOGL, GOOG, GOOGN and
GOOGM; JPMorgan has nine including preferred shares. Requesting two aliases
fetches the company once and records every alias, rather than billing you for
duplicate rows.

**XBRL documents are large** — 3.8 MB for Apple, 7.9 MB for JPMorgan — so
`includeFinancials` costs one sizeable request per company. The response is
filtered to your requested concepts before any row is written.

**Only SEC registrants are covered.** A ticker absent from SEC's own
ticker map produces a `ticker_not_found` ERROR row.

***

### Rate limits and etiquette

SEC's Fair Access policy asks automated clients to identify themselves and to
stay under 10 requests/second. Both are honoured by default. Set
`userAgentContact` to your own contact string (e.g.
`"Acme Research (research@acme.com)"`) — SEC prefers a real address.

A proxy is **off by default**: EDGAR has no WAF and no geo gating, and SEC
would rather see one identifiable client than rotating IPs.

# Actor input Schema

## `tickers` (type: `array`):

Stock tickers to look up, e.g. AAPL, MSFT, BRK-B. Resolved through SEC's own ticker-to-CIK map. Note that several tickers can name the same company (Alphabet has GOOGL, GOOG, GOOGN and GOOGM) — those collapse into one company, with every alias recorded.

## `ciks` (type: `array`):

SEC Central Index Keys, with or without leading zeros (both '320193' and '0000320193' work — they are padded to 10 digits automatically). Use these for filers that have no ticker.

## `includeFilings` (type: `boolean`):

Emit one row per filing (form type, dates, accession number, document URLs).

## `completeFilingHistory` (type: `boolean`):

SEC's main document caps filing history at ~1000 rows and puts everything older in separate archive files. Without this, you silently get only recent filings — measured, that loses 55% of Apple's history, 58% of Berkshire's and 79% of GE's. Costs one extra request per archive file (usually 1-2).

## `formTypes` (type: `array`):

Only return these SEC form types, e.g. 10-K, 10-Q, 8-K, 4, DEF 14A. Leave empty for all forms. Matching is case-insensitive and exact.

## `filedFrom` (type: `string`):

Only return filings filed on or after this date. Leave empty for no lower bound.

## `filedTo` (type: `string`):

Only return filings filed on or before this date. Leave empty for no upper bound.

## `includeFinancials` (type: `boolean`):

Emit one row per financial datapoint from SEC's XBRL company-facts data. Costs one extra request per company, and those documents are large (3.8 MB for Apple, 7.9 MB for JPMorgan) — the response is filtered to your requested concepts before any row is written.

## `financialConcepts` (type: `array`):

Which XBRL concepts to return, e.g. Assets, NetIncomeLoss, StockholdersEquity. Defaults to a set verified present across five different industries. IMPORTANT: availability is industry-dependent — GrossProfit exists for only 1 filer in 5 sampled, and banks report no RevenueFromContractWithCustomer\* at all. Anything a filer does not report is listed in conceptsMissing rather than failing.

## `canonicalFactsOnly` (type: `boolean`):

SEC reports the same period repeatedly across filings, sometimes with DIFFERENT values — Apple's total assets for period-end 2009-09-26 appears 7 times, at $53.85B in the original 10-K and $47.50B after restatement. With this on, only datapoints carrying SEC's own canonical period marker are returned (one value per calendar period) and the number dropped is reported. Turn it off to see every reported revision.

## `maxFilingsPerCompany` (type: `integer`):

Stop after this many filing rows per company. Set 0 for unlimited.

## `maxFactsPerCompany` (type: `integer`):

Stop after this many financial datapoints per company. Set 0 for unlimited.

## `userAgentContact` (type: `string`):

SEC asks automated clients to identify themselves with a contact address, e.g. 'MyCompany research (me@example.com)'. Sent as the User-Agent header. A default is used if you leave this empty.

## `maxConcurrency` (type: `integer`):

Requests in flight at once across all companies. SEC's published limit is 10 requests/second overall; Min request interval is the control that actually enforces it.

## `minRequestInterval` (type: `integer`):

Politeness pacing shared across all workers, to stay inside SEC's 10 requests/second Fair Access limit. The default already keeps the run well under it.

## `proxyConfiguration` (type: `object`):

Optional and OFF by default. SEC EDGAR is public US government data with no WAF and no geo gating, so a proxy is not needed — and SEC prefers one identifiable client staying under its rate limit.

## Actor input object example

```json
{
  "tickers": [
    "AAPL",
    "MSFT"
  ],
  "ciks": [
    "0000320193"
  ],
  "includeFilings": true,
  "completeFilingHistory": false,
  "formTypes": [
    "10-K",
    "10-Q"
  ],
  "filedFrom": "2020-01-01",
  "filedTo": "2026-12-31",
  "includeFinancials": false,
  "financialConcepts": [
    "Assets",
    "NetIncomeLoss"
  ],
  "canonicalFactsOnly": true,
  "maxFilingsPerCompany": 500,
  "maxFactsPerCompany": 2000,
  "userAgentContact": "MyCompany research (me@example.com)",
  "maxConcurrency": 3,
  "minRequestInterval": 0,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `items` (type: `string`):

One row per scraped record. See the dataset's default view for field definitions.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "tickers": [
        "AAPL"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapyx/sec-edgar-fundamentals-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "tickers": ["AAPL"] }

# Run the Actor and wait for it to finish
run = client.actor("scrapyx/sec-edgar-fundamentals-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "tickers": [
    "AAPL"
  ]
}' |
apify call scrapyx/sec-edgar-fundamentals-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,scrapyx/sec-edgar-fundamentals-scraper"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/GkPCRin2WcDR17G1y/builds/iUP9qLTwIIexgyDD3/openapi.json
