Access Brazil CVM Filings with Kotlin
Need a Kotlin CVM filings API client for Brazilian public companies? apicvm is plain HTTPS JSON. Use the JDK HttpClient from Kotlin (or Ktor Client), a Bearer token, and the /v1/* routes — there is no official Kotlin SDK.
The problem
Kotlin services on the JVM often inherit the same pain as Java stacks:
- Scrapers that break when the CVM portal UI changes
- Local ZIP parsers without stable document UUIDs
- One-off scripts that never become a shared client module
A small HTTP wrapper against a fixed contract is easier to keep in a Spring Boot, Ktor, or Gradle multi-module repo.
Setup
export APICVM_URL='https://apicvm.dev'
export APICVM_KEY='apicvm_...' # from /signup
Kotlin on JVM 11+ is enough. The examples below use java.net.http.HttpClient and kotlinx.serialization.json (or you can parse with Jackson the same way as the Java guide).
Resolve a ticker
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
fun main() {
val base = System.getenv("APICVM_URL")
val key = System.getenv("APICVM_KEY")
val client = HttpClient.newHttpClient()
val q = URLEncoder.encode("PETR4", StandardCharsets.UTF_8)
val url = "$base/v1/companies/resolve?query=$q&by=ticker"
val req = HttpRequest.newBuilder(URI.create(url))
.timeout(Duration.ofSeconds(30))
.header("Authorization", "Bearer $key")
.GET()
.build()
val res = client.send(req, HttpResponse.BodyHandlers.ofString())
require(res.statusCode() < 400) { "HTTP ${res.statusCode()}: ${res.body()}" }
println(res.body())
}
List DFP documents
val listUrl = "$base/v1/documents?ticker=PETR4&type=DFP&year=2024&perPage=20"
val listReq = HttpRequest.newBuilder(URI.create(listUrl))
.header("Authorization", "Bearer $key")
.GET()
.build()
val body = client.send(listReq, HttpResponse.BodyHandlers.ofString()).body()
println(body)
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
val docId = "..." // UUID from list response
val fileReq = HttpRequest.newBuilder(
URI.create("$base/v1/documents/$docId/file")
)
.header("Authorization", "Bearer $key")
.GET()
.build()
val pdf = client.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"
Errors and rate limits
Map status codes in your sealed hierarchy or Result type: 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 Kotlin return value. - Student plans cannot start markdown extraction (
403). - 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.