Kotlin SDK
UnpublishedBlocking; JDK 17+; zero dependencies.
Not yet published
Initialize
Build a client with the Builder (the constructor is private). CountersClient is Closeable — prefer use { }.
import dev.counters.sdk.CountersClient
val client = CountersClient.builder(System.getenv("COUNTERS_API_KEY")).build()Count
add and subtract are buffered and coalesced per counter into one POST /batch; addNow/subtractNow are blocking and return the counter. Each is overloaded for Long, String, and BigInteger amounts. Calls are synchronous and thread-safe.
val reg = client.counter("registrations")
reg.add(1) // buffered
reg.add(5) // coalesced
client.flush() // one POST /batch
val c = reg.addNow(1) // immediate
println(c.value) // "42"Read
Read the current value, or a per-bucket time series via a SeriesParams data class (OffsetDateTime from/to). Kotlin is the one SDK that rejects an unknown bucket client-side.
val value = reg.value().value
val series = reg.series(SeriesParams(
from = OffsetDateTime.parse("2026-01-01T00:00:00Z"),
to = OffsetDateTime.parse("2026-01-08T00:00:00Z"),
bucket = "1d",
))Manage
clear resets to zero (new epoch, history retained); delete tombstones the counter; list pages the counters in the organization.
reg.clear()
reg.delete()
val page = client.list(null, 50) // cursor, limitLifecycle
Buffered writes flush on a daemon thread that will not keep the JVM alive — so you must close() (or use { }) before exit, or buffered writes are lost.
client.close() // flush + stop the flusher thread
// or: CountersClient.builder(key).build().use { c -> /* ... */ }Errors
Bad keys or amounts throw CountersValidationException. Failed requests throw CountersApiException (status, title, problem); network failures throw CountersTransportException. The base CountersException is sealed, so a when over it is exhaustive.
try {
reg.addNow(1)
} catch (e: CountersValidationException) {
throw e // bad input
} catch (e: CountersApiException) {
println(e.status)
println(e.title)
}