Usage Aggregator
curl --request GET \
--url https://app.opencomputer.dev/api/usage \
--header 'X-API-Key: <api-key>'import requests
url = "https://app.opencomputer.dev/api/usage"
headers = {"X-API-Key": "<api-key>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {'X-API-Key': '<api-key>'}};
fetch('https://app.opencomputer.dev/api/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://app.opencomputer.dev/api/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 => [
"X-API-Key: <api-key>"
],
]);
$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://app.opencomputer.dev/api/usage"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("X-API-Key", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://app.opencomputer.dev/api/usage")
.header("X-API-Key", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.opencomputer.dev/api/usage")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["X-API-Key"] = '<api-key>'
response = http.request(request)
puts response.read_body{
"from": "2026-03-23T00:00:00Z",
"to": "2026-04-22T00:00:00Z",
"groupBy": "sandbox",
"total": { "memoryGbSeconds": 20000, "diskOverageGbSeconds": 360 },
"items": [
{
"sandboxId": "sb-abc",
"alias": "my-agent",
"status": "running",
"tags": { "env": "prod", "team": "payments" },
"tagsLastUpdatedAt": "2026-04-19T14:02:00Z",
"memoryGbSeconds": 8000,
"diskOverageGbSeconds": 120
}
],
"nextCursor": null
}
{
"from": "2026-03-23T00:00:00Z",
"to": "2026-04-22T00:00:00Z",
"groupBy": "tag:team",
"total": { "memoryGbSeconds": 19000, "diskOverageGbSeconds": 340 },
"untagged": { "memoryGbSeconds": 1000, "diskOverageGbSeconds": 20, "sandboxCount": 2 },
"items": [
{ "tagKey": "team", "tagValue": "payments",
"memoryGbSeconds": 8000, "diskOverageGbSeconds": 120, "sandboxCount": 12 },
{ "tagKey": "team", "tagValue": "growth",
"memoryGbSeconds": 4000, "diskOverageGbSeconds": 50, "sandboxCount": 5 }
],
"nextCursor": null
}
Usage
Usage Aggregator
GET
/
api
/
usage
Usage Aggregator
curl --request GET \
--url https://app.opencomputer.dev/api/usage \
--header 'X-API-Key: <api-key>'import requests
url = "https://app.opencomputer.dev/api/usage"
headers = {"X-API-Key": "<api-key>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {'X-API-Key': '<api-key>'}};
fetch('https://app.opencomputer.dev/api/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://app.opencomputer.dev/api/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 => [
"X-API-Key: <api-key>"
],
]);
$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://app.opencomputer.dev/api/usage"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("X-API-Key", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://app.opencomputer.dev/api/usage")
.header("X-API-Key", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.opencomputer.dev/api/usage")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["X-API-Key"] = '<api-key>'
response = http.request(request)
puts response.read_body{
"from": "2026-03-23T00:00:00Z",
"to": "2026-04-22T00:00:00Z",
"groupBy": "sandbox",
"total": { "memoryGbSeconds": 20000, "diskOverageGbSeconds": 360 },
"items": [
{
"sandboxId": "sb-abc",
"alias": "my-agent",
"status": "running",
"tags": { "env": "prod", "team": "payments" },
"tagsLastUpdatedAt": "2026-04-19T14:02:00Z",
"memoryGbSeconds": 8000,
"diskOverageGbSeconds": 120
}
],
"nextCursor": null
}
{
"from": "2026-03-23T00:00:00Z",
"to": "2026-04-22T00:00:00Z",
"groupBy": "tag:team",
"total": { "memoryGbSeconds": 19000, "diskOverageGbSeconds": 340 },
"untagged": { "memoryGbSeconds": 1000, "diskOverageGbSeconds": 20, "sandboxCount": 2 },
"items": [
{ "tagKey": "team", "tagValue": "payments",
"memoryGbSeconds": 8000, "diskOverageGbSeconds": 120, "sandboxCount": 12 },
{ "tagKey": "team", "tagValue": "growth",
"memoryGbSeconds": 4000, "diskOverageGbSeconds": 50, "sandboxCount": 5 }
],
"nextCursor": null
}
Aggregate usage grouped by sandbox or by tag. Dollars are not exposed
— the platform returns the same physical quantities the invoice is
computed from. For per-sandbox time-series drilldown (memory
utilization over time, 1-minute resolution), see
GET /api/sandboxes/:id/usage.
Preview: Usage and tag APIs are new. Endpoints, response fields,
and SDK method names may change before GA; temporary inaccuracies or
rough edges are possible while the surface settles.
All
*GbSeconds fields are GiB-seconds (binary, 230
bytes/GiB), not decimal GB. For a sandbox provisioned at 1024 MiB
running for one second, memoryGbSeconds is 1.Freshness: accurate to the minute under normal conditions. A sandbox
that exited uncleanly continues to accrue against its provisioned
tier until the platform reconciles state — the same behavior the
billing pipeline uses, so the numbers here match the invoice.
Request
string
required
sandbox for top sandboxes, or tag:<key> for usage by tag value.
Keys may contain : — everything after the first : is the tag
key (so groupBy=tag:team:payments groups by the key
team:payments).string
Inclusive lower bound. Accepts either an ISO date (
2026-05-27,
interpreted as UTC midnight) or an RFC3339 timestamp. Default: now
minus 30 days. Max window: 90 days.string
Exclusive upper bound. Same accepted formats as
from. Default:
now.string
One param per dimension. Comma-separated values are OR’d within
that dimension; different
filter[...] params are AND’d across
dimensions. Passing the same filter[...] key twice returns 400 —
put multiple OR values in one comma-separated string instead. Empty
value (filter[tag:team]=) matches sandboxes without the team
key.string
-memoryGbSeconds (default) or -diskOverageGbSeconds.integer
Max items per page. Default 50, max 500.
string
Opaque cursor from a prior response’s
nextCursor.Response
string
required
RFC3339 echo of the effective window lower bound.
string
required
RFC3339 echo of the effective window upper bound.
string
required
Echo of the requested
groupBy value.object
required
Aggregate across all rows matching the window and filters, not
just the current page. Use this for headline numbers; sum
items[]
if you need a per-page subtotal instead.object[]
required
Paginated rows, sorted by
When
sort (default: largest first).When groupBy=sandbox, each row is one sandbox:Show groupBy=sandbox row
Show groupBy=sandbox row
string
required
The sandbox identifier.
string
Display name set at create time. Absent when none was provided.
string
Current sandbox status (e.g.,
running, hibernated,
stopped). Absent when no session exists.object
required
Current tag set on the sandbox. Empty object when none.
string
RFC3339 timestamp of the most recent tag change, or
null if
no tags exist.number
required
Provisioned memory × time for this sandbox in the window.
number
required
Disk overage × time for this sandbox.
groupBy=tag:<key>, each row is one tag value:Show groupBy=tag row
Show groupBy=tag row
string
required
The tag key from the request (e.g.,
team).string
required
The tag value for this group.
number
required
Total provisioned memory × time across sandboxes carrying
tagKey=tagValue.number
required
Total disk overage × time for the same group.
integer
required
Distinct sandboxes contributing to this row.
object
Only present when
groupBy=tag:<key>. Sibling bucket for sandboxes
that lack the grouping key — without it, untagged sandboxes would
silently disappear from the rollup. Same shape as a tag row minus
tagKey/tagValue.string
Opaque cursor to pass as
cursor= on the next request, or null
when no further pages exist.{
"from": "2026-03-23T00:00:00Z",
"to": "2026-04-22T00:00:00Z",
"groupBy": "sandbox",
"total": { "memoryGbSeconds": 20000, "diskOverageGbSeconds": 360 },
"items": [
{
"sandboxId": "sb-abc",
"alias": "my-agent",
"status": "running",
"tags": { "env": "prod", "team": "payments" },
"tagsLastUpdatedAt": "2026-04-19T14:02:00Z",
"memoryGbSeconds": 8000,
"diskOverageGbSeconds": 120
}
],
"nextCursor": null
}
{
"from": "2026-03-23T00:00:00Z",
"to": "2026-04-22T00:00:00Z",
"groupBy": "tag:team",
"total": { "memoryGbSeconds": 19000, "diskOverageGbSeconds": 340 },
"untagged": { "memoryGbSeconds": 1000, "diskOverageGbSeconds": 20, "sandboxCount": 2 },
"items": [
{ "tagKey": "team", "tagValue": "payments",
"memoryGbSeconds": 8000, "diskOverageGbSeconds": 120, "sandboxCount": 12 },
{ "tagKey": "team", "tagValue": "growth",
"memoryGbSeconds": 4000, "diskOverageGbSeconds": 50, "sandboxCount": 5 }
],
"nextCursor": null
}