C# SDK
UnpublishedFully async, .NET 8+, zero dependencies, arbitrary precision via BigInteger.
Not yet published
Initialize
Construct with an options object. CountersClient is IAsyncDisposable — prefer await using.
using Counters.Sdk;
await using var client = new CountersClient(new CountersClientOptions
{
ApiKey = Environment.GetEnvironmentVariable("COUNTERS_API_KEY")!,
});Count
Add and Subtract are buffered and coalesced per counter (flushed as one POST /batch); AddNowAsync/SubtractNowAsync write immediately and return the counter. Each is overloaded for long, string, and BigInteger amounts.
var reg = client.Counter("registrations");
reg.Add(1); // buffered
reg.Add(5); // coalesced
await client.FlushAsync(); // one POST /batch
Counter c = await reg.AddNowAsync(1); // immediate
Console.WriteLine(c.Value); // "42"Read
Read the current value, or a per-bucket time series. Every network call is async and accepts an optional CancellationToken.
var value = (await reg.ValueAsync()).Value;
var series = await reg.SeriesAsync(new SeriesParams
{
From = DateTimeOffset.Parse("2026-01-01T00:00:00Z"),
To = DateTimeOffset.Parse("2026-01-08T00:00:00Z"),
Bucket = "1d",
});Manage
ClearAsync resets to zero (new epoch, history retained); DeleteAsync tombstones the counter; ListAsync pages the counters in the organization.
await reg.ClearAsync();
await reg.DeleteAsync();
var page = await client.ListAsync(limit: 50);Lifecycle
Buffered writes flush on a background timer and at maxBatchSize. await using disposes the client (flush + stop); or call CloseAsync yourself.
await client.CloseAsync(); // flush + stop the background timerErrors
Bad keys or amounts throw CountersValidationException before any request. Failed requests throw CountersApiException (Status, Title, Problem); both extend CountersException.
try
{
await reg.AddNowAsync(1);
}
catch (CountersValidationException)
{
throw; // bad input
}
catch (CountersApiException e)
{
Console.Error.WriteLine($"{e.Status} {e.Title}");
}