Subscriptions
Get Subscription Usage
Current-period and lifetime usage per dimension, with entitlement headroom
GET
/
v1
/
subscriptions
/
{id}
/
usage
Current-period usage for a subscription
curl --request GET \
--url https://billing.example.com/v1/subscriptions/{id}/usage \
--header 'Authorization: Bearer <token>'import requests
url = "https://billing.example.com/v1/subscriptions/{id}/usage"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://billing.example.com/v1/subscriptions/{id}/usage', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://billing.example.com/v1/subscriptions/{id}/usage",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://billing.example.com/v1/subscriptions/{id}/usage"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://billing.example.com/v1/subscriptions/{id}/usage")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://billing.example.com/v1/subscriptions/{id}/usage")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"data": {
"subscription_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"customer_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"current_period_start": "2023-11-07T05:31:56Z",
"current_period_end": "2023-11-07T05:31:56Z",
"dimensions": [
{
"dimension": "<string>",
"period_quantity": 123,
"lifetime_quantity": 123,
"limit_value": 123,
"remaining": 123
}
]
}
}{
"error": {
"code": "validation_failed",
"message": "<string>"
}
}{
"error": {
"code": "validation_failed",
"message": "<string>"
}
}{
"error": {
"code": "validation_failed",
"message": "<string>"
}
}Per-dimension usage inside the subscription’s current billing period, plus
lifetime totals — the “you’ve used 4,231 of 10,000 API calls” view.
When the subscription’s customer holds an entitlement limit whose
feature_key equals the dimension name, the effective limit_value and the
remaining headroom are joined into the row. Dimensions without a matching
entitlement limit report null for both.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string (UUID) | Yes | Subscription ID (must belong to your tenant) |
Example Request
curl https://api.recurso.dev/v1/subscriptions/9a1f0f9e-.../usage \
-H "Authorization: Bearer $API_KEY"
Response
{
"subscription_id": "9a1f0f9e-52f1-4c11-a1b0-1f6d3f1c2a7e",
"customer_id": "d5b7c1de-8a34-4a37-8f0e-b1a9f3f6f001",
"current_period_start": "2026-07-01T00:00:00Z",
"current_period_end": "2026-08-01T00:00:00Z",
"dimensions": [
{
"dimension": "api_calls",
"period_quantity": 4231,
"lifetime_quantity": 61540,
"limit_value": 10000,
"remaining": 5769
},
{
"dimension": "storage_gb",
"period_quantity": 42,
"lifetime_quantity": 42,
"limit_value": null,
"remaining": null
}
]
}
| Field | Type | Description |
|---|---|---|
subscription_id / customer_id | string | Identifiers echoed back |
current_period_start / current_period_end | string | The billing period period_quantity is measured over |
dimensions[].dimension | string | Usage dimension name |
dimensions[].period_quantity | integer | Total recorded inside the current billing period |
dimensions[].lifetime_quantity | integer | Total ever recorded for the subscription |
dimensions[].limit_value | integer or null | Effective entitlement limit for a feature_key equal to the dimension name; null when none exists |
dimensions[].remaining | integer or null | limit_value - period_quantity; negative when over the limit; null when limit_value is null |
Errors
| Status | When |
|---|---|
400 | Malformed subscription ID |
401 | Missing or invalid API key |
404 | Subscription does not exist or belongs to another tenant |
remaining compares the entitlement limit against the current billing
period’s quantity, not lifetime usage. See the
Usage-Based Billing guide
for the full metering-plus-entitlements pattern.⌘I
Current-period usage for a subscription
curl --request GET \
--url https://billing.example.com/v1/subscriptions/{id}/usage \
--header 'Authorization: Bearer <token>'import requests
url = "https://billing.example.com/v1/subscriptions/{id}/usage"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://billing.example.com/v1/subscriptions/{id}/usage', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://billing.example.com/v1/subscriptions/{id}/usage",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://billing.example.com/v1/subscriptions/{id}/usage"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://billing.example.com/v1/subscriptions/{id}/usage")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://billing.example.com/v1/subscriptions/{id}/usage")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"data": {
"subscription_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"customer_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"current_period_start": "2023-11-07T05:31:56Z",
"current_period_end": "2023-11-07T05:31:56Z",
"dimensions": [
{
"dimension": "<string>",
"period_quantity": 123,
"lifetime_quantity": 123,
"limit_value": 123,
"remaining": 123
}
]
}
}{
"error": {
"code": "validation_failed",
"message": "<string>"
}
}{
"error": {
"code": "validation_failed",
"message": "<string>"
}
}{
"error": {
"code": "validation_failed",
"message": "<string>"
}
}