Usage History
Track storage consumption over time, per project or per bucket. Snapshots capture per-bucket bytes used, file counts, and quotas daily — building a historical record for usage charts and billing audit.
Why this exists
The live /storage/usage endpoint tells you what your storage looks like right now. That's perfect for the dashboard's overview card or a one-shot health check, but it doesn't tell you whether you've grown 10% this week, doubled this month, or stayed flat.
Usage history fills that gap. A daily snapshot per bucket gives you trend lines instead of point-in-time numbers, and gives Koolbase an audit trail of usage that future billing can reconcile against.
What gets captured
One row per (project, bucket, day) in the storage_usage_snapshots table. Each row stores:
current_size_bytes— total bytes the bucket held that dayfile_count— number of objects in the bucket that daymax_size_bytes— the bucket's quota at snapshot time (nullable; some buckets are uncapped). Captured per-day because quotas change — historical usage_percent has to use the quota that was set on that day, not today's.captured_at— the timestamp the snapshot was written
Bucket names aren't captured — they're always resolved via JOIN to storage_buckets by ID. That way, if you rename a bucket, its historical chart stays correctly attributed.
When snapshots happen
A background goroutine on the API runs at 00:30 UTC daily, and also fires once on every API boot. The 30-minute offset past midnight gives upload-confirm and reaper flows a moment to settle before counters are read.
The capture is one atomic SQL statement that touches every bucket. The primary key on (bucket_id, snapshot_date) means re-runs the same day are idempotent — they update the existing row rather than failing. Restart the API four times in a day and you still get exactly one row per bucket for that day.
Snapshots aren't retroactive
API endpoint
GET /v1/projects/{project_id}/storage/usage/historyQuery parameters:
| Param | Type | Default |
|---|---|---|
| from | YYYY-MM-DD | 30 days ago |
| to | YYYY-MM-DD | today |
| bucket_id | UUID | all buckets (aggregated) |
Project-wide response
When bucket_id is omitted, each point in the series is a daily aggregate across every bucket in the project.
{
"from": "2026-05-07",
"to": "2026-06-05",
"series": [
{
"date": "2026-06-05",
"current_size_bytes": 1597222,
"file_count": 9,
"bucket_count": 3
}
]
}Per-bucket response
When bucket_id is set, the series is daily snapshots for that one bucket, with max_size_bytes and usage_percent populated when a quota was set.
{
"from": "2026-05-07",
"to": "2026-06-05",
"bucket_id": "60c9dfb8-4283-4c03-bfbf-86767394b45e",
"series": [
{
"date": "2026-06-05",
"current_size_bytes": 1597222,
"file_count": 9,
"max_size_bytes": 10485760,
"usage_percent": 15.2
}
]
}Curl examples
# Default window (last 30 days, project-wide)
curl -H "Authorization: Bearer $TOKEN" "https://api.koolbase.com/v1/projects/$PROJECT_ID/storage/usage/history"
# Explicit range
curl -H "Authorization: Bearer $TOKEN" "https://api.koolbase.com/v1/projects/$PROJECT_ID/storage/usage/history?from=2026-05-01&to=2026-06-05"
# Per-bucket
curl -H "Authorization: Bearer $TOKEN" "https://api.koolbase.com/v1/projects/$PROJECT_ID/storage/usage/history?bucket_id=$BUCKET_ID"Dashboard
On the Storage page, the Usage button (next to Trusted Origins and Presets in the stats row) opens a modal showing storage over time. Inside the modal you can:
- Toggle the time window between 30 days, 90 days, and 1 year
- Switch between project-wide totals and a single bucket via the dropdown
- Hover the chart for daily details (size, file count, and either bucket count or quota usage)
When there's only one snapshot (day one of a project), the modal shows a clean 3-card summary instead of a sparse chart, with a caption explaining the chart will fill in over coming days.
Retention
Snapshots are kept indefinitely. Each row is around 80 bytes, so a project with 10 buckets running for a year is roughly 300KB — well below any meaningful storage concern in Postgres.
If a project ever crosses a threshold where indefinite retention becomes an issue, we'll add a monthly rollup table and start aging out individual daily rows older than ~13 months. Not built yet because there's no reason to build it speculatively.
Troubleshooting
“No snapshots yet in this window”
The writer captures on API boot and at 00:30 UTC daily. If you just deployed and don't see today's row, the boot capture might have failed — check the API logs for storage usage snapshot captured. If it's missing, restart the API and watch the logs.
Gaps in the chart
Days without snapshots aren't reconstructed. The most common cause is the API being down across midnight UTC. If your uptime is solid, you should see a contiguous series.
Per-bucket query returns the same totals as project-wide
That just means the other buckets in your project are empty — the selected bucket holds all the data. Empty buckets still get snapshot rows (with current_size_bytes: 0), they just sum to nothing.