How to Download DFP and ITR Filings from Brazilian Public Companies
DFP and ITR are the two CVM filing types you need for financial statement analysis: annual standardized statements and quarterly updates. If you are building a DFP ITR API integration, the workflow is list by ticker and type, pick a document, download the PDF.
Note on ITR: here ITR means Informações Trimestrais (Brazilian quarterly filings) — not an income tax return API.
This guide shows curl and Python examples using PETR4 (annual DFP) and VALE3 (quarterly ITR) with apicvm.
The problem
Financial data pipelines for Brazilian equities need:
- DFP — full-year audited statements (roughly analogous to a 10-K)
- ITR — quarterly interim reports (Informações Trimestrais; roughly analogous to a 10-Q, not a tax filing)
Finding the correct PDF on CVM portals means navigating Portuguese UI, matching CNPJ to ticker, and handling inconsistent naming. You want GET filings?ticker=VALE3&type=ITR&year=2024 — that is what apicvm provides.
What DFP and ITR contain
| Type | Full name | When filed | Typical contents |
|---|---|---|---|
| DFP | Demonstrações Financeiras Padronizadas | After fiscal year close | Balance sheet, income statement, cash flow, notes |
| ITR | Informações Trimestrais (not income tax) | Each quarter | Unaudited/quarterly financials, MD&A updates |
Both are submitted to the CVM by B3-listed companies. apicvm indexes them with type, year, dateRef, and name metadata.
List DFP filings
export APICVM_KEY='apicvm_...'
export APICVM_URL='https://apicvm.dev'
curl -H "Authorization: Bearer $APICVM_KEY" \
"$APICVM_URL/v1/documents?ticker=PETR4&type=DFP&year=2024&perPage=20"
Sample response item:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"type": "DFP",
"year": 2024,
"dateRef": "2024-12-31",
"name": "Demonstrações Financeiras Padronizadas",
"idCompany": 123,
"company": {
"name": "PETROBRAS",
"tickers": [{ "ticker": "PETR4", "tickerClass": "PN" }]
}
}
List ITR filings
Same endpoint, different type:
curl -H "Authorization: Bearer $APICVM_KEY" \
"$APICVM_URL/v1/documents?ticker=VALE3&type=ITR&year=2024&perPage=20"
Filter multiple types in one request:
curl -H "Authorization: Bearer $APICVM_KEY" \
"$APICVM_URL/v1/documents?ticker=VALE3&types=DFP,ITR&year=2024&perPage=50"
Download the PDF
Use the UUID from list results — never guess document IDs:
curl -OJ -H "Authorization: Bearer $APICVM_KEY" \
"$APICVM_URL/v1/documents/550e8400-e29b-41d4-a716-446655440000/file"
Returns the original PDF with Content-Disposition filename headers.
Python: download latest DFP and ITR for a ticker
import os
import requests
from pathlib import Path
BASE = os.environ["APICVM_URL"]
H = {"Authorization": f"Bearer {os.environ['APICVM_KEY']}"}
def list_documents(ticker: str, doc_type: str, year: int) -> list:
r = requests.get(
f"{BASE}/v1/documents",
headers=H,
params={"ticker": ticker, "type": doc_type, "year": year, "perPage": 50},
)
r.raise_for_status()
return r.json()["data"]
def download_document(doc_id: str, dest: Path) -> None:
r = requests.get(f"{BASE}/v1/documents/{doc_id}/file", headers=H, stream=True)
r.raise_for_status()
with dest.open("wb") as f:
for chunk in r.iter_content(8192):
f.write(chunk)
def fetch_filing(ticker: str, doc_type: str, year: int, out_dir: Path) -> Path | None:
docs = list_documents(ticker, doc_type, year)
if not docs:
print(f"No {doc_type} {year} for {ticker}")
return None
doc = docs[0] # first result; inspect docs[] to pick a specific filing
filename = out_dir / f"{ticker.lower()}-{doc_type.lower()}-{year}.pdf"
download_document(doc["id"], filename)
print(f"Downloaded {filename}")
return filename
out_dir = Path("filings")
out_dir.mkdir(exist_ok=True)
fetch_filing("PETR4", "DFP", 2024, out_dir)
fetch_filing("VALE3", "ITR", 2024, out_dir)
Tip: When list results return multiple documents, inspect name and dateRef to select the right one. The API returns an explicit list — it does not auto-pick.
Pagination
Default perPage is 10; maximum is 50. For companies with long filing histories:
page = 1
all_docs = []
while True:
r = requests.get(
f"{BASE}/v1/documents",
headers=H,
params={"ticker": "PETR4", "type": "ITR", "page": page, "perPage": 50},
)
r.raise_for_status()
body = r.json()
all_docs.extend(body["data"])
if page >= body["meta"]["lastPage"]:
break
page += 1
Default sort is year desc, dateRef desc — recent filings appear first.
Beyond download: text extraction
PDFs work for archival and manual review. For NLP, RAG, or LLM agents, request page-level markdown:
curl -X POST -H "Authorization: Bearer $APICVM_KEY" \
-H "Content-Type: application/json" \
-d '{"callback_url":"https://your-app.example.com/callbacks/apicvm","document":{"id":"<document-id>"}}' \
"$APICVM_URL/v1/document-text-extractions"
See Extract Text from CVM PDFs for AI Agents for the callback flow.
Current limitations
- Availability — DFP/ITR exist in apicvm only for companies and periods in the ingested corpus.
- No XBRL API — downloads are PDF originals, not structured XBRL feeds.
- Multiple filings per year — some companies file revisions; always inspect list results.
- Rate limit — 60 req/min per API key for most endpoints; markdown extraction is limited to 1 document per key per UTC day.
Next steps
- Read the API docs
- Get an API key
- Access CVM filings with Python — full resolve → list → download tutorial
- Brazil CVM vs SEC EDGAR — mapping to 10-K/10-Q
Ready to integrate?
Get an API key and start querying Brazilian CVM filings programmatically.