# 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](https://docs.finout.io/api/finout-api/finout-api-v2/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](https://docs.finout.io/api/finout-api/generate-an-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](https://docs.finout.io/api/finout-api/finout-api-v2/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.

{% hint style="info" %}
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`.
{% endhint %}

***

### 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**

```
POST https://app.finout.io/v2/data/cost-usage/generate-query
```

{% hint style="info" %}
If no sort order is specified, results default to descending order by aggregated measurement sum.
{% endhint %}

**Example request body**

```json
{
  "date": {
    "unixTimeMillisecondsStart": 1767225600000,
    "unixTimeMillisecondsEnd": 1769817600000
  },
  "timeInterval": {
    "interval": "day"
  },
  "measurements": [
    {
      "type": "usageAmount",
      "sortDirection": "descending"
    }
  ],
  "dimensions": [
    {
      "costCenter": "AWS",
      "key": "extracted_resource",
      "type": "tag"
    }
  ],
  "filters": {
    "AND": [
      {
        "costCenter": "AWS",
        "key": "region",
        "displayName": "Regions",
        "operator": "oneOf",
        "type": "billing_dimension",
        "value": ["us-east-1", "us-east-2", "eu-central-1"]
      },
      {
        "costCenter": "Global",
        "key": "cost_center_type",
        "displayName": "Cost Center",
        "operator": "oneOf",
        "type": "billing_dimension",
        "value": ["AWS"]
      }
    ]
  },
  "rowLimit": 10000
}
```

**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      | <p>Measurement type. Can be one of:</p><ul><li>blendedCost</li><li>unblendedCost </li><li>netUnblendedCost </li><li>amortizedCost </li><li>netAmortizedCost </li><li>listCost</li><li>usageAmount </li><li>normalizedUsageAmount</li></ul><p><br>For definitions of each cost type, see <a href="https://docs.finout.io/get-started-with-finout/cost-and-usage-types">Cost and Usage Types</a>.</p> |
| `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](https://docs.finout.io/api/finout-api/finout-api-v2/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](https://docs.finout.io/api/finout-api/filter-object-definition-v2).                                                                                                                                                                               |
| `rowLimit`                       | number         | No       | Maximum rows to return. Max: `100,000`. Default: `1,000`.                                                                                                                                                                                                                                                                                                                                           |

**Response**

```json
{
  "requestId": "5deb7942-5fe4-48eb-8d3f-a6fab6b7eb75",
  "data": {
    "queryId": "q_8b6f4a4961964251bd42b292dfea3c1a"
  }
}
```

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

```
GET https://app.finout.io/v2/data/cost-usage/{queryId}/status
```

**Path parameters**

| Name      | Type   | Description                                  |
| --------- | ------ | -------------------------------------------- |
| `queryId` | string | The ID returned from `POST /generate-query`. |

**Example response**

```json
{
  "requestId": "1dd3b88f-7b13-4b04-87ba-73fd6402924b",
  "data": {
    "status": "COMPLETED",
    "completedAt": "Mar 30, 2026 11:16 am",
    "expiredAt": "Mar 31, 2026 11:16 am",
    "totalRows": 228977
  }
}
```

**Response fields**

| Field              | Type   | Description                                                                                                                 |
| ------------------ | ------ | --------------------------------------------------------------------------------------------------------------------------- |
| `requestId`        | string | Unique identifier for the request.                                                                                          |
| `data.status`      | string | Current query status. See [Status values](https://claude.ai/chat/b94a8660-851b-444b-84f9-29bc379d4e6f#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**

```
GET https://app.finout.io/v2/data/cost-usage/{queryId}/results?pageSize={pageSize}&cursor={cursor}
```

**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)**

```json
{
  "requestId": "a0ced922-98e9-4050-b627-7a26f6805cb2",
  "pagination": {
    "hasMore": false,
    "totalRows": 0
  },
  "data": []
}
```

**Example response (with data)**

```json
{
  "requestId": "30116158-33cf-4327-96e8-93926c733bb7",
  "pagination": {
    "hasMore": true,
    "totalRows": 1878,
    "nextCursor": "100"
  },
  "data": [
    {
      "AWS_billing_dimension_region": "us-east-1",
      "GCP_billing_dimension_region": "us-central1",
      "day": "2025-03-12",
      "usageAmount_sum": {
        "amount": 9215827456,
        "unit": "Metric Datapoints"
      }
    }
  ]
}
```

**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](https://docs.finout.io/api/finout-api/finout-api-v2/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](https://docs.finout.io/api/finout-api/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-*`


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.finout.io/api/finout-api/finout-api-v2/cost-and-usage-api-v2.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
