Access Brazil CVM Filings with Java

Need a Java CVM filings API client for Brazilian public companies? apicvm is plain HTTPS JSON. Use java.net.http.HttpClient, a Bearer token, and the /v1/* routes — there is no official Java SDK.

The problem

JVM services that need CVM filings often end up:

  • Wrapping brittle portal scrapers in Selenium
  • Parsing local ZIP dumps without stable document IDs
  • Reimplementing auth for every microservice language

A typed HTTP client against a fixed contract is easier to keep in Spring Boot or Quarkus CI.

Setup

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

Java 11+ is enough (HttpClient is in the JDK). Add Jackson (or Gson) if you want POJOs; the examples below use String + JsonNode from Jackson.

Resolve a ticker

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.time.Duration;

public class ResolveTicker {
  static final ObjectMapper MAPPER = new ObjectMapper();

  public static void main(String[] args) throws Exception {
    String base = System.getenv("APICVM_URL");
    String key = System.getenv("APICVM_KEY");

    String url = base + "/v1/companies/resolve?query="
        + URLEncoder.encode("PETR4", StandardCharsets.UTF_8)
        + "&by=ticker";

    HttpRequest req = HttpRequest.newBuilder(URI.create(url))
        .timeout(Duration.ofSeconds(30))
        .header("Authorization", "Bearer " + key)
        .GET()
        .build();

    HttpResponse<String> res = HttpClient.newHttpClient()
        .send(req, HttpResponse.BodyHandlers.ofString());

    if (res.statusCode() >= 400) {
      throw new RuntimeException("HTTP " + res.statusCode() + ": " + res.body());
    }

    JsonNode company = MAPPER.readTree(res.body());
    System.out.println(company.toPrettyString());
  }
}

List DFP documents

String listUrl = base + "/v1/documents?ticker=PETR4&type=DFP&year=2024&perPage=20";

HttpRequest listReq = HttpRequest.newBuilder(URI.create(listUrl))
    .header("Authorization", "Bearer " + key)
    .GET()
    .build();

JsonNode docs = MAPPER.readTree(
    HttpClient.newHttpClient()
        .send(listReq, HttpResponse.BodyHandlers.ofString())
        .body());

for (JsonNode row : docs.path("data")) {
  System.out.println(row.path("id").asText() + " " + row.path("name").asText());
}

perPage max is 50. Paginate with page until meta.lastPage. See Paginate and filter CVM documents.

Download a PDF

import java.nio.file.Files;
import java.nio.file.Path;

String docId = "..."; // UUID from list response
HttpRequest fileReq = HttpRequest.newBuilder(
        URI.create(base + "/v1/documents/" + docId + "/file"))
    .header("Authorization", "Bearer " + key)
    .GET()
    .build();

byte[] pdf = HttpClient.newHttpClient()
    .send(fileReq, HttpResponse.BodyHandlers.ofByteArray())
    .body();

Files.write(Path.of("petr4-dfp.pdf"), pdf);

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"

Same contract as Go, Rust, and curl.

Errors and rate limits

Map status codes in your exception hierarchy: 401 auth, 404 missing document, 429 rate limit. Read X-RateLimit-Remaining when fan-out across tickers. Details: Handle errors and rate limits.

Current limitations

  • Text extraction (POST /v1/document-text-extractions) is async via HTTPS callback — not a sync Java return value.
  • perPage max is 50.
  • Corpus coverage depends on ingestion lag; resolve before hard-coding CNPJ.

Next steps

Ready to integrate?

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