> ## Documentation Index
> Fetch the complete documentation index at: https://doc.astreus.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Guide: analyze financials in Python

> Pull standardized statements into pandas and compare companies in 30 lines.

`/financials` returns every statement in one call, as maps of line key → row, with values
keyed by period label. Turn that into a DataFrame once and everything else is pandas.

```python theme={null}
import os
import requests
import pandas as pd

BASE = "https://astreus.ai/api/v1"
HEADERS = {"X-API-KEY": os.environ["ASTREUS_API_KEY"]}


def statement(company: str, name: str = "income_statement",
              period_type: str = "quarterly", limit: int = 12) -> pd.DataFrame:
    """One statement as a DataFrame: rows = standardized lines, columns = period end dates."""
    r = requests.get(
        f"{BASE}/companies/{company}/financials",
        params={"period_type": period_type, "limit": limit},
        headers=HEADERS,
        timeout=30,
    )
    r.raise_for_status()
    fin = r.json()["financials"]
    rows = fin[name]["data"]
    meta = fin["period_meta"]                       # {"Q3 2025": {"end_date": "2024-10-27", ...}, ...}

    lines = {key: row["values"] for key, row in rows.items() if key.startswith("std:")}
    df = pd.DataFrame(lines).T                      # index = std:* keys, columns = period labels
    df.columns = [meta.get(c, {}).get("end_date", c) for c in df.columns]   # "TTM" has no meta
    return df.sort_index(axis=1)


nvda = statement("NVDA")
amd = statement("AMD")

# Revenue growth, quarter over quarter (fiscal calendars differ, so align on your own terms)
growth = pd.DataFrame({
    "NVDA": nvda.loc["std:revenue"].pct_change(),
    "AMD": amd.loc["std:revenue"].pct_change(),
})
print(growth.tail(6))
```

<Note>
  Row keys are the standardized vocabulary (`std:revenue`, `std:cost_of_revenue`,
  `std:gross_profit`, `std:cfo`, …) — the same keys `fact-source` accepts, so any cell in the
  frame can be cited: `statement=income_statement&key=std:revenue&end_date=<column>`. The
  filer's own XBRL lines (`us-gaap:Revenues`) sit in the same map with a `parent_key`; the
  filter above keeps only the standardized rows. Values are in `fin["reporting_currency"]`.
</Note>

## Handle limits like a good citizen

```python theme={null}
import time

def get(url, **kwargs):
    while True:
        r = requests.get(url, headers=HEADERS, timeout=30, **kwargs)
        if r.status_code == 429:
            wait = int(r.headers.get("Retry-After", "5"))
            time.sleep(wait)
            continue
        r.raise_for_status()
        return r.json()
```

Watch `X-Quota-Remaining` on quota-carrying plans, and remember: one `/financials`
call returns whole statements — never page line by line.

## Discrete or cumulative quarters

The default (`reporting_basis=individual`) gives discrete three-month figures. If you want
the year-to-date column exactly as each 10-Q prints it, add `reporting_basis=as_reported`.
Annual columns are the same either way.

## As originally reported

Backtesting against what the market knew at the time? `/financials` serves the latest
restated value for every period. For the originally filed figures use the as-filed view:

```python theme={null}
r = requests.get(
    f"{BASE}/companies/NVDA/financials/as-filed",
    params={"statement": "income_statement", "period_type": "quarterly", "vintage": "original"},
    headers=HEADERS, timeout=30,
)
grid = r.json()          # rows = printed captions, cells carry value, docRef and own/comparative
```

Add `format=csv` or `format=xlsx` to download the grid instead. Macro endpoints are
point-in-time by construction — no look-ahead there either.
