The official C# / .NET SDK for Meteroid, the open-source billing and pricing platform. Meteroid manages subscriptions, usage-based billing and metering, invoicing and revenue analytics; this library calls its REST API and verifies its webhooks, against Meteroid Cloud (https://api.meteroid.com) or a self-hosted instance.
Website · Documentation · API reference · Meteroid on GitHub
dotnet add package MeteroidTargets .NET 8, trimming and native AOT safe. Every method of the API is listed in api.md.
using Meteroid;
using var client = new MeteroidClient("your-api-key");
var addOn = await client.AddOns.RetrieveAsync("addon_id");
Console.WriteLine(addOn);new MeteroidClient() reads the key from METEROID_API_KEY, and METEROID_BASE_URL
overrides the base URL; what you pass, in the constructor or in MeteroidClientOptions, wins.
The default base URL is https://api.meteroid.com.
Create one client and reuse it: it is thread-safe and pools connections. To send requests through
your own HttpClient, pass it first: new MeteroidClient(httpClient, "your-api-key").
Resources hang off the client as properties. Every method is async and takes an optional
RequestOptions (headers, timeout, retries, idempotency key) and a CancellationToken. Models
are records with init properties, compared by value:
using Meteroid.Models;
var onboardingLinkResponse = await client.Connect.CreateOnboardingLinkAsync("id", new CreateOnboardingLinkRequest { RedirectUrl = "redirect_url" });Properties this SDK version does not know are kept in AdditionalProperties and sent back.
Unions are abstract records to match on, and enums keep unknown values too (IsKnown); switch
on status.Value with the Status.Values constants.
Everything the SDK throws derives from MeteroidException:
try
{
await client.AddOns.RetrieveAsync("addon_id");
}
catch (NotFoundException e)
{
Console.WriteLine($"{e.StatusCode} {e.RequestId}: {e.Error}");
}
catch (ApiTimeoutException) { /* no response in time, after the retries */ }
catch (ApiConnectionException) { /* the API could not be reached */ }Error responses are ApiExceptions, as a subclass per status (BadRequestException,
UnauthorizedException, NotFoundException, RateLimitException, ServerErrorException...),
with the raw Body, the Headers, and Error: the body parsed as the error model the operation
declares for the status, else a JsonElement. GetError<T>() parses it as any model. A
successful response the SDK cannot read throws an ApiDecodeException.
Connection errors, timeouts, 408, 429 and 5xx responses are retried twice with jittered backoff
(0.5s, then 1s), honoring Retry-After and retry-after-ms up to a minute (the backoff
otherwise), when the request is idempotent or carries an Idempotency-Key. Each attempt times out after 60 seconds.
var client = new MeteroidClient(options: new() { MaxRetries = 5, Timeout = TimeSpan.FromSeconds(20) });
await client.AddOns.RetrieveAsync("addon_id", requestOptions: new RequestOptions { MaxRetries = 0, Timeout = TimeSpan.FromSeconds(5) });The method of a paginated operation returns a pager. await it for the first page, which has the
properties of the response body, the page's Items, HasNextPage and GetNextPageAsync();
await foreach over it (or over a page) yields every item, fetching the pages as it goes:
var page = await client.AddOns.ListAsync();
Console.WriteLine(page.PaginationMeta);
foreach (var addOn in page.Items)
{
Console.WriteLine(addOn);
}
if (page.HasNextPage)
{
page = await page.GetNextPageAsync();
}
await foreach (var addOn in client.AddOns.ListAsync())
{
Console.WriteLine(addOn);
}
await foreach (var each in client.AddOns.ListAsync().AsPagesAsync())
{
Console.WriteLine(each.Items.Count);
}page.Body is the decoded body itself, with the properties a paging member (Items, Body,
HasNextPage...) shadows on the page. The CancellationToken of the call also cancels the
requests of the following pages; WithCancellation(token) adds one to an enumeration.
WithRawResponse returns the status and headers with the decoded body:
var response = await client.AddOns.WithRawResponse.RetrieveAsync("addon_id");
Console.WriteLine($"{response.StatusCode} {response.RequestId}");IMeteroidClient and an interface per resource let you substitute a fake. Each call is an
Activity of the Meteroid ActivitySource, for OpenTelemetry.
Generated by perseid.