Access Brazil CVM Filings with Go
Need a Go CVM filings API client for Brazilian public companies? apicvm is plain HTTP — use net/http, Bearer auth, and JSON. There is no official Go SDK; you call /v1/* directly.
The problem
Go services that ingest Brazil filings usually hit one of:
- Portal ZIP downloads that break CI when HTML changes
- Local CSV dumps that do not map to
ticker + type + year - Missing stable document IDs for caching and retries
Setup
export APICVM_URL='https://apicvm.dev'
export APICVM_KEY='apicvm_...' # from /signup
Minimal client
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"os"
)
func apicvmGET(path string, q url.Values) (map[string]any, error) {
base := os.Getenv("APICVM_URL")
u, _ := url.Parse(base + path)
u.RawQuery = q.Encode()
req, _ := http.NewRequest(http.MethodGet, u.String(), nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("APICVM_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
if res.StatusCode >= 400 {
return nil, fmt.Errorf("status %d: %s", res.StatusCode, body)
}
var out map[string]any
return out, json.Unmarshal(body, &out)
}
func main() {
company, err := apicvmGET("/v1/companies/resolve", url.Values{
"query": {"PETR4"},
"by": {"ticker"},
})
if err != nil {
panic(err)
}
fmt.Println(company)
docs, err := apicvmGET("/v1/documents", url.Values{
"ticker": {"PETR4"},
"type": {"DFP"},
"year": {"2024"},
"perPage": {"20"},
})
if err != nil {
panic(err)
}
fmt.Println(docs["data"])
}
Download a PDF
func downloadFile(docID, outPath string) error {
req, _ := http.NewRequest(
http.MethodGet,
os.Getenv("APICVM_URL")+"/v1/documents/"+docID+"/file",
nil,
)
req.Header.Set("Authorization", "Bearer "+os.Getenv("APICVM_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer res.Body.Close()
f, err := os.Create(outPath)
if err != nil {
return err
}
defer f.Close()
_, err = io.Copy(f, res.Body)
return err
}
File download does not consume extraction credits.
curl cross-check
curl -H "Authorization: Bearer $APICVM_KEY" \
"$APICVM_URL/v1/companies/resolve?query=PETR4&by=ticker"
Paginate documents
perPage max is 50. Walk pages until meta.lastPage:
func listAll(ticker, docType, year string) ([]any, error) {
var all []any
for page := 1; ; page++ {
payload, err := apicvmGET("/v1/documents", url.Values{
"ticker": {ticker},
"type": {docType},
"year": {year},
"page": {fmt.Sprintf("%d", page)},
"perPage": {"50"},
})
if err != nil {
return nil, err
}
data, _ := payload["data"].([]any)
all = append(all, data...)
meta, _ := payload["meta"].(map[string]any)
last := 1
if meta != nil {
if v, ok := meta["lastPage"].(float64); ok {
last = int(v)
}
}
if page >= last {
break
}
}
return all, nil
}
Errors and rate limits
Map HTTP status codes explicitly: 401 auth, 404 missing document, 429 rate limit. Read X-RateLimit-Remaining when batching tickers. Details: Handle errors and rate limits.
Current limitations
- Text extraction (
POST /v1/document-text-extractions) is async via callback — not a sync Go return value. perPagemax is 50; paginate withpage.- Corpus coverage depends on ingestion lag.
Next steps
Ready to integrate?
Get an API key and start querying Brazilian CVM filings programmatically.