Cost query API
Query reconciled cost data (by service, region, team, tag, or any combination) with grouping, filtering, and time-range pagination.
Base URL and auth
https://api.zop.dev/v1All requests require a personal access token in the Authorization: Bearer … header. The Cost query endpoints are read-only and accept any PAT that has been granted the cost:read scope (granted by default to every PAT).
Query reconciled cost
POST /cost/queryThe cost query endpoint returns reconciled spend across any combination of dimensions, filters, and time ranges. Results are already reconciled to your actual billing export (Cost Explorer, BigQuery, Cost Management); no rack-rate conversion, no estimates.
Request body
{ "timeRange": { "start": "2026-05-01", "end": "2026-05-31", "granularity": "day" }, "groupBy": ["service", "team"], "filters": { "providers": ["aws", "gcp"], "environments": ["prod", "stage"], "tags": { "include": [{ "key": "cost-center", "value": "platform" }], "exclude": [{ "key": "ignore-cost", "value": "true" }] } }, "currency": "USD", "page": 1, "pageSize": 1000}Example
curl -X POST "https://api.zop.dev/v1/cost/query" \ -H "Authorization: Bearer $ZOPNIGHT_PAT" \ -H "Content-Type: application/json" \ -d '{ "timeRange": { "start": "2026-05-01", "end": "2026-05-31", "granularity": "day" }, "groupBy": ["service", "team"], "filters": { "providers": ["aws", "gcp"] }, "currency": "USD" }'Supported groupBy dimensions
provider;aws,gcp,azure.account; cloud account ID.service; provider service (e.g.ec2,rds,cloud-storage,azure-functions).resourceType; sub-service type.region; provider region.environment;prod,stage,uat,dev.team; owning team slug.tag:<key>; group by a tag key, e.g.tag:cost-center.resource; resource-level breakdown (limited to 10k results per query).
You can group by up to 4 dimensions per query. Order matters for cardinality; put the lowest-cardinality dimension first.
Granularity
day; daily buckets. Max 92-day range per query.month; monthly buckets. Max 24-month range per query.hour; hourly buckets. Max 7-day range per query. Available only on the Enterprise tier.
Response
{ "data": [ { "service": "ec2", "team": "payments-platform", "buckets": [ { "date": "2026-05-01", "amount": 412.18, "currency": "USD" }, { "date": "2026-05-02", "amount": 408.94, "currency": "USD" } ], "total": { "amount": 12438.21, "currency": "USD" } } ], "pagination": { "page": 1, "pageSize": 1000, "total": 47 }, "reconciliation": { "billingExport": "aws-cur-2026-05", "lastReconciledAt": "2026-06-01T03:12:00Z" }}Anomaly detection
GET /cost/anomaliesReturns anomalies detected against the 7-day rolling average across five dimensions: org, cloud account, resource group, resource, and team. See Cost anomalies for the detection method and severity bands.
Query parameters
| Name | Type | Description |
|---|---|---|
since | date | ISO-8601 date. Default: 7 days ago. |
minDelta | number | Minimum dollar delta to include. Default: $100/day. |
dimension | enum | Filter to one dimension. |
Budgets
GET /cost/budgetsPOST /cost/budgetsPATCH /cost/budgets/:idDELETE /cost/budgets/:idBudgets are computed live from the Cost Graph; there is no nightly snapshot delay. A budget update takes effect on the next query.
Budget object
{ "id": "bud_01HK3...", "name": "Payments. May 2026", "scope": { "team": "payments-platform" }, "period": "month", "amount": 50000, "currency": "USD", "alerts": [ { "threshold": 0.7, "channels": ["slack:#payments-finops"] }, { "threshold": 0.9, "channels": ["slack:#payments-finops", "email:lead@example.com"] } ]}Export
For large pulls (resource-level, full month, every account), use the export flow:
POST /cost/exportGET /cost/export/:idThe first call enqueues an export job; the second polls its status. Completed exports are delivered as CSV, JSON, or Parquet to a signed URL; or directly to your S3 / GCS / Azure Blob bucket on the Enterprise tier.
Rate limits
- Read endpoints (
POST /cost/query,GET /cost/anomalies,GET /cost/budgets,GET /cost/export/:id): 600 requests/min per workspace. - Write and export endpoints (
POST /cost/export, budget create/update/delete): 60 requests/min per workspace.
Headers X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset are returned on every response.
Errors
Errors use standard HTTP status codes. The body carries a machine-readable code, a human-readable message, and the numeric status.
| Status | Meaning |
|---|---|
400 | Invalid or malformed parameters (bad granularity, range over the granularity limit, unknown groupBy dimension). |
401 | Missing or invalid token. |
403 | Token lacks the required scope (e.g. cost:read, or write scope for budget mutations). |
404 | Budget or export job not found, or not visible to the token’s role. |
429 | Rate limited; see Rate limits. |
5xx | Server error; safe to retry with backoff. |
{ "error": { "code": "invalid_request", "message": "granularity \"hour\" allows a maximum range of 7 days", "status": 400 }}