Access Brazil CVM Filings with Elixir

Need an Elixir CVM filings API client for Brazilian public companies? apicvm is plain HTTPS JSON. Use Req or HTTPoison with Bearer auth — there is no official Hex package.

The problem

Phoenix apps and Oban workers that archive Brazil filings often scrape the CVM portal or unpack yearly ZIPs. That breaks CI and loses document identity. A fixed /v1 contract is easier to supervise.

Setup

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

Add {:req, "~> 0.5"} (or HTTPoison) to mix.exs.

Resolve a ticker

base = System.fetch_env!("APICVM_URL")
key = System.fetch_env!("APICVM_KEY")

{:ok, %{body: body}} =
  Req.get(base <> "/v1/companies/resolve",
    headers: [{"authorization", "Bearer " <> key}],
    params: [query: "PETR4", by: "ticker"]
  )

IO.inspect(body)

List documents

{:ok, %{body: docs}} =
  Req.get(base <> "/v1/documents",
    headers: [{"authorization", "Bearer " <> key}],
    params: [ticker: "VALE3", type: "ITR", year: "2024", perPage: 20]
  )

Enum.each(docs["data"], fn d -> IO.puts("#{d["id"]} #{d["name"]}") end)

Download a PDF

doc_id = hd(docs["data"])["id"]

{:ok, %{body: pdf}} =
  Req.get(base <> "/v1/documents/#{doc_id}/file",
    headers: [{"authorization", "Bearer " <> key}],
    decode_body: false
  )

File.write!("vale3-itr.pdf", pdf)

Oban sketch

Enqueue a job per ticker watchlist item: resolve → list new IDs since last run → download → store UUID. Do not invent push webhooks from CVM — apicvm list endpoints are pull-based.

Current limitations

  • Text extraction is async via HTTPS callback (Pro), not a sync Req return
  • perPage max 50
  • Empty data arrays mean corpus gap, not "ticker invalid" — always resolve first

Next steps

Ready to integrate?

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