Dart SDK

Unpublished

Async (Future-based); zero dependencies.

Initialize

Construct with your API key (the factory throws on an empty key). Construction is synchronous.

example.dart
import 'dart:io';
import 'package:counters/counters.dart';

final client = CountersClient(
  apiKey: Platform.environment['COUNTERS_API_KEY']!,
);

Count

add and subtract are buffered (synchronous, fire-and-forget) and coalesced per counter into one POST /batch; addNow/subtractNow are async and return the counter. Amounts accept an int, a decimal String, or a BigInt.

count
final reg = client.counter('registrations');

reg.add(1);           // buffered
reg.add(5);           // coalesced
await client.flush(); // one POST /batch

final c = await reg.addNow(1); // immediate
print(c.value);       // "42"

Read

Read the current value, or a per-bucket time series. SeriesParams takes native DateTime values.

read
final value = (await reg.value()).value;

final series = await reg.series(SeriesParams(
  from: DateTime.utc(2026, 1, 1),
  to: DateTime.utc(2026, 1, 8),
  bucket: '1d',
));

Manage

clear resets to zero (new epoch, history retained); delete tombstones the counter; list pages the counters in the organization.

manage
await reg.clear();
await reg.delete();

final page = await client.list(limit: 50);

Lifecycle

Buffered writes flush on a background timer and at maxBatchSize. Always await close() before your program exits, or buffered writes are lost.

lifecycle
await client.close(); // flush + cancel the timer

Errors

Bad keys or amounts throw CountersValidationException before any request. Failed requests throw CountersApiException (status, title, problem); network/retry exhaustion throws CountersTransportException. All extend CountersException.

errors
try {
  await reg.addNow(1);
} on CountersValidationException {
  rethrow;                        // bad input
} on CountersApiException catch (e) {
  print(e.status);
  print(e.title);
}
Prefer raw HTTP? See the REST API reference. Create a key in your dashboard.