OpenAI Tools for Brazil CVM Filings
If you want OpenAI tools CVM filings access inside a GPT agent, you need stable HTTP wrappers — not scraped HTML. apicvm exposes resolve, list, download, and async text extraction. There is no official OpenAI SDK for apicvm; you define tools that call /v1/*.
The problem
OpenAI tool-calling models expect JSON schemas and short, reliable results. Brazilian CVM data fails that pattern when:
- Bulk CSV dumps do not map cleanly to
PETR4+ year + filing type. - FRE filings split into dozens of section PDFs.
- PDF bytes cannot go into the model context without extraction.
Architecture
User → OpenAI Responses / Chat Completions (tools)
→ your tool handlers (HTTP)
→ apicvm /v1/*
You own auth, error mapping, and truncation. Keep API keys in your server — never in the model prompt.
Setup
import os, json, requests
BASE = os.environ["APICVM_URL"].rstrip("/")
HEADERS = {"Authorization": f"Bearer {os.environ['APICVM_KEY']}"}
def apicvm_get(path: str, **params):
r = requests.get(f"{BASE}{path}", headers=HEADERS, params=params, timeout=30)
r.raise_for_status()
return r.json()
Tool definitions
Minimal tool schemas for Chat Completions-style APIs:
TOOLS = [
{
"type": "function",
"function": {
"name": "resolve_company",
"description": "Resolve a Brazilian public company by ticker, CNPJ, or name.",
"parameters": {
"type": "object",
"properties": {
"query": {"type": "string"},
"by": {"type": "string", "enum": ["ticker", "cnpj", "name", "auto"]},
},
"required": ["query"],
},
},
},
{
"type": "function",
"function": {
"name": "list_documents",
"description": "List CVM filings. type is DFP, ITR, or FRE.",
"parameters": {
"type": "object",
"properties": {
"ticker": {"type": "string"},
"type": {"type": "string", "enum": ["DFP", "ITR", "FRE"]},
"year": {"type": "integer"},
"name": {"type": "string"},
"perPage": {"type": "integer"},
},
"required": ["ticker", "type", "year"],
},
},
},
]
Handlers
def run_tool(name: str, args: dict):
if name == "resolve_company":
return apicvm_get(
"/v1/companies/resolve",
query=args["query"],
by=args.get("by", "ticker"),
)
if name == "list_documents":
params = {
"ticker": args["ticker"],
"type": args["type"],
"year": args["year"],
"perPage": args.get("perPage", 10),
}
if args.get("name"):
params["name"] = args["name"]
return apicvm_get("/v1/documents", **params)
raise ValueError(name)
Example: list PETR4 DFP 2024, then pick a document.id for download.
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=10"
Download is free (no extraction credits):
def download_filing(document_id: str, path: str) -> str:
r = requests.get(
f"{BASE}/v1/documents/{document_id}/file",
headers=HEADERS,
timeout=120,
)
r.raise_for_status()
open(path, "wb").write(r.content)
return path
Extraction and agents
POST /v1/document-text-extractions is async (callback only, Pro plan). Do not block the tool loop waiting for every page. Patterns that work:
- Pre-extract offline; agents read your store.
- Webhook writes pages; a second tool reads stored markdown.
- Demo path for VALE3 FRE 2025 risk factors:
/v1/demo/*(no key).
See async extraction callbacks and LangChain tools for the same HTTP surface.
Current limitations
- No official OpenAI package for apicvm — HTTP only.
- Extraction is async; Student keys get
403on extract. - Corpus is ingested filings, not live portal coverage.
- Truncate tool responses so long
data[]lists do not blow context.
Next steps
Ready to integrate?
Get an API key and start querying Brazilian CVM filings programmatically.