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-id

    • x-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:

  1. Submit a query — Call POST /v2/data/cost-usage/generate-query to create a query and receive a queryId.

  2. Poll for completion — Call GET /v2/data/cost-usage/{queryId}/status repeatedly until status is completed. Poll every 5 seconds.

  3. 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

Endpoint
Rate Limit (per account, per route)

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

Field
Type
Required
Description

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[].costCenter

string

Yes

Cost center for the dimension (from Query Language API V2 /keys).

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

Field
Type
Description

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

Code
Description

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

Name
Type
Description

queryId

string

The ID returned from POST /generate-query.

Example response

Response fields

Field
Type
Description

requestId

string

Unique identifier for the request.

data.status

string

Current query status. See Status values below.

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

Status
Meaning

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

Name
Type
Required
Description

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

Field
Type
Always returned
Description

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

Code
Description
HTTP Status

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:

  • ratelimit

  • x-account-ratelimit-*

  • x-endpoint-ratelimit-*

Last updated

Was this helpful?