Load Brazil CVM Filings into pandas
Data scientists who want a pandas CVM filings API workflow need document metadata in a DataFrame — ticker, type, year, name, stable id — then optional PDF download. apicvm returns JSON lists you can flatten with pandas.json_normalize or a one-line DataFrame(...).
The problem
CVM open-data ZIPs are fine for bulk panels, but a notebook that asks “show me VALE3 FRE 2024 sections” should not re-download the entire market dump. You want:
resolve → list → DataFrame → filter → download selected ids
Setup
import os
import requests
import pandas as pd
BASE = os.environ["APICVM_URL"].rstrip("/")
H = {"Authorization": f"Bearer {os.environ['APICVM_KEY']}"}
def apicvm_get(path: str, **params):
r = requests.get(f"{BASE}{path}", headers=H, params=params, timeout=30)
r.raise_for_status()
return r.json()
Resolve a company
company = apicvm_get("/v1/companies/resolve", query="VALE3", by="ticker")
print(company["name"], company["cnpj"])
List filings as a DataFrame
payload = apicvm_get(
"/v1/documents",
ticker="VALE3",
type="FRE",
year=2024,
perPage=50,
)
df = pd.DataFrame(payload["data"])[
["id", "type", "year", "name", "dateRef", "idCompany"]
]
print(df.head())
Filter section names client-side or with the name query param:
risks = apicvm_get(
"/v1/documents",
ticker="VALE3",
type="FRE",
year=2024,
name="FatoresRisco",
perPage=20,
)
risk_df = pd.DataFrame(risks["data"])
Paginate into one frame
perPage max is 50. Loop pages when you need a fuller catalog:
def list_all_documents(**params):
rows, page = [], 1
while True:
payload = apicvm_get(
"/v1/documents",
page=page,
perPage=50,
**params,
)
rows.extend(payload["data"])
meta = payload.get("meta") or {}
if page >= meta.get("lastPage", page):
break
page += 1
return pd.DataFrame(rows)
itr = list_all_documents(ticker="PETR4", type="ITR", year=2025)
print(itr[["id", "name", "dateRef"]].head())
Download selected PDFs
def download_doc(doc_id: str, out_dir: str = "filings") -> str:
os.makedirs(out_dir, exist_ok=True)
path = os.path.join(out_dir, f"{doc_id}.pdf")
r = requests.get(f"{BASE}/v1/documents/{doc_id}/file", headers=H, timeout=120)
r.raise_for_status()
open(path, "wb").write(r.content)
return path
if not df.empty:
download_doc(df.iloc[0]["id"])
Downloads do not consume extraction credits.
curl equivalent
export APICVM_KEY='apicvm_...'
export APICVM_URL='https://apicvm.dev'
curl -H "Authorization: Bearer $APICVM_KEY" \
"$APICVM_URL/v1/documents?ticker=VALE3&type=DFP&year=2024&perPage=20"
Text extraction note
Page-level markdown via POST /v1/document-text-extractions is async and Pro-only. For pandas pipelines, prefer listing + download first; add extraction when you need searchable text for RAG. See extract text from CVM PDFs.
Current limitations
- API returns filing metadata and files, not accounting line items as DataFrames (unlike some open-data CSV parsers).
- Corpus coverage depends on ingestion — empty filters mean “not ingested,” not “issuer has no filings.”
- Rate limits apply per API key; batch politely.
Next steps
Ready to integrate?
Get an API key and start querying Brazilian CVM filings programmatically.