3spread
All guides

Screen companies by factor scores in Python

Pull 90 point-in-time factors with percentile ranks for a single company, then run a cross-company screen sorted by any factor. About 40 lines of Python on a free key.

The 3spread TeamSeptember 9, 2026Financials dataset

Factor investing starts with a question: which companies are the most profitable, the most efficient, the cheapest relative to their fundamentals? Answering it means computing ratios across thousands of filers, normalizing for sector, and ranking within a cohort, all while respecting point-in-time data so a backtest never sees the future.

3spread ships 90 precomputed factors covering profitability, growth, cash flow, efficiency, leverage, and composite scores (Altman Z, Piotroski F, Beneish M). Every factor carries a percentile rank within its sector and period cohort, and every value is stamped to the filing that produced it, so you can replay exactly what was known at any date.

This guide pulls a single company's factor profile, then runs a cross-company screen sorted by return on equity.

Everything below runs on the free Community tier: every dataset, 10,000 requests per day (600 per minute), no credit card.

Setup

pip install py3spread
export THREESPREAD_API_KEY="sk_live_..."

Grab a key at 3spread.com/auth/signup. The client reads THREESPREAD_API_KEY from the environment, so you never put it in the code.

List the available factors

from py3spread import Client
 
with Client() as client:
    names = client.financials.factor_names()
 
    # Group by category so the output is scannable
    by_cat = {}
    for f in names["data"]:
        by_cat.setdefault(f["category"], []).append(f["name"])
 
    for cat in sorted(by_cat):
        factors = by_cat[cat]
        print(f"{cat} ({len(factors)})")
        for name in factors:
            print(f"  {name}")

Categories include profitability, growth, cash_flow, efficiency, health, leverage, and composite. Each factor has a formula description in the response so you know exactly what the number means.

Pull a company's factor profile

from decimal import Decimal
 
TICKER = "AAPL"
 
with Client() as client:
    page = client.financials.factors(
        ticker=TICKER,
        version="latest",
        period_type="FY",
        latest_only=True,
        limit=100,
    )
 
    print(f"{TICKER} factor profile (latest FY):")
    print(f"{'factor':<30} {'value':>15} {'pctile':>8}")
    print("-" * 55)
 
    for row in page["data"]:
        val = row["value"]
        pctile = row["value_pctile"]
        if val is None:
            print(f"{row['factor_name']:<30} {'null':>15} {str(pctile or '-'):>8}")
        else:
            print(f"{row['factor_name']:<30} {Decimal(val):>15.4f} {pctile:>8.1f}")

latest_only=True collapses the series to the newest period per factor, so one call gives you the current snapshot. The value field is a raw fraction (ROE of 1.57 means 157.4 percent, not 1.57 percent). The value_pctile field is the precomputed rank within the company's sector and period cohort, 0 to 100.

If a factor is null, the flag field tells you why: div_zero for a division by zero, neg_base for a negative base period, insufficient_inputs when the underlying statement did not have enough data. Null-faithful means a non-computable factor is null with a reason, never a zero that would pollute a screen.

Screen the universe by a factor

The same endpoint does cross-company screens. Drop the ticker, pass a factor_name, and sort by value:

    screen = client.financials.factors(
        factor_name="roe",
        version="latest",
        period_type="FY",
        period_end="2024-12-31",
        sort="value",
        order="desc",
        limit=20,
    )
 
    print(f"\nTop 20 by ROE, FY ending 2024-12-31:")
    print(f"{'ticker':<8} {'company':<30} {'ROE':>12} {'pctile':>8}")
    print("-" * 60)
 
    for row in screen["data"]:
        ticker = (row.get("filer_tickers") or ["-"])[0]
        name = (row.get("filer_name") or "")[:28]
        val = Decimal(row["value"]) if row["value"] else Decimal("0")
        pctile = row["value_pctile"] or 0
        print(f"{ticker:<8} {name:<30} {val:>12.2f} {pctile:>8.1f}")

The period_end pin matters for a screen. Without it, rows from different fiscal periods are mixed. Pinning to a specific date ensures every row in the response comes from the same cohort, so the percentile ranks are directly comparable.

Point-in-time factors for backtesting

For a backtest, you need the factor values that were known on a specific date, not the latest restated version. The version parameter handles this:

    pit = client.financials.factors(
        ticker=TICKER,
        factor_name="roe",
        version="as_of:2024-01-01",
        period_type="FY",
        limit=5,
    )
 
    print(f"\n{TICKER} ROE, point-in-time as of 2024-01-01:")
    for row in pit["data"]:
        val = Decimal(row["value"]) if row["value"] else None
        print(f"  period: {row['period_end']}  ROE: {val}  filed: {row['accepted_time']}")

version="as_of:YYYY-MM-DD" selects the newest filing that was public on that date. A restatement filed later does not appear, so the factor values your backtest sees are exactly what a live strategy would have seen on that day.

Where to take it

Combine factors into composite screens. Pull ROE and debt-to-equity in two calls, join on CIK, and filter for high-return, low-leverage names. Or use the precomputed composite scores directly: altman_z_book for distress risk, piotroski_f for financial strength, beneish_aqi for earnings manipulation risk.

The factors/percentile endpoint goes deeper than the precomputed value_pctile field. It lets you freeze the cohort at an arbitrary date and get the exact percentile rank against the sector peers that were public then, which is what a rigorous cross-sectional backtest needs.

The dataset

Full endpoint reference, response schemas, and every parameter are on the financials dataset page. The interactive API reference has the complete specification.

The py3spread client (v0.3.0) wraps all 11 financials endpoints with typed method signatures and automatic pagination. See the client documentation and the financials example script for statements, metrics, ratios, factors, and percentile ranks in one file.

Run this yourself

Everything here works on the free Community tier: every dataset, 10,000 requests per day (600 per minute), no credit card. Provision a key and paste the code in.