# SEC Fundamentals Screener — XBRL Ratios Across All Filers (`yzy001/sec-fundamentals-screener`) Actor

Rank every SEC filer by a fundamental ratio from official XBRL data: net margin, ROE, ROA, debt-to-equity, current ratio, accrual ratio, or your own numerator and denominator. Handles the US-GAAP tag fragmentation that makes naive screens cover half the market.

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

## Pricing

from $4.00 / 1,000 companies

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 Fundamentals Screener — XBRL Ratios Across All Filers

Rank every SEC filer by a fundamental ratio, computed from the official XBRL data the
companies themselves filed. Not a list of filings — a ranking.

One request to SEC's XBRL frames API returns a single accounting figure for **every filer in
a period** (total assets for Q4 2025 came back with 6,163 companies). A ratio therefore
needs only a handful of requests to cover the whole market.

### Ratios

| Ratio | Formula |
|---|---|
| Net margin | net income ÷ revenue |
| Gross margin | gross profit ÷ revenue |
| Operating margin | operating income ÷ revenue |
| ROE | net income ÷ shareholders' equity |
| ROA | net income ÷ total assets |
| Asset turnover | revenue ÷ total assets |
| Debt to equity | total liabilities ÷ equity |
| Current ratio | current assets ÷ current liabilities |
| Cash to assets | cash ÷ total assets |
| Cash flow margin | operating cash flow ÷ revenue |
| **Accrual ratio** | net income ÷ operating cash flow — far from 1 means the earnings are not backed by cash |

Or set your own numerator and denominator from: revenue, netIncome, operatingIncome,
grossProfit, operatingCashFlow, assets, liabilities, equity, currentAssets,
currentLiabilities, cash, longTermDebt, shares.

### What you get per company

| Field | Example |
|---|---|
| `entityName` / `cik` | `ABBOTT LABORATORIES` / `1800` |
| `ratio` | `0.1842` |
| `numerator` / `denominator` | `8,243,000,000` / `44,750,000,000` |
| **`numeratorTag` / `denominatorTag`** | **which US-GAAP tag the value actually came from** |
| `numeratorPeriodEnd` / `denominatorPeriodEnd` | `2025-12-31` / `2025-12-31` |
| `numeratorAccession` / `denominatorAccession` | the filings the numbers came from |

Two dataset views: **Ranking** and **Provenance** (tags and filing accession numbers, so
every number traces back to a filing).

### The two problems that make naive screens wrong

**One accounting concept is spread across several US-GAAP tags.** Measured for full-year
2025 revenue:

| Tag | Companies |
|---|---|
| `RevenueFromContractWithCustomerExcludingAssessedTax` | 2,705 |
| `Revenues` | 2,251 |
| `RevenueFromContractWithCustomerIncludingAssessedTax` | 658 |
| **Total** | **5,614** |

Using only the largest tag covers **48%** of filers that reported revenue — and the output
looks like a complete market screen either way. This Actor walks a fallback list per concept
and records in every row which tag supplied the value.

**A quarterly frame mixes fiscal calendars.** For the same SEC period `CY2025Q4`, one
company's net income covers 1 Sep – 30 Nov while another's revenue covers 1 Oct – 31 Dec.
Divide them and the ratio spans different periods. This Actor compares the period end dates
of same-type concepts, drops the mismatches, and counts them in
`SUMMARY.droppedPeriodMismatch`. **Annual periods pair about 5.8 times more companies than
quarterly ones**, which is why `CY2025` is the default.

### How it reports what it could not do

| Check | What it catches |
|---|---|
| Concept unavailable | Every fallback tag returned 404 for that period. Reported as "no data for this concept and period", never as "no companies matched". Balance-sheet concepts need the `I` suffix on the period, and a mismatch is a hard 404. |
| Period mismatch | Numerator and denominator reported for different period end dates. Dropped, counted, and the run stops if most companies are affected. |
| Balance-sheet date far from the income period | For ROE, ROA and other income ÷ balance-sheet ratios, the balance-sheet date must be within `maxInstantGapDays` (default 10) of the income period's end. Otherwise a July fiscal year's income would be divided by January equity. Dropped and counted in `droppedInstantMisaligned`. |
| Low coverage | Too few filers can be paired. A ranking built on a small intersection is not the market, so the run fails loudly rather than looking complete. |
| Zero denominator | Dropped and counted, never returned as infinity. |
| Tiny denominator | `minDenominatorAbs` removes small absolute denominators. `minDenominatorToAssets` (default 0.002) removes denominators that are tiny next to the company's total assets: measured for CY2025 net margin, the unfiltered top two were a SPAC with $58 of revenue (5,212x) and a fund whose revenue tag was 0.009% of its assets. Mortgage REITs, the lowest legitimate revenue-to-assets values (0.006–0.008), stay in. |
| Misfiled scale | `scaleCheck` drops numerators larger than `maxScaleRatio` × total assets (a proxy statement filed net income 1,000 times too large, and SEC's frame picked it) and companies with total assets under `minScaleAbs`. |
| Missing contact | SEC requires programmatic requests to identify the requester, so the run is refused with the required format instead of sending an anonymous request. The input form's placeholder (`Your Name your@email.com`) and reserved addresses such as `@example.com` count as missing: they are refused before any request reaches SEC. |

`SUMMARY` also carries the ratio distribution (min, p25, median, p75, max) and per-tag
coverage, so you can see the shape of the market before reading the top of the list.

### Notes on the data

- SEC's fair-access policy requires a **contact name and email** in the request header. You
  provide it; the Actor will not invent one.
- Values are taken as filed, with **no restatement adjustments**. Outliers are removed only by
  the scale checks above, and every removal is counted in `SUMMARY`.
- `coverageOfLargerFrame` tells you what share of filers this ranking represents. A low
  value means the ranking is a subset, not the market.
- SEC's annual balance-sheet frame holds one value per company near the calendar year end.
  Companies whose fiscal year ends elsewhere therefore drop out of ROE-type ratios rather than
  being divided by a balance from another point in their year.
- This is a screening tool, not investment advice.

# Actor input Schema

## `contactUserAgent` (type: `string`):

SEC's fair-access policy requires programmatic requests to identify the requester. Replace the placeholder with your own name and an email you read, e.g. 'Jane Doe jane.doe@yourfirm.com'. The placeholder itself is refused before any request is sent. This is SEC policy, not a restriction of this Actor.

## `ratio` (type: `string`):

Which fundamental ratio to compute and rank by. Ignored if you set a custom numerator and denominator below.

## `period` (type: `string`):

SEC calendar period, e.g. CY2025 for the full year or CY2025Q4 for a quarter. Annual periods cover about 5.8 times more companies than quarterly ones, because fewer filers tag a comparable quarter.

## `order` (type: `string`):

Highest ratio first, or lowest first.

## `numeratorConcept` (type: `string`):

Override the preset ratio with your own numerator concept: revenue, netIncome, operatingIncome, grossProfit, operatingCashFlow, assets, liabilities, equity, currentAssets, currentLiabilities, cash, longTermDebt, shares.

## `denominatorConcept` (type: `string`):

The denominator concept, from the same list as the numerator. Must be set together with the numerator.

## `minCoverage` (type: `number`):

Stop if fewer than this fraction of filers can be paired. A ranking built on a tiny intersection is not a picture of the market, so the run fails loudly instead of looking complete.

## `maxPeriodMismatch` (type: `number`):

Stop if more than this share of companies report the numerator and denominator for different period end dates, which would make the ratio cross fiscal periods.

## `maxInstantGapDays` (type: `integer`):

For ratios that divide an income-statement figure by a balance-sheet figure (ROE, ROA), the balance-sheet date must be within this many days of the income period's end. The default 10 allows 52/53-week fiscal years. Companies outside it are dropped and counted.

## `minDenominatorAbs` (type: `number`):

Drop companies whose denominator is smaller than this in absolute value. Tiny denominators inflate ratios into meaningless outliers. In USD; 0 disables.

## `minDenominatorToAssets` (type: `number`):

Drops companies whose denominator is tiny next to their total assets, which turns a ratio into noise (a SPAC with $58 of revenue, a fund whose revenue tag is 0.009% of assets). Measured over 4,600 companies for CY2025 revenue: p1 = 0.0022, and mortgage REITs, the lowest legitimate case, sit at 0.006–0.008. Set 0 to turn off. Needs scaleCheck.

## `scaleCheck` (type: `boolean`):

SEC's frames API does not prefer financial statements: one measured company filed net income of 22,367,000 in its 10-K and 22,367,000,000 in its proxy statement, and the frame returned the proxy figure. This check compares the numerator against total assets and drops such rows.

## `maxScaleRatio` (type: `number`):

Drop companies whose numerator exceeds total assets by more than this multiple, which indicates a scale error in the filing. Measured distribution of net income over assets: p90 = 0.71, p99 = 13.1, so the default of 5 sits in the tail rather than inside the body.

## `minScaleAbs` (type: `number`):

Drop companies with total assets below this. Shells with near-zero assets produce arithmetically correct but meaningless ratios - two measured examples had total assets of 1,402 and 1,058 USD.

## `excludeNegativeDenominator` (type: `boolean`):

A negative denominator flips the sign of the ratio. One measured company had net income of -696M and equity of -164M, giving an ROE of +424% and placing a heavily loss-making company with negative equity at the top of a highest-ROE ranking. Turn this off and use the denominatorSign field to judge for yourself.

## `maxResults` (type: `integer`):

Maximum number of company rows to return after ranking.

## `delaySeconds` (type: `number`):

Pause between requests to data.sec.gov. SEC asks for no more than about 10 requests per second; a ratio needs only a handful of requests in total.

## Actor input object example

```json
{
  "contactUserAgent": "Your Name your@email.com",
  "ratio": "netMargin",
  "period": "CY2025",
  "order": "desc",
  "minCoverage": 0.05,
  "maxPeriodMismatch": 0.5,
  "maxInstantGapDays": 10,
  "minDenominatorAbs": 1000000,
  "minDenominatorToAssets": 0.002,
  "scaleCheck": true,
  "maxScaleRatio": 5,
  "minScaleAbs": 1000000,
  "excludeNegativeDenominator": true,
  "maxResults": 1000,
  "delaySeconds": 0.3
}
```

# Actor output Schema

## `companies` (type: `string`):

CIK, name, the ratio, numerator and denominator values, which US-GAAP tag each value came from, and the period end dates.

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

How many filers reported each side, how many could be paired, the coverage fraction, how many were dropped for period mismatch or a zero denominator, the ratio distribution, and per-tag coverage.

# 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 = {
    "contactUserAgent": "Your Name your@email.com",
    "ratio": "netMargin",
    "period": "CY2025",
    "order": "desc",
    "minCoverage": 0.05,
    "maxPeriodMismatch": 0.5,
    "maxInstantGapDays": 10,
    "minDenominatorAbs": 1000000,
    "minDenominatorToAssets": 0.002,
    "scaleCheck": true,
    "maxScaleRatio": 5,
    "minScaleAbs": 1000000,
    "excludeNegativeDenominator": true,
    "maxResults": 1000,
    "delaySeconds": 0.3
};

// Run the Actor and wait for it to finish
const run = await client.actor("yzy001/sec-fundamentals-screener").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 = {
    "contactUserAgent": "Your Name your@email.com",
    "ratio": "netMargin",
    "period": "CY2025",
    "order": "desc",
    "minCoverage": 0.05,
    "maxPeriodMismatch": 0.5,
    "maxInstantGapDays": 10,
    "minDenominatorAbs": 1000000,
    "minDenominatorToAssets": 0.002,
    "scaleCheck": True,
    "maxScaleRatio": 5,
    "minScaleAbs": 1000000,
    "excludeNegativeDenominator": True,
    "maxResults": 1000,
    "delaySeconds": 0.3,
}

# Run the Actor and wait for it to finish
run = client.actor("yzy001/sec-fundamentals-screener").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 '{
  "contactUserAgent": "Your Name your@email.com",
  "ratio": "netMargin",
  "period": "CY2025",
  "order": "desc",
  "minCoverage": 0.05,
  "maxPeriodMismatch": 0.5,
  "maxInstantGapDays": 10,
  "minDenominatorAbs": 1000000,
  "minDenominatorToAssets": 0.002,
  "scaleCheck": true,
  "maxScaleRatio": 5,
  "minScaleAbs": 1000000,
  "excludeNegativeDenominator": true,
  "maxResults": 1000,
  "delaySeconds": 0.3
}' |
apify call yzy001/sec-fundamentals-screener --silent --output-dataset

```

## MCP server setup

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

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/1N7Qq0nLtrTgRdaT3/builds/tgAWbN6RgjlxRajLc/openapi.json
