shrtnr

Dart SDK for shrtnr, a self-hosted URL shortener on Cloudflare Workers. Create short links, manage slugs, and read click analytics.

pub.dev license

Install

dart pub add shrtnr

Quick start

import 'package:shrtnr/shrtnr.dart';

final client = ShrtnrClient(
  baseUrl: 'https://your-shrtnr.example.com',
  apiKey: 'sk_your_api_key',
);

final link = await client.links.create(url: 'https://example.com/very-long-path');
print(link.slugs.first.slug); // 'a3x9'

client.close();

Configuration

ShrtnrClient(
  baseUrl: 'https://your-shrtnr.example.com', // required
  apiKey: 'sk_...',                            // required; from the admin dashboard
  httpClient: customHttpClient,                // optional; inject a custom http.Client
)

The httpClient parameter accepts any http.Client. Pass a custom implementation for test mocking or custom TLS configuration. When omitted, a new http.Client is created and closed by client.close().

Resources

Method Description
get(id, {range?}) Get a link with click count
list({owner?, range?}) List all links
create({url, label?, slugLength?, expiresAt?, allowDuplicate?}) Create a short link
update(id, {url?, label?, expiresAt?}) Update URL, label, or expiry
disable(id) Stop redirecting
enable(id) Resume redirecting
delete(id) Permanently delete
analytics(id, {range?}) Click breakdown by country, device, referrer, etc.
timeline(id, {range?}) Click counts bucketed over time
qr(id, {slug?, size?}) QR code as SVG string
bundles(id) Bundles this link belongs to
// Shorten a URL
final link = await client.links.create(url: 'https://example.com', label: 'Landing page');

// Get a 7-day click count
final fresh = await client.links.get(link.id, range: '7d');

// Full analytics for the last 30 days
final stats = await client.links.analytics(link.id, range: '30d');
print('${stats.totalClicks} clicks, ${stats.numCountries} countries');

Slugs (client.slugs)

Method Description
lookup(slug) Find a link by slug
add(linkId, slug) Add a custom slug
disable(linkId, slug) Disable a slug
enable(linkId, slug) Re-enable a slug
remove(linkId, slug) Remove a slug
// Add a campaign slug then disable it when the campaign ends
await client.slugs.add(link.id, 'spring-sale');
await client.slugs.disable(link.id, 'spring-sale');

// Look up a link by its slug
final found = await client.slugs.lookup('spring-sale');

Bundles (client.bundles)

Groups of related links with combined analytics.

Method Description
get(id, {range?}) Get a bundle with click summary
list({archived?, range?}) List bundles
create({name, description?, icon?, accent?}) Create a bundle
update(id, {name?, description?, icon?, accent?}) Update metadata
delete(id) Permanently delete
archive(id) Hide from default listing
unarchive(id) Restore an archived bundle
analytics(id, {range?}) Combined click analytics
links(id) List links in the bundle
addLink(id, linkId) Add a link
removeLink(id, linkId) Remove a link
// Create a bundle and add links to it
final bundle = await client.bundles.create(name: 'Spring 2026', accent: 'green');
await client.bundles.addLink(bundle.id, linkA.id);
await client.bundles.addLink(bundle.id, linkB.id);

// Combined analytics for the last 7 days
final stats = await client.bundles.analytics(bundle.id, range: '7d');
print(stats.totalClicks);

Models

All model fields use camelCase. The SDK maps snake_case JSON from the wire automatically inside each fromJson factory.

Key types exported from package:shrtnr/shrtnr.dart:

  • Link, Slug, Bundle, BundleWithSummary, BundleTopLink
  • ClickStats, TimelineData, TimelineBucket, TimelineSummary, NameCount
  • DateClickCount, SlugClickCount
  • DeletedResult, AddedResult, RemovedResult

Timestamp fields (createdAt, expiresAt, disabledAt, archivedAt, updatedAt) are plain int Unix seconds, matching the wire format exactly.

Errors

Every 4xx/5xx response throws ShrtnrError. Network failures also throw ShrtnrError with status: 0.

import 'package:shrtnr/shrtnr.dart';

try {
  await client.links.get(99999);
} on ShrtnrError catch (err) {
  print(err.status);         // 404
  print(err.serverMessage);  // 'not found'
  print(err);                // 'ShrtnrError(HTTP 404): not found'
}

See also

Attribution

shrtnr is developed and maintained by Oddbit.

If you publish a fork or derivative work, retain the license and notice files, preserve applicable copyright and attribution notices, and clearly indicate that your version has been modified.

Libraries

shrtnr
Dart client for the shrtnr URL shortener API.