Load Brazil CVM Filings into Polars
Want a Polars CVM filings API workflow instead of pandas? apicvm returns paginated JSON. Use httpx or requests, then pl.DataFrame — same /v1 contract as the pandas guide.
The problem
Data teams building Brazil filing inventories often:
- Paste portal CSVs into notebooks that break on layout changes
- Mix quotes APIs with regulatory PDFs and lose auditability
- Need lazy, typed tables for multi-ticker scans
Polars fits batch joins; apicvm supplies stable document UUIDs.
Setup
pip install polars httpx
export APICVM_URL='https://apicvm.dev'
export APICVM_KEY='apicvm_...'
Resolve + list into a DataFrame
import os
import httpx
import polars as pl
BASE = os.environ["APICVM_URL"]
H = {"Authorization": f"Bearer {os.environ['APICVM_KEY']}"}
with httpx.Client(base_url=BASE, headers=H, timeout=30.0) as client:
company = client.get(
"/v1/companies/resolve",
params={"query": "PETR4", "by": "ticker"},
)
company.raise_for_status()
docs = client.get(
"/v1/documents",
params={"ticker": "PETR4", "type": "DFP", "year": 2024, "perPage": 50},
)
docs.raise_for_status()
payload = docs.json()
df = pl.DataFrame(payload["data"])
print(df.select(["id", "name", "type", "year", "dateRef"]))
Paginate all pages
def list_all_documents(client: httpx.Client, **params) -> pl.DataFrame:
page = 1
frames: list[pl.DataFrame] = []
while True:
r = client.get("/v1/documents", params={**params, "page": page, "perPage": 50})
r.raise_for_status()
body = r.json()
chunk = body.get("data") or []
if not chunk:
break
frames.append(pl.DataFrame(chunk))
meta = body.get("meta") or {}
if page >= int(meta.get("lastPage") or page):
break
page += 1
return pl.concat(frames) if frames else pl.DataFrame()
with httpx.Client(base_url=BASE, headers=H, timeout=30.0) as client:
itr = list_all_documents(client, ticker="VALE3", type="ITR", year=2024)
print(itr.height, itr.columns)
Multi-ticker inventory
tickers = ["PETR4", "VALE3", "VIVT3"]
parts = []
with httpx.Client(base_url=BASE, headers=H, timeout=30.0) as client:
for t in tickers:
parts.append(list_all_documents(client, ticker=t, type="FRE", year=2024))
universe = pl.concat(parts).with_columns(pl.col("ticker").cast(pl.Utf8))
print(universe.group_by("ticker").len())
Watch X-RateLimit-Remaining when the basket grows. Details: Handle errors and rate limits.
Download is separate from the frame
curl -OJ -H "Authorization: Bearer $APICVM_KEY" \
"$APICVM_URL/v1/documents/<document-id>/file"
Keep UUIDs in Polars; pull PDFs in a second pass. File download does not consume extraction credits.
Current limitations
- Responses are JSON document metadata — not XBRL fact tables.
- Text extraction is async via callback and credit-based on Pro.
- Empty frames mean corpus miss or filter miss, not “no filing exists at CVM.”
Next steps
Ready to integrate?
Get an API key and start querying Brazilian CVM filings programmatically.