Paginate and Filter CVM Documents via API
A useful CVM documents API filter lets you ask precise questions: “PETR4 DFP for 2024”, “ITR + DFP for one year”, or “FRE sections whose name contains risk”. apicvm exposes those filters on GET /v1/documents, with pagination capped so clients stay predictable under rate limits.
The problem
Brazilian issuers file many PDFs per year. FRE packages alone can return dozens of section files. Without filters you page through noise; without pagination you hit perPage validation errors or blow your request budget. Product code needs stable query parameters, not ad-hoc CSV joins.
Filter parameters
| Parameter | Type | Use |
|---|---|---|
ticker |
string | Issuer ticker (e.g. PETR4) |
cnpj |
string | Company tax id |
companyId |
integer | Internal company id |
companyName |
string | Partial company name |
type |
string | Single type: DFP, ITR, FRE, … |
types |
string | Comma-separated: DFP,ITR |
year |
integer | Filing year |
dateRef |
string | YYYY-MM-DD or "Sem data" |
name |
string | Partial document name |
search |
string | Name or type search |
id |
UUID | Exact document id |
Combine filters liberally. Typical product query: ticker + type + year.
Pagination
| Parameter | Rules |
|---|---|
page |
1-based page index |
perPage |
Integer 1–50 (default 10) |
field / order |
Sort control when supported by the list endpoint |
Exceeding perPage=50 returns 422 validation errors. Loop pages until data is empty or shorter than perPage.
Example: curl — DFP for PETR4
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&page=1"
Multiple types in one call
curl -H "Authorization: Bearer $APICVM_KEY" \
"$APICVM_URL/v1/documents?ticker=PETR4&types=DFP,ITR&year=2024&perPage=50"
Narrow FRE by name
curl -H "Authorization: Bearer $APICVM_KEY" \
"$APICVM_URL/v1/documents?ticker=VALE3&type=FRE&year=2025&name=DescricaoFatoresRisco&perPage=5"
Example: Python — walk all pages
import os, requests
BASE = os.environ["APICVM_URL"]
H = {"Authorization": f"Bearer {os.environ['APICVM_KEY']}"}
def list_all(**params):
page, out = 1, []
while True:
r = requests.get(
f"{BASE}/v1/documents",
headers=H,
params={**params, "page": page, "perPage": 50},
)
r.raise_for_status()
batch = r.json()["data"]
out.extend(batch)
if len(batch) < 50:
break
page += 1
return out
docs = list_all(ticker="PETR4", type="ITR", year=2024)
print(len(docs), "ITR files")
for d in docs[:3]:
print(d["id"], d["name"], d["dateRef"])
Each item includes id, type, year, dateRef, name, and nested company with tickers — enough to choose a file before GET /v1/documents/:id/file.
Choosing filters for common jobs
| Job | Suggested filters |
|---|---|
| Annual package | ticker + type=DFP + year |
| Quarterly monitor | ticker + type=ITR + year (then sort client-side by dateRef) |
| Both financial forms | types=DFP,ITR + year |
| FRE section hunt | type=FRE + name= partial section title |
| Exact known file | id= (skip broad lists) |
FRE years often return many section PDFs. Prefer name filters (for example risk factors) instead of downloading every page of every file. See also filter by section name.
Validation errors on pagination
perPage outside 1–50 returns 422 with VALIDATION_FAILED and field messages in error.details. Fix the client — do not retry the same invalid value.
# Bad: perPage=100 → 422
curl -H "Authorization: Bearer $APICVM_KEY" \
"$APICVM_URL/v1/documents?ticker=PETR4&type=DFP&year=2024&perPage=100"
From list → file → text
Filtering only returns metadata. Next steps:
- Pick
document.idfromdata[] GET /v1/documents/:idif you need a single metadata refreshGET /v1/documents/:id/filefor the original PDFPOST /v1/document-text-extractionswhen you need page markdown (async callback)
Keep list queries cheap (perPage just large enough) so watchlist jobs stay inside the rate limit window.
Current limitations
perPagemaximum is 50.- Coverage is the ingested corpus (primarily DFP, ITR, FRE), not every CVM portal category.
- Filtering by
nameis partial-match on stored document names; naming conventions vary by issuer. typesaccepts comma-separated values; unknown types simply yield emptydatafor that slice of the corpus.
Next steps
Ready to integrate?
Get an API key and start querying Brazilian CVM filings programmatically.