/screener/metrics

Return filings that match a DSL filter tree, grouped by company (default) or flattened.

POST/api/v1/screener/metrics
Credits89–1825Rate limit10 per minute

Query the universe of US-listed filers by composing metric conditions ("ROE > 20% for the last 5 years AND PE < 20"). Supports OR/NOT, time-window modifiers (for_latest / for_consecutive / for_at_least), 1–120 period lookback, USD-normalized values for ADR foreign filers, and paging up to 500 cumulative results.

Plan-tier gate: POST /screener/metrics, POST /screener/ownership, POST /screener/initial-holdings, POST /screener/institutional-holdings and POST /screener/events require the Pro plan or higher; Free / Starter callers receive 403 PLAN_TIER_INSUFFICIENT (see API key, account, and plan errors). GET /screener/metrics/filters requires the Starter plan or higher.

Pricing note: Filters that touch price-sensitive metrics (market_cap, enterprise_value, all 13 valuation ratios) use the price observed on each filer's filing date; these dates differ across filers.

Request body (unknown fields return 422):

as_of_dateISO date

Must be ≤ today. Filings with filing_date > as_of_date are excluded.

Default today (UTC)

lookbackint 1–120

Most recent N filings per company within scope.

Default 1

include_quarterlybool

false → annual only (10-K / 20-F / 40-F). true adds 10-Q.

Default false

tickersstring[]

Whitelist, max 100. Case-insensitive (normalized to upper). Empty or omitted = full universe.

Default omitted

exclude_tickersstring[]

Blacklist, max 100.

Default []

usd_onlybool

When true, exclude foreign-currency reporters.

Default false

sectorsstring[]

Restrict the universe to these canonical sector buckets (the 12 in Industry classification (Pro+)). Omitted = no sector restriction.

Default omitted

where.filterDSL tree

See Filter DSL (/screener/metrics). Leaf count 1–10, nesting depth ≤ 4.

Default (required)

where.sort_bystring

Metric name, "ticker", "period_end", "filing_date", or "match_count".

Default see Filter DSL (/screener/metrics)

where.sort_order"asc" | "desc"

Default "asc"

include_metrics_using_filing_date_pricebool

false rejects price-sensitive metrics in filter / sort_by / include_metrics.

Default true

exclude_derivationsstring[]

Subset of derived, as_reported, composite, class_sum, segment_sum, company_override, proxy_substituted, TTM_rollforward.

Default []

include_metricsstring[]

Extra metric names (≤ 10) to include in the response beyond filter-referenced ones.

Default []

group_by_tickerbool

true → grouped by company with nested filings[]. false → flat per-filing rows.

Default true

pageint ≥ 1

1-indexed.

Default 1

page_sizeint 1–100

Counts companies (when grouped) or filings (when flat).

Default 50

detailstring

"light" leaves out each company's classification.source.url and the lookback_applied and include_quarterly_applied echoes. See Response detail.

Default "full"

Pagination cap: page × page_size ≤ 500 (422 above it). When the next page would pass the cap, has_more is false, next_page is null, and _warnings says the limit was reached.

Response (group_by_ticker=true, default):

JSON
{
  "page": 1, "page_size": 50,
  "has_more": true,
  "next_page": 2,
  "as_of_date_applied": "2024-12-31",
  "lookback_applied": 5,
  "include_quarterly_applied": false,
  "results": [
    {
      "ticker": "AAPL",
      "match_count": 5,
      "filings": [
        {
          "metric_id": 46,
          "form_type": "10-K",
          "fiscal_year": 2024, "quarter": "FY",
          "period_end": "2024-09-28",
          "filing_date": "2024-11-01",
          "reporting_currency": "usd",
          "metrics": {
            "return_on_equity": {"value": 1.5774, "derivation": "derived", "unit": "ratio"},
            "pe_ratio": {"value": 33.21, "derivation": "derived", "unit": "ratio", "price_date": "2024-11-01"},
            "market_cap": {"value": 3.37e12, "derivation": "as_reported", "unit": "usd", "price_date": "2024-11-01"}
          }
        }
        /* T-1, T-2, … up to lookback filings within company */
      ]
    }
  ],
  "credits_used": 242
}

Response (group_by_ticker=false): identical envelope, but results[] is a flat list of {ticker, metric_id, form_type, fiscal_year, quarter, period_end, filing_date, reporting_currency, metrics} — one row per matching filing.

For Pro+ callers each results[] entry (grouped or flat) also carries an industry classification block when the company has one — see Industry classification (Pro+).

match_count is the number of filings within lookback that satisfy the whole filter tree on a per-filing basis (using each filing's own metric values).

Foreign-currency filers (ADRs) carry fx_rate_used + currency on FX-converted metric entries:

JSON
"market_cap": {
  "value": 2.42e11,            // USD-converted from JPY
  "derivation": "as_reported",
  "unit": "usd",
  "currency": "jpy",
  "fx_rate_used": 145.2,
  "price_date": "2024-06-30"
}

All screener filtering, sorting, and comparison use the USD-converted value. USD reporters omit fx_rate_used and currency.

Within-company filings[] ordering is filing_date DESC, period_end DESC (newest first). For time-series ordering, use group_by_ticker=false with sort_by="period_end" or "filing_date".

Errors: 422 VALIDATION_ERROR (schema; a metric name outside the catalog also returns _supported_metric_names), 402 CREDITS_INSUFFICIENT, 403 PLAN_TIER_INSUFFICIENT below Pro, 504 QUERY_TIMEOUT when the query exceeds the 30-second server timeout (charged).

#Filter DSL (/screener/metrics)

#Leaf node

JSON
{"metric": "<name>", "op": "<operator>", "value": <number | [lo, hi]>,
 "for_latest": <N> | "for_consecutive": <N> | "for_at_least": <N>}
OperatorValue shapeNotes
>, >=, <, <=single number
between[lo, hi] (lo ≤ hi)Inclusive on both ends.

= and != are not supported.

value must be numeric. null is rejected. Filings where the metric value is missing are excluded from filter comparisons (no synthetic match).

#Temporal modifiers (mutually exclusive, at most one per leaf)

ModifierMeaning
(none)Latest filing in lookback must satisfy. Equivalent to for_latest: 1.
for_latest: NMost recent N filings (right-anchored) all satisfy.
for_consecutive: NSome N adjacent filings within lookback all satisfy (sliding window over filing_date).
for_at_least: NAt least N filings within lookback satisfy (any positions).

N ≥ 1 and N ≤ lookback. Tickers without enough filings in lookback to evaluate the modifier are filtered out.

#Composite nodes

JSON
{"and": [<node>, <node>, …]}
{"or":  [<node>, <node>, …]}
{"not": <node>}

where.filter may itself be a bare leaf (no wrapper required for single-condition queries).

#Limits

  • Leaf nodes total: 1–10 (0 → rejected as "filter required"; >10 → rejected as "too complex")
  • Nesting depth: ≤ 4
  • include_metrics length: ≤ 10
  • tickers, exclude_tickers: each ≤ 100 entries

#Credit formula (/screener/metrics)

POST /screener/metrics charges per call (paginate ⇒ each page bills independently):

Formula
credits = max(65, 425
              − tickerDiscount             // ceil(200 × (100 − n_tickers) / 100); 0 (whole universe, or ≥ 100 tickers) … 198 (1 ticker)
              − lookbackDiscount           // 0 (lookback ≥ 30) … 78 (lookback = 1)
              − quarterlyDiscount          // 60 when include_quarterly=false, else 0
              + lookbackSurcharge          // 0 (lookback ≤ 30) … 240 (lookback = 120)
              + filterSurcharge            // 40 × (leaves − 1), 0 … 360
              + temporalSurcharge          // per-leaf 60 (for_latest / for_at_least) or 100 (for_consecutive), capped 600
              + includeMetricsSurcharge)   // 20 × |include_metrics \ filter_metrics|, 0 … 200

Examples:

ScenarioCredits
1 ticker, lookback=1, annual only, 1 leaf (best case)89
Whole universe, lookback=30, quarterly+annual, 1 leaf425
2 tickers, lookback=5, annual, 2 leaves, 1 for_latest, 2 extra include_metrics242
Whole universe, lookback=120 (Business or Enterprise plan), 10 leaves all for_consecutive, 10 extra include_metrics1825 (worst case)

lookback is priced after it is clamped to your plan's history window.

Charge applies even when results is empty, and on 504 QUERY_TIMEOUT. Errors returned before the charge (422, 402, 403, authentication errors, 429) are not charged.

GET /screener/metrics/filters is free.