Cost and Usage API V2
The Cost and Usage API V2 lets you programmatically query Finout cost and usage data using the Query Language API V2, designed for large-scale data extraction.
Prerequisites
Before using the Cost and Usage API V2:
You have generated a Finout API token and are including the following headers in every request:
x-finout-client-idx-finout-secret-key
You are familiar with Query Language V2 fields (
costCenter,key,type) — use the Query Language API V2 to look these up before building your query.
How it works
Retrieving cost and usage data follows a three-step asynchronous flow:
Submit a query — Call POST /v2/data/cost-usage/generate-query to create a query and receive a
queryId.Poll for completion — Call GET /v2/data/cost-usage/{queryId}/status repeatedly until
statusiscompleted. Poll every 5 seconds.Retrieve results — Call GET /v2/data/cost-usage/{queryId}/results?pageSize={pageSize}&cursor={cursor} to fetch paginated results when query status is completed. Results are stored for 24 hours.
The Generate Query endpoint is asynchronous. Do not call the Results endpoint immediately after submitting a query — always check the Status endpoint first and wait for completed.
Rate Limits
POST /v2/data/cost-usage/generate-query
50 req / min and up to 10K req / day
GET /v2/data/cost-usage/{queryId}/status
50 req / min
GET /v2/data/cost-usage/{queryId}/results?pageSize={pageSize}&cursor={cursor}
50 req / min
POST /Generate Query
Submits a cost or usage query and returns a queryId for use with the Status and Results endpoints.
Request
If no sort order is specified, results default to descending order by aggregated measurement sum.
Example request body
Request fields
date
object
Yes
Query date range.
date.unixTimeMillisecondsStart
number
Yes
Start of the date range as a Unix timestamp in milliseconds.
date.unixTimeMillisecondsEnd
number
Yes
End of the date range as a Unix timestamp in milliseconds. Maximum range: 60 days.
timeInterval
object
Yes
Time interval for grouping results.
timeInterval.interval
string
Yes
Grouping interval. Accepted values: day (default), week, month.
timeInterval.sortDirection
string
No
Sort direction for the interval. Accepted values: ascending, descending. Default: descending.
measurements
array<object>
Yes
Up to 5 measurement objects per request.
measurements[].type
string
Yes
Measurement type. Can be one of:
blendedCost
unblendedCost
netUnblendedCost
amortizedCost
netAmortizedCost
listCost
usageAmount
normalizedUsageAmount
For definitions of each cost type, see Cost and Usage Types.
measurements[].operator
string
No
Aggregation operator. Accepted values: sum, avg, min, max.
measurements[].sortDirection
string
No
Sort direction for this measurement. Accepted values: ascending, descending. Default: descending.
dimensions
array<object>
No
Up to 20 dimensions. A maximum of 3 unique Virtual Tags are allowed across dimensions and filters combined.
dimensions[].key
string
Yes
Key identifier for the dimension (from Query Language API V2 /keys).
dimensions[].type
string
Yes
Key type for the dimension (from Query Language API V2 /keys).
dimensions[].sortDirection
string
No
Sort direction for this dimension. Accepted values: ascending, descending. Default: descending.
filters
object
No
Filter conditions. Up to 30 filters. A maximum of 3 unique Virtual Tags across filters and dimensions combined. See Filter Object Definition V2.
rowLimit
number
No
Maximum rows to return. Max: 100,000. Default: 1,000.
Response
Response fields
requestId
string
Unique identifier for the API request. Use this for debugging and support.
data
object
Container for the query response.
data.queryId
string
ID of the submitted query. Use this with the Status and Results endpoints.
Limit errors
DIMENSIONS_LIMIT_EXCEEDED
Too many dimensions. Maximum allowed: 20.
FILTERS_LIMIT_EXCEEDED
Too many filters. Maximum allowed: 30.
VTAGS_LIMIT_EXCEEDED
Too many Virtual Tags. Maximum allowed: 3 across dimensions and filters.
TIMEFRAME_LIMIT_EXCEEDED
Date range too large. Maximum allowed: 60 days.
ROW_LIMIT_EXCEEDED
Row limit too high. Maximum allowed: 100,000. Default: 1,000.
RATE_LIMIT_DAILY_EXCEEDED
Daily submission limit exceeded. Limit: 10,000 per day per account.
CONCURRENT_REQUESTS_LIMIT_EXCEEDED
Too many concurrent queries. Maximum allowed: 50 running at a time.
MULTIPLE_SORT_FIELDS_NOT_ALLOWED
Only one sort field can be defined per request.
GET /Status
Checks whether a query has finished processing. Call this endpoint after submitting a query and continue polling until status is completed before attempting to retrieve results.
Request
Path parameters
queryId
string
The ID returned from POST /generate-query.
Example response
Response fields
requestId
string
Unique identifier for the request.
data.completedAt
string
Timestamp when the query finished. Only present when status is completed.
data.expiredAt
string
Timestamp when results expire. Only present when status is completed.
data.totalRows
number
Total number of rows available. Only returned when status is completed.
Status values
queued
Query is waiting to be processed.
running
Query is currently executing.
completed
Query finished — results are ready.
failed
Query execution failed.
expired
Results are no longer available.
GET /Results
Retrieves paginated results for a completed query. Only call this endpoint after GET /status returns completed.
Request
Query parameters
pageSize
number
No
Number of rows per page. Max: 10,000. Default: 100.
cursor
string
No
Pagination cursor. Use the nextCursor value from the previous response.
queryId
string
Yes
The ID returned from POST /generate-query.
Example response (no data)
Example response (with data)
Response fields
requestId
string
Yes
Unique identifier for the request.
pagination
object
Yes
Pagination metadata.
pagination.hasMore
boolean
Yes
Whether additional pages of results exist.
pagination.totalRows
number
Yes
Total number of rows across all pages.
pagination.nextCursor
string
No
Cursor token for fetching the next page. Only present when hasMore is true.
data
array<object>
Yes
Result rows. Each row is a flat object with dynamic fields based on the query.
data[].<dimensionField>
string
No
Dimension value. Field name format: <costCenter>_<type>_<key>.
data[].<intervalField>
string
No
Time bucket. Field name matches the requested timeInterval value: day, week, or month.
data[].<measurementField>
object
No
Measurement result. Field name format: <measurementType>_<operator>.
data[].<measurementField>.amount
number
Yes (when measurement exists)
Aggregated measurement value.
data[].<measurementField>.unit
string
No
Measurement unit. Returned for usage-based measurements only.
Error responses
QUERY_RUNNING
Query is still running.
102
QUERY_QUEUED
Query has not started yet.
102
QUERY_FAILED
Query execution failed.
424
CURSOR_INVALID
Pagination cursor is invalid.
400
PAGE_SIZE_LIMIT_EXCEEDED
Page size exceeds the maximum.
400
RESULTS_EXPIRED
Query results have expired.
410
FAQs
How is V2 different from V1? V2 is async — you submit a job and poll for results, which supports larger datasets than V1's synchronous model. V2 also lets you query cost or usage data, whereas V1 returns cost data only.
Do I need to create a View in MegaBill to use V2? No. V2 accepts filter and group-by parameters directly in the request body. Use Query Language API V2 to discover valid keys and values.
How do I know when my query is ready?
Use the Status endpoint. Only call the Results endpoint after status is completed.
How long are results available? Results expire 24 hours after query completion. After that, you must submit a new query.
Can I reuse a completed query? No. Each query execution is independent. To retrieve updated data, submit a new query.
What happens if I call the Results endpoint before the query is complete?
The API returns a QUERY_RUNNING or QUERY_QUEUED error. Always check the Status endpoint first.
How do I retrieve large result sets?
Use the nextCursor field returned in the response to paginate through results. Pass the cursor value in your next request until hasMore is false.
What is the maximum date range I can query? The maximum range is 60 days per query.
What happens if my query exceeds the row limit?
Results are paginated. Use the nextCursor to retrieve remaining rows.
Can I filter by any dimension? You can filter using any key returned by the Query Language API V2. If you use an unsupported key or operator combination, the API returns an error.
What operators are supported in filters? See Filter Object Definition V2 for the full list of supported operators.
How does sorting work?
Sorting is applied based on the aggregated measurement value, not by time. The default sort order is descending. Only one sort field is allowed per request.
What happens if I exceed the rate limits?
The API returns a 429 Too Many Requests response. Use the following response headers to monitor your rate limit consumption:
ratelimitx-account-ratelimit-*x-endpoint-ratelimit-*
Last updated
Was this helpful?