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:

  1. Pre-extract offline; agents read your store.
  2. Webhook writes pages; a second tool reads stored markdown.
  3. 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 403 on 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.