Skip to main content
Billing answers what you have consumed and what you have been charged. It is global — one endpoint, one bill, no region segment:
One bill covers your whole organization. Usage from every account inside it rolls up into a single monthly invoice, so there is no X-Account-Id on these calls — the organization you authenticated as is the whole scope.
The billing API is read-only, deliberately. Every operation here answers a question; none of them moves money. Settling an invoice, adding or changing a payment method, and billing configuration are console flows at console.basaltic.sh and have no API equivalent. If you are looking for an endpoint to pay an invoice programmatically, there is not one.

Prices

Public and unauthenticated — the whole catalogue, identical for everyone.

Usage and invoices

What is accruing now, and what has already been billed.

Credits

How grants are consumed and where they show up on an invoice.

The collection timeline

Retry days, past due, and what non-payment eventually costs you.

The public price catalogue

GET /v1/prices takes no credentials. It is one of only two unauthenticated endpoints on the platform — the other is GET https://iam.basaltic.sh/v1/regions.
It is public because there is nothing tenant-specific in it. There are no account-level rates, discounts or committed-use terms in this table — every caller gets the same numbers, which is precisely why it is safe to publish and useful to read. It exists so that a pricing page or a cost estimator reads the rate billing will actually charge, instead of keeping its own copy that drifts the next time something is repriced.
Because it takes no credentials, the budget is counted per client IP: 100 requests per minute. Read X-RateLimit-Remaining and X-RateLimit-Reset rather than hard-coding that; on a 429, wait Retry-After seconds, since retrying early extends the window. Responses carry Cache-Control: public, max-age=300 — the catalogue changes when something is repriced, not per request, so caching it for five minutes costs you nothing.

Filters

family is how the managed products are told apart from the general compute flavors they share a resource_type with — load balancer replicas and database cluster nodes are billed as instances, but they are their own family. at is what you use to explain a past invoice: pass the invoice’s period_start and you get the rates that were in force then. as_of in the response echoes the instant the rows were selected as of, so a client can say which revision of the catalogue it is holding.
Money is a decimal string, never a JSON number, everywhere in this API. "0.085" survives a round trip through any language’s JSON parser exactly; a float does not. The quoted rate is the one that will be charged, so it cannot be allowed to round differently on the way out.
There is no pagination on this endpoint. The catalogue is the whole answer — a client that had to page it could observe half of one revision and half of the next.

Month-to-date usage

Returns unbilled usage accrued so far in the current UTC month, with a per-SKU breakdown ordered by cost:
Line amount carries four decimal places while the total carries two. That is not inconsistency — early in a month a line can be worth a fraction of a cent, and rounding it to two places would render it as 0.00 and make it look like nothing is accruing. The total, and every figure on an invoice, stays at the ledger’s two.

Invoices

An invoice is generated on the 1st of each month, covering the previous UTC calendar month, one per organization.
period_start is the first day of the billed month and period_end is exclusive — the first day of the following month. Late-arriving usage from older months sweeps into the next invoice generated rather than reopening a closed one, so an invoice’s line items are not always confined to its labelled period. The arithmetic is subtotal - credits_applied = total. Usage lines carry kind: "usage"; credit lines carry kind: "credit" and a negative amount.
items is populated only on the detail endpoint. GET /v1/invoices returns the invoice documents without line items, because a list of a year’s invoices with every line expanded is a large response nobody asked for.

Statuses

due_at equals issued_at. An invoice is due when it is issued, and the first charge attempt happens immediately — the days that follow are retries, not a grace period.
Small invoices are written off rather than charged. A total below 1.00 in the invoice currency is written off at generation, and the invoice reads paid without any payment ever having been attempted. The cost of collecting a sub-unit amount exceeds the amount.

The PDF statement

Rendered on demand from the invoice’s current state, under the same authorization as the invoice document — there is no stored file to drift out of sync with the status it shows. The pdf_url field on an invoice is the path to this endpoint, not a pre-signed link you can hand to someone else.

Credits

A credit grant carries the amount it was issued for and the remaining balance, plus a sourcepromo, coupon, adjustment or migration — and an optional expires_at. Grants are consumed at invoice generation, soonest-expiring first, until the subtotal is covered. Each consumed slice becomes its own negative line on the invoice and its own credit_applied entry in the ledger, so you can always trace which grant paid for what.
Credits are applied automatically. There is no endpoint to apply one to a particular invoice, and none to redeem a code — a code is redeemed in the console, which is what creates the grant.

Transactions and payments

GET /v1/transactions is the ledger: payment, refund, adjustment, credit_grant and credit_applied entries.
Transaction amount is always positive. The direction lives in type, not in the sign. Summing amounts without reading types gives you a number that means nothing.
GET /v1/payments lists charge attempts. Each row carries attempt, a 1-based counter within the collection schedule for its invoice, and a status of pending, processing, succeeded, failed or refunded. Several rows against one invoice is the normal shape of a retry sequence, not a sign of duplicate charges.

What happens to an unpaid invoice

Collection runs on a fixed schedule from the moment the invoice is issued: If none of the three attempts collects, the invoice moves to past_due.
Non-payment eventually costs you your resources. At day 7 the organization is suspended. At day 15 it is terminated, and its resources are deleted after that. Every step re-checks the invoice first, so settling it at any point stops the sequence immediately.
Settle a past_due invoice from the console. That is also where you fix the payment method that caused the declines — the API has no path to either.

Pagination

GET /v1/invoices, /v1/credits, /v1/transactions and /v1/payments all page the same way: pass limit (default 20, maximum 100) and echo back meta.marker from the previous page.
A limit above the maximum is clamped, not rejected, so a page shorter than the one you asked for is normal. Page until meta.has_more is false — not until a page looks short.

Permissions

GET /v1/prices needs no permission, because it needs no identity.
Billing policies cannot be scoped to a resource. Every billing action authorizes against * — the organization boundary is the whole tenant fence here, since there is one bill and it belongs to the organization rather than to any account inside it. A policy granting billing:GetInvoice grants it for every invoice; there is no way to narrow it to one.Grant billing read access at the group level to the people who need it, not broadly. See policies.

Next

Rate limits

How the X-RateLimit-* headers work, and signing every other request.

Policies

Who in your organization can read the bill.

Regions

Why billing has no region segment.

API reference

Every billing operation, with request and response schemas.