# SEC EDGAR Scraper - Filings and Full-Text Search (`s-r/sec-edgar-scraper`) Actor

Pull SEC filings by ticker or CIK, or search the full text of every filing. Returns form type, filing and report dates, XBRL flags and direct document links from the SEC's own public APIs.

- **URL**: https://apify.com/s-r/sec-edgar-scraper.md
- **Developed by:** [SR](https://apify.com/s-r) (community)
- **Categories:** Business
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## SEC EDGAR Scraper

Pull **SEC filings** for any public company by ticker or CIK, or **search the
full text of every filing** to find which companies used a phrase. Form type,
filing and report dates, XBRL flags, and a direct link to the document itself.

This reads the SEC's own public APIs. No key, no login.

### Two questions, two modes

**"What has this company filed?"**

```
companies: ["AAPL", "MSFT"]
forms: "10-K,10-Q"
```

Tickers are resolved to CIK numbers against the SEC's own published ticker file,
so you do not need to look them up. CIK numbers work directly too, padded or
not.

**"Which companies talk about this?"**

```
search: "climate risk"
forms: "10-K"
```

Full-text search runs across every filing on EDGAR. Searching "climate risk" in
10-K filings returns 10,000 hits across hundreds of companies. This is the mode
that answers questions a company-by-company pull cannot: which filers disclose a
material weakness, mention a competitor by name, or first used a term and when.

### Why this Actor connects directly

Worth explaining, because it is unusual in this collection. Measured on 3
September 2026, same URL:

| Route | Result |
|---|---|
| Direct | **HTTP 200**, 164 KB, Apple Inc., 1,001 filings |
| Through a residential proxy, US-pinned | `CONNECT tunnel failed, response 491` |

A 491 on the CONNECT is the **proxy refusing to tunnel to the host**, not the
SEC refusing us. Routing around that would be solving the wrong problem: the SEC
publishes these APIs *for* automated use. Their fair-access policy asks only
that callers identify themselves and stay under ten requests a second, and this
Actor does both — it paces itself well under that, and puts your
`contact_email` in the User-Agent.

Supplying a real address is the polite thing and the more reliable one. Runs
work without it, but a bare library default is what gets throttled, and
deservedly.

### The trap in this data

`filings.recent` is **columnar**. It is not a list of filings — it is a dozen
parallel arrays, `form[]`, `filingDate[]`, `accessionNumber[]`,
`primaryDocument[]` and more, each 1,001 entries long for Apple. There is no
per-filing object anywhere in the response.

Zip them by index and you get filings. Index them independently and every field
belongs to a different filing, **and nothing raises**. You get a 10-K with a
10-Q's date and a link to the wrong document, in a run that reports success.

This Actor takes the length from the shortest array and builds every row at one
index, so a ragged response truncates rather than misaligning. The tests pin
that.

### Fields

- **Filer**: `cik`, `company`, `ticker`, `tickers`, `exchanges`, `sic`,
  `sic_description`, `state_of_incorporation`, `fiscal_year_end`
- **Filing**: `accession_number`, `form`, `filing_date`, `report_date`,
  `acceptance_datetime`, `file_number`, `size_bytes`
- **Documents**: `primary_document`, `primary_doc_description`,
  **`document_url`** (a direct link to the filing), `filing_index_url`
- **Structured data**: `is_xbrl`, `is_inline_xbrl`
- **8-K only**: `items`, the item numbers cited

`filing_date` and `report_date` are different and both matter: a 10-K filed on
31 October 2025 reports on a fiscal year that ended 27 September 2025. Comparing
companies on the wrong one mixes up reporting periods.

On full-text search results, EDGAR crams three facts into one string —
`CARNIVAL CORP (CCL) (CIK 0000815097)` — and this Actor splits them into
`company`, `ticker` and `cik` rather than writing that whole line into a name
column.

### Input reference

| Field | Type | Default |
|---|---|---|
| `companies` | tickers or CIKs | `["AAPL","MSFT"]` |
| `search` | full-text phrase | — |
| `forms` | comma separated form types | all |
| `date_from`, `date_to` | YYYY-MM-DD, search mode only | — |
| `contact_email` | goes in the User-Agent | a generic address |
| `limit` | 1-5000 | 100 |
| `retries` | 1-6 | 3 |

### Typical uses

- **Filing monitoring.** Watch a set of companies for new 8-K or 10-Q filings on
  a schedule, and pull `document_url` straight into whatever reads them.
- **Disclosure research.** Full-text search for a risk factor, an accounting
  term or a competitor's name across every 10-K, and see who says it.
- **Insider activity.** Filter `forms: "4"` for a company and you have its
  insider transaction filings. On a typical recent-30 pull for Microsoft, 20 of
  30 filings were Form 4.
- **Peer sets.** `sic` and `sic_description` group filers by industry as the SEC
  classifies them, which is often cleaner than a vendor's sector labels.
- **XBRL pipelines.** `is_inline_xbrl` tells you which filings carry machine
  readable financials before you download anything.

### Notes on behaviour

A ticker with no company on EDGAR returns a `not_found` error naming it, never a
row of nulls. A 404 is definitive and not retried; a 429 is the SEC throttling
and is waited out rather than retried immediately, because rotating away from a
throttle you caused is the wrong response.

Full-text search covers filings from 2001 onward and caps at 10,000 hits per
query, which the run summary reports as `totalHits` so you can narrow rather
than assume you have everything.

The submissions endpoint returns roughly the last 1,000 filings per company.
Older ones live in separate archive files that this Actor does not currently
fetch.

# Actor input Schema

## `companies` (type: `array`):

Companies to pull filings for, as tickers (AAPL) or CIK numbers (0000320193). Tickers are resolved against the SEC's own ticker file.

## `search` (type: `string`):

Search the full text of every filing instead, for example "climate risk" or "material weakness". Finds which companies used a phrase.

## `forms` (type: `string`):

Restrict to specific forms, comma separated: 10-K, 10-Q, 8-K, 4, S-1. Leave empty for everything.

## `date_from` (type: `string`):

Only filings on or after this date, as YYYY-MM-DD. Full-text search only.

## `date_to` (type: `string`):

Only filings on or before this date, as YYYY-MM-DD. Full-text search only.

## `contact_email` (type: `string`):

The SEC's fair-access policy asks automated callers to identify themselves. Your address goes in the User-Agent. Runs work without it, but supplying a real one is the polite and more reliable choice.

## `limit` (type: `integer`):

How many filings to return.

## `retries` (type: `integer`):

Retries with backoff before a request is reported as an error.

## Actor input object example

```json
{
  "companies": [
    "AAPL",
    "MSFT"
  ],
  "search": "climate risk",
  "forms": "10-K,10-Q",
  "contact_email": "you@company.com",
  "limit": 100,
  "retries": 3
}
```

# Actor output Schema

## `filings` (type: `string`):

One row per SEC filing.

## `summary` (type: `string`):

Counts, resolved CIKs and the breakdown of form types.

## `errors` (type: `string`):

Failures with a code and a redacted message.

# 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 = {
    "companies": [
        "AAPL",
        "MSFT"
    ],
    "limit": 100,
    "retries": 3
};

// Run the Actor and wait for it to finish
const run = await client.actor("s-r/sec-edgar-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 = {
    "companies": [
        "AAPL",
        "MSFT",
    ],
    "limit": 100,
    "retries": 3,
}

# Run the Actor and wait for it to finish
run = client.actor("s-r/sec-edgar-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 '{
  "companies": [
    "AAPL",
    "MSFT"
  ],
  "limit": 100,
  "retries": 3
}' |
apify call s-r/sec-edgar-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,s-r/sec-edgar-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/najeoRnq9XHNwdDka/builds/54yujf3MSgMRb3hdC/openapi.json
