Brazil Financial Statements API: Access DFP and ITR Filings

Fundamentals pipelines for B3 issuers need the statement layer: annual and quarterly financial packages filed with the CVM. If you are searching for a Brazil financial statements API, you usually want DFP (annual standardized statements) and ITR (Informações Trimestrais — Brazilian quarterly filings, not an income tax return) listable by ticker, with stable document IDs and PDF download. apicvm exposes that workflow. It is not a quotes feed — see Brazil financial data API for the filings-vs-prices split.

The problem

Two common traps:

  1. Quotes vs filings — A Brazil financial API SERP mixes OHLCV vendors with regulatory PDFs. Candles do not replace audited statements. apicvm is the filings side.
  2. Event vs periodic — Material facts and investor decks (IPE types) matter for monitoring, but they are not the DFP/ITR statement stack. See Brazil IPE disclosures API for event-driven filings.

Scraping the CVM portal for one ticker works until you need a watchlist, quarter filters, and reproducible document UUIDs. Bulk open-data ZIPs are dataset-oriented, not ticker-first. You want GET /v1/documents?ticker=PETR4&types=DFP,ITR&year=2025.

DFP and ITR on CVM

type Cadence Role Rough US analogy
DFP Annual Standardized financial statements (Demonstrações Financeiras Padronizadas) 10-K financials
ITR Quarterly Interim financials (Informações Trimestrais) 10-Q

ITR disambiguation: throughout this page, ITR means Brazilian quarterly Informações Trimestrais — not a US income tax return API.

On ITR rows, dateRef is the quarter-end reference date (e.g. 2025-06-30 for Q2). On IPE types, dateRef behaves more like a publication date — do not mix semantics when sorting.

Both types support section-level prefixes via GET /v1/document-prefixes when you need a named slice (balance sheet, cash flow) instead of the full PDF.

How apicvm helps

  1. Resolve the issuer — GET /v1/companies/resolve
  2. List statements — GET /v1/documents with type or comma-separated types=DFP,ITR
  3. Download the PDF — GET /v1/documents/:id/file
  4. Optional (Pro): page-level markdown — POST /v1/document-text-extractions (async callback)

Auth: Authorization: Bearer or X-API-Key. Try VALE3 without a key via the demo API.

On VALE3 for calendar year 2026, the demo corpus held 27 ITR rows (demo API, queried 2026-09-06). Listing types=DFP,ITR for 2025 returned 51 rows. On PETR4 for 2025, the production corpus held 20 DFP and 31 ITR rows (queried 2026-09-06). Counts vary by issuer and ingestion coverage.

Setup

export APICVM_KEY='apicvm_...'
export APICVM_URL='https://apicvm.dev'

Step 1: Resolve the company

curl -H "Authorization: Bearer $APICVM_KEY" \
  "$APICVM_URL/v1/companies/resolve?query=PETR4&by=ticker"

Confirm cnpj and tickers[]. Ambiguous name searches return 409 AMBIGUOUS_RESULT — pin down with by=ticker or by=cnpj.

Step 2: List financial statements (single or multi-type)

Annual DFP only:

curl -H "Authorization: Bearer $APICVM_KEY" \
  "$APICVM_URL/v1/documents?ticker=PETR4&type=DFP&year=2024&perPage=20"

Quarterly ITR for the same issuer:

curl -H "Authorization: Bearer $APICVM_KEY" \
  "$APICVM_URL/v1/documents?ticker=PETR4&type=ITR&year=2025&perPage=50"

List both statement types in one call with types (comma-separated, no spaces):

curl -H "Authorization: Bearer $APICVM_KEY" \
  "$APICVM_URL/v1/documents?ticker=VALE3&types=DFP,ITR&year=2025&perPage=50"

perPage maxes at 50. Paginate with page= when meta.lastPage > 1. Filter a quarter window with dateRef when you know the reference date (e.g. dateRef=2025-06-30 for Q2 2025 ITR).

Step 3: Download a filing

curl -H "Authorization: Bearer $APICVM_KEY" \
  -o statements.pdf \
  "$APICVM_URL/v1/documents/<document-id>/file"

Download does not consume extraction credits.

Example: Python statement pull for a watchlist

import os, requests

BASE = os.environ["APICVM_URL"]
H = {"Authorization": f"Bearer {os.environ['APICVM_KEY']}"}
STATEMENT_TYPES = "DFP,ITR"
WATCH = ["PETR4", "VALE3", "ITUB4"]
YEAR = 2025

def list_statements(ticker):
    r = requests.get(
        f"{BASE}/v1/documents",
        headers=H,
        params={
            "ticker": ticker,
            "types": STATEMENT_TYPES,
            "year": YEAR,
            "perPage": 50,
        },
    )
    r.raise_for_status()
    return r.json()["data"]

for ticker in WATCH:
    docs = list_statements(ticker)
    dfp = [d for d in docs if d["type"] == "DFP"]
    itr = [d for d in docs if d["type"] == "ITR"]
    print(f"{ticker}: {len(dfp)} DFP, {len(itr)} ITR in {YEAR}")
    for d in sorted(itr, key=lambda x: x["dateRef"], reverse=True)[:2]:
        print(f"  ITR  {d['dateRef']}  {d['name'][:55]}")

Sort ITR by dateRef descending to surface the latest quarter first. For earnings-season workflows that also pull presentations and material facts, see Track Brazil earnings season.

Periodic statements vs event disclosures

Layer Types Use
Statements (this page) DFP, ITR Fundamentals, QoQ/YoY models, audit trail
Event IPE FATO_RELEVANTE, COMUNICADO_AO_MERCADO, etc. Guidance, M&A, decks between quarters

Many production pipelines poll both layers on different schedules. The IPE hub is Brazil IPE disclosures API.

Per-type deep dives

Focused guides with SEC analogies and edge cases:

For the full EDGAR-style type map including FRE and material facts, see Brazil SEC filings API.

Current limitations

  • Polling, not push — apicvm does not emit a webhook when CVM publishes a new DFP or ITR. Poll on a schedule and diff document IDs.
  • Ingestion lag — sync is not guaranteed real-time. A filing may appear on the portal before it is in the corpus.
  • Corpus coverage — not every issuer or period is present. Zero rows for a year does not prove the company did not file.
  • No SEC form codes — type=10-K or type=10-Q is rejected. Use DFP and ITR.
  • Demo scope — /v1/demo/* is limited to VALE3. Other tickers need a production key.

Next steps

Ready to integrate?

Get an API key and start querying Brazilian CVM filings programmatically.