# Usage

What was streamed in a month and what it cost, stream by stream, and per
customer -- what a partner bills its own customers from.

Base URL: `https://api.cardinalstreaming.com`. Authenticate with `Authorization: Bearer <your API key>`.

## Read a month's usage

`GET /v1/usage`

What was streamed in a month (UTC) and what it cost, broken down stream
by stream. With `customer_ref`, one customer's -- what a partner bills
that customer from.

### Query parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `month` | string | no | `YYYY-MM`. Defaults to the current month (UTC). |
| `customer_ref` | string | no | Only this customer's usage. |

### Examples

cURL:

```bash
curl "https://api.cardinalstreaming.com/v1/usage?month=2026-09&customer_ref=grace-chapel" \
  -H "Authorization: Bearer $CARDINAL_API_KEY"
```

Python:

```python
import os

import requests

response = requests.get(
    "https://api.cardinalstreaming.com/v1/usage",
    headers={
        "Authorization": f"Bearer {os.environ['CARDINAL_API_KEY']}",
    },
    params={
        "month": "2026-09",
        "customer_ref": "grace-chapel",
    },
    timeout=30,
)
print(response.status_code, response.text)
```

JavaScript:

```js
const response = await fetch('https://api.cardinalstreaming.com/v1/usage?month=2026-09&customer_ref=grace-chapel', {
  method: 'GET',
  headers: {
    Authorization: `Bearer ${process.env.CARDINAL_API_KEY}`,
  },
});
const data = await response.json();
console.log(response.status, data);
```

TypeScript:

```ts
const response: Response = await fetch('https://api.cardinalstreaming.com/v1/usage?month=2026-09&customer_ref=grace-chapel', {
  method: 'GET',
  headers: {
    Authorization: `Bearer ${process.env.CARDINAL_API_KEY}`,
  },
});
const data: unknown = await response.json();
console.log(response.status, data);
```

PHP:

```php
<?php
require 'vendor/autoload.php'; // composer require guzzlehttp/guzzle

$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.cardinalstreaming.com/v1/usage', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('CARDINAL_API_KEY'),
    ],
    'query' => [
        'month' => '2026-09',
        'customer_ref' => 'grace-chapel',
    ],
    'http_errors' => false,
]);
echo $response->getStatusCode(), ' ', $response->getBody(), PHP_EOL;
```

Ruby:

```ruby
require 'json'
require 'net/http'

uri = URI("https://api.cardinalstreaming.com/v1/usage?month=2026-09&customer_ref=grace-chapel")
request = Net::HTTP::Get.new(uri)
request['Authorization'] = "Bearer #{ENV.fetch('CARDINAL_API_KEY')}"

response = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(request) }
puts response.code, response.body
```

Go:

```go
package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
)

func main() {
	req, err := http.NewRequest("GET", "https://api.cardinalstreaming.com/v1/usage?month=2026-09&customer_ref=grace-chapel", nil)
	if err != nil {
		panic(err)
	}
	req.Header.Set("Authorization", "Bearer "+os.Getenv("CARDINAL_API_KEY"))

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()
	data, _ := io.ReadAll(resp.Body)
	fmt.Println(resp.StatusCode, string(data))
}
```

Java:

```java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Example {
    public static void main(String[] args) throws Exception {
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://api.cardinalstreaming.com/v1/usage?month=2026-09&customer_ref=grace-chapel"))
            .header("Authorization", "Bearer " + System.getenv("CARDINAL_API_KEY"))
            .method("GET", HttpRequest.BodyPublishers.noBody())
            .build();
        HttpResponse<String> response = HttpClient.newHttpClient()
            .send(request, HttpResponse.BodyHandlers.ofString());
        System.out.println(response.statusCode() + " " + response.body());
    }
}
```

C#:

```csharp
using System.Net.Http.Headers;
using System.Text;

using var client = new HttpClient();
using var request = new HttpRequestMessage(new HttpMethod("GET"), "https://api.cardinalstreaming.com/v1/usage?month=2026-09&customer_ref=grace-chapel");
request.Headers.Authorization = new AuthenticationHeaderValue(
    "Bearer", Environment.GetEnvironmentVariable("CARDINAL_API_KEY"));

using var response = await client.SendAsync(request);
Console.WriteLine($"{(int)response.StatusCode} {await response.Content.ReadAsStringAsync()}");
```

### Response 200

The month's usage.

| Name | Type | Required | Description |
|---|---|---|---|
| `month` | string | yes | `YYYY-MM`. |
| `customer_ref` | string | no | Your identifier for a customer. 1-128 letters, digits, `.`, `_` or `-`; not `.` or `..`. |
| `stream_hours_used` | number | yes |  |
| `restream_destination_hours_used` | number | yes |  |
| `captions_hours_used` | number | no |  |
| `translation_language_hours_used` | number | no |  |
| `stream_hour_cap` | number or null | yes | Your organization's monthly cap, or null for none. |
| `stream_hours_remaining` | number or null | yes |  |
| `streams` | array of object | yes |  |
| `streams[].playback_id` | string | yes |  |
| `streams[].customer_ref` | string | yes | Your identifier for a customer. 1-128 letters, digits, `.`, `_` or `-`; not `.` or `..`. |
| `streams[].title` | string | yes |  |
| `streams[].tier` | string | yes | `standard_1080p` ($5 per stream-hour) or `pro_4k` ($20 per stream-hour). One of `"standard_1080p"`, `"pro_4k"`. |
| `streams[].billable_minutes` | integer | yes |  |
| `streams[].unit_rate_usd_per_hour` | number | yes |  |
| `streams[].amount_usd` | number | yes |  |
| `streams[].occurred_at` | string (date-time) | yes |  |
| `streams[].restream_billable_minutes` | integer | yes |  |
| `streams[].restream_destinations` | integer | yes |  |
| `streams[].restream_unit_rate_usd_per_hour` | number or null | yes |  |
| `streams[].restream_amount_usd` | number | yes |  |
| `streams[].restream_destination_minutes` | array of integer | yes |  |
| `streams[].end_reason` | string or null | yes |  |
| `streams[].started_at` | string (date-time) or null | yes |  |
| `streams[].captions` | object | no |  |
| `streams[].captions.captions_billable_minutes` | integer | yes |  |
| `streams[].captions.captions_amount_usd` | number | yes |  |
| `streams[].captions.translations` | array of object | yes |  |
| `streams[].captions.translations[].language` | string | yes |  |
| `streams[].captions.translations[].name` | string | yes |  |
| `streams[].captions.translations[].billable_minutes` | integer | yes |  |
| `streams[].captions.translations[].unit_rate_usd_per_hour` | number | yes |  |
| `streams[].captions.translations[].amount_usd` | number | yes |  |
| `streams[].captions.translation_billable_minutes` | integer | yes |  |
| `streams[].captions.translation_amount_usd` | number | yes |  |

### Errors

| Status | Code | Meaning |
|---|---|---|
| 400 | `invalid_month` | `month` is not `YYYY-MM`. |
| 400 | `invalid_customer_ref` | `customer_ref` is not a valid customer reference. |

## Read usage month by month

`GET /v1/usage/months`

One entry per calendar month (UTC), oldest first, with zero months
included: broadcasts, billed stream minutes by tier, every priced line
and the total in cents -- the amounts the month's invoice uses. The
current month is the invoice so far (broadcasts that have ended).

### Query parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `from` | string | no | First month, `YYYY-MM`. Defaults to eleven months before `to`. |
| `to` | string | no | Last month, `YYYY-MM`, inclusive. Defaults to the current month. At most 36 months from `from`. |
| `customer_ref` | string | no | Only this customer's usage. |

### Examples

cURL:

```bash
curl "https://api.cardinalstreaming.com/v1/usage/months?from=2026-04&to=2026-09" \
  -H "Authorization: Bearer $CARDINAL_API_KEY"
```

Python:

```python
import os

import requests

response = requests.get(
    "https://api.cardinalstreaming.com/v1/usage/months",
    headers={
        "Authorization": f"Bearer {os.environ['CARDINAL_API_KEY']}",
    },
    params={
        "from": "2026-04",
        "to": "2026-09",
    },
    timeout=30,
)
print(response.status_code, response.text)
```

JavaScript:

```js
const response = await fetch('https://api.cardinalstreaming.com/v1/usage/months?from=2026-04&to=2026-09', {
  method: 'GET',
  headers: {
    Authorization: `Bearer ${process.env.CARDINAL_API_KEY}`,
  },
});
const data = await response.json();
console.log(response.status, data);
```

TypeScript:

```ts
const response: Response = await fetch('https://api.cardinalstreaming.com/v1/usage/months?from=2026-04&to=2026-09', {
  method: 'GET',
  headers: {
    Authorization: `Bearer ${process.env.CARDINAL_API_KEY}`,
  },
});
const data: unknown = await response.json();
console.log(response.status, data);
```

PHP:

```php
<?php
require 'vendor/autoload.php'; // composer require guzzlehttp/guzzle

$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.cardinalstreaming.com/v1/usage/months', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('CARDINAL_API_KEY'),
    ],
    'query' => [
        'from' => '2026-04',
        'to' => '2026-09',
    ],
    'http_errors' => false,
]);
echo $response->getStatusCode(), ' ', $response->getBody(), PHP_EOL;
```

Ruby:

```ruby
require 'json'
require 'net/http'

uri = URI("https://api.cardinalstreaming.com/v1/usage/months?from=2026-04&to=2026-09")
request = Net::HTTP::Get.new(uri)
request['Authorization'] = "Bearer #{ENV.fetch('CARDINAL_API_KEY')}"

response = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(request) }
puts response.code, response.body
```

Go:

```go
package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
)

func main() {
	req, err := http.NewRequest("GET", "https://api.cardinalstreaming.com/v1/usage/months?from=2026-04&to=2026-09", nil)
	if err != nil {
		panic(err)
	}
	req.Header.Set("Authorization", "Bearer "+os.Getenv("CARDINAL_API_KEY"))

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()
	data, _ := io.ReadAll(resp.Body)
	fmt.Println(resp.StatusCode, string(data))
}
```

Java:

```java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Example {
    public static void main(String[] args) throws Exception {
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://api.cardinalstreaming.com/v1/usage/months?from=2026-04&to=2026-09"))
            .header("Authorization", "Bearer " + System.getenv("CARDINAL_API_KEY"))
            .method("GET", HttpRequest.BodyPublishers.noBody())
            .build();
        HttpResponse<String> response = HttpClient.newHttpClient()
            .send(request, HttpResponse.BodyHandlers.ofString());
        System.out.println(response.statusCode() + " " + response.body());
    }
}
```

C#:

```csharp
using System.Net.Http.Headers;
using System.Text;

using var client = new HttpClient();
using var request = new HttpRequestMessage(new HttpMethod("GET"), "https://api.cardinalstreaming.com/v1/usage/months?from=2026-04&to=2026-09");
request.Headers.Authorization = new AuthenticationHeaderValue(
    "Bearer", Environment.GetEnvironmentVariable("CARDINAL_API_KEY"));

using var response = await client.SendAsync(request);
Console.WriteLine($"{(int)response.StatusCode} {await response.Content.ReadAsStringAsync()}");
```

### Response 200

Usage by month.

| Name | Type | Required | Description |
|---|---|---|---|
| `from` | string | yes |  |
| `to` | string | yes |  |
| `customer_ref` | string | no | Echoed when the request named one. |
| `months` | array of object | yes |  |
| `months[].month` | string | yes | `YYYY-MM` (UTC). |
| `months[].streams` | integer | yes | Broadcasts that ended (and were billed) in the month. |
| `months[].stream_minutes` | object | yes | Billed stream minutes by tier; both tiers are always present. |
| `months[].stream_minutes.standard_1080p` | integer | yes |  |
| `months[].stream_minutes.pro_4k` | integer | yes |  |
| `months[].lines` | array of object | yes |  |
| `months[].lines[].product` | string | yes |  One of `"stream"`, `"restream"`, `"captions"`, `"translation"`. |
| `months[].lines[].tier` | string | yes |  One of `"standard_1080p"`, `"pro_4k"`. |
| `months[].lines[].minutes` | integer | yes | Billed minutes. |
| `months[].lines[].cents` | integer | yes | The amount in whole cents, exactly as invoiced. |
| `months[].usage_cents` | integer | yes |  |

### Errors

| Status | Code | Meaning |
|---|---|---|
| 400 | `invalid_month` | `from` or `to` is not `YYYY-MM`. |
| 400 | `invalid_range` | `from` is after `to`, or the range is longer than 36 months. |
| 400 | `invalid_customer_ref` | `customer_ref` is not a valid customer reference. |

## Read a month's usage by customer

`GET /v1/usage/customers`

One month's usage per customer, highest spend first, with the name you
gave each. Only customers that streamed that month appear.

### Query parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `month` | string | no | `YYYY-MM`. Defaults to the current month (UTC). |

### Examples

cURL:

```bash
curl "https://api.cardinalstreaming.com/v1/usage/customers?month=2026-09" \
  -H "Authorization: Bearer $CARDINAL_API_KEY"
```

Python:

```python
import os

import requests

response = requests.get(
    "https://api.cardinalstreaming.com/v1/usage/customers",
    headers={
        "Authorization": f"Bearer {os.environ['CARDINAL_API_KEY']}",
    },
    params={
        "month": "2026-09",
    },
    timeout=30,
)
print(response.status_code, response.text)
```

JavaScript:

```js
const response = await fetch('https://api.cardinalstreaming.com/v1/usage/customers?month=2026-09', {
  method: 'GET',
  headers: {
    Authorization: `Bearer ${process.env.CARDINAL_API_KEY}`,
  },
});
const data = await response.json();
console.log(response.status, data);
```

TypeScript:

```ts
const response: Response = await fetch('https://api.cardinalstreaming.com/v1/usage/customers?month=2026-09', {
  method: 'GET',
  headers: {
    Authorization: `Bearer ${process.env.CARDINAL_API_KEY}`,
  },
});
const data: unknown = await response.json();
console.log(response.status, data);
```

PHP:

```php
<?php
require 'vendor/autoload.php'; // composer require guzzlehttp/guzzle

$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.cardinalstreaming.com/v1/usage/customers', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('CARDINAL_API_KEY'),
    ],
    'query' => [
        'month' => '2026-09',
    ],
    'http_errors' => false,
]);
echo $response->getStatusCode(), ' ', $response->getBody(), PHP_EOL;
```

Ruby:

```ruby
require 'json'
require 'net/http'

uri = URI("https://api.cardinalstreaming.com/v1/usage/customers?month=2026-09")
request = Net::HTTP::Get.new(uri)
request['Authorization'] = "Bearer #{ENV.fetch('CARDINAL_API_KEY')}"

response = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(request) }
puts response.code, response.body
```

Go:

```go
package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
)

func main() {
	req, err := http.NewRequest("GET", "https://api.cardinalstreaming.com/v1/usage/customers?month=2026-09", nil)
	if err != nil {
		panic(err)
	}
	req.Header.Set("Authorization", "Bearer "+os.Getenv("CARDINAL_API_KEY"))

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()
	data, _ := io.ReadAll(resp.Body)
	fmt.Println(resp.StatusCode, string(data))
}
```

Java:

```java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Example {
    public static void main(String[] args) throws Exception {
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://api.cardinalstreaming.com/v1/usage/customers?month=2026-09"))
            .header("Authorization", "Bearer " + System.getenv("CARDINAL_API_KEY"))
            .method("GET", HttpRequest.BodyPublishers.noBody())
            .build();
        HttpResponse<String> response = HttpClient.newHttpClient()
            .send(request, HttpResponse.BodyHandlers.ofString());
        System.out.println(response.statusCode() + " " + response.body());
    }
}
```

C#:

```csharp
using System.Net.Http.Headers;
using System.Text;

using var client = new HttpClient();
using var request = new HttpRequestMessage(new HttpMethod("GET"), "https://api.cardinalstreaming.com/v1/usage/customers?month=2026-09");
request.Headers.Authorization = new AuthenticationHeaderValue(
    "Bearer", Environment.GetEnvironmentVariable("CARDINAL_API_KEY"));

using var response = await client.SendAsync(request);
Console.WriteLine($"{(int)response.StatusCode} {await response.Content.ReadAsStringAsync()}");
```

### Response 200

Usage by customer.

| Name | Type | Required | Description |
|---|---|---|---|
| `month` | string | yes |  |
| `customers` | array of object | yes |  |
| `customers[].customer_ref` | string | yes |  |
| `customers[].name` | string or null | yes |  |
| `customers[].streams` | integer | yes |  |
| `customers[].stream_minutes` | object | yes | Billed stream minutes by tier; both tiers are always present. |
| `customers[].stream_minutes.standard_1080p` | integer | yes |  |
| `customers[].stream_minutes.pro_4k` | integer | yes |  |
| `customers[].lines` | array of object | yes |  |
| `customers[].lines[].product` | string | yes |  One of `"stream"`, `"restream"`, `"captions"`, `"translation"`. |
| `customers[].lines[].tier` | string | yes |  One of `"standard_1080p"`, `"pro_4k"`. |
| `customers[].lines[].minutes` | integer | yes | Billed minutes. |
| `customers[].lines[].cents` | integer | yes | The amount in whole cents, exactly as invoiced. |
| `customers[].usage_cents` | integer | yes |  |

### Errors

| Status | Code | Meaning |
|---|---|---|
| 400 | `invalid_month` | `month` is not `YYYY-MM`. |
