---
title: "Several measures in one round trip"
description: "A twelve-widget board is ONE request."
canonical_url: "https://twominutebilling.com/help/developers/api-reference/analytics/several-measures-in-one-round-trip"
---
# Several measures in one round trip

> A twelve-widget board is ONE request.

<api-operation method="POST" path="/v1/analytics/query">
<template v-slot:docs="">

Each entry answers exactly as `GET /v1/analytics` would, in the same order, tagged with the `key` you gave it.

**A refused question fails alone.** It comes back in its own slot as `object: analytics_error` with `status: 422` and the same explanatory sentence, and every other question still answers — one bad tile must not blank a board. The HTTP status is 200 whenever the batch itself was well formed.

Top-level `grain`, `range`, `start`, `end`, `currency`, `compare` and `business` are defaults; anything set on an individual query wins. Reads, so no `Idempotency-Key`.

## Headers

<table>
<thead>
  <tr>
    <th>
      Parameter
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Required
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        Idempotency-Key
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      Safely retry writes — repeats return the original response. If a write FAILED after it had already taken effect, that failure is what repeats: the key is spent, and a new attempt needs a new key.
    </td>
  </tr>
</tbody>
</table>

## Body

The request body is **required**.

<table>
<thead>
  <tr>
    <th>
      Field
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Required
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        queries
      </code>
    </td>
    
    <td>
      <code>
        array of object
      </code>
    </td>
    
    <td>
      <strong>
        yes
      </strong>
    </td>
    
    <td>
      The questions, answered in this order. At most 24 — a board, not a crawl.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        queries.key
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      Your label for this question, echoed on its answer. Defaults to its index (<code>
        q0
      </code>
      
      , <code>
        q1
      </code>
      
      , …).
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        queries.measure
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <strong>
        yes
      </strong>
    </td>
    
    <td>
      The measure id, as listed by <code>
        GET /v1/analytics/measures
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        queries.grain
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      Overrides the batch's <code>
        grain
      </code>
      
       for this question. One of <code>
        day
      </code>
      
      , <code>
        week
      </code>
      
      , <code>
        month
      </code>
      
      , <code>
        quarter
      </code>
      
      , <code>
        year
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        queries.range
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      A named window, overriding the batch's <code>
        range
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        queries.start
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      Explicit window start, overriding the batch's.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        queries.end
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      Explicit window end, overriding the batch's.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        queries.split
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      A dimension to cut this question by.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        queries.split_limit
      </code>
    </td>
    
    <td>
      <code>
        integer
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      How many split rows to keep before folding the rest into one <code>
        kind: truncated
      </code>
      
       row. Defaults to 20.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        queries.currency
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      ISO 4217 code to state this question's figures in.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        queries.compare
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      Add the previous equal-length window. One of <code>
        previous
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        queries.shape
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      <code>
        totals
      </code>
      
       leaves the periods out of this answer; see <code>
        shape
      </code>
      
       on GET /v1/analytics. One of <code>
        series
      </code>
      
      , <code>
        totals
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        queries.segment
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      A segment's id; see <code>
        segment
      </code>
      
       on GET /v1/analytics.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        queries.business
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      Answer this question for one business (<code>
        biz_…
      </code>
      
      ).
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        grain
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      Default grain for every question that does not set its own. One of <code>
        day
      </code>
      
      , <code>
        week
      </code>
      
      , <code>
        month
      </code>
      
      , <code>
        quarter
      </code>
      
      , <code>
        year
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        range
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      Default named window for every question that does not set its own.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        start
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      Default explicit window start.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        end
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      Default explicit window end.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        currency
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      Default currency for every question that does not set its own.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        compare
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      Default comparison for every question. One of <code>
        previous
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        business
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      Default business filter (<code>
        biz_…
      </code>
      
      ). Omit for organization-wide answers.
    </td>
  </tr>
</tbody>
</table>

## Response

<table>
<thead>
  <tr>
    <th>
      Field
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        object
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      Always <code>
        analytics_batch
      </code>
      
      . One of <code>
        analytics_batch
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        as_of
      </code>
    </td>
    
    <td>
      <code>
        string · ISO-8601
      </code>
    </td>
    
    <td>
      One instant for the whole batch, so the tiles on a board agree about "now".
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        data
      </code>
    </td>
    
    <td>
      <code>
        array of object
      </code>
    </td>
    
    <td>
      One entry per question, in order: an <code>
        analytics_result
      </code>
      
       with a <code>
        key
      </code>
      
      , or an <code>
        analytics_error
      </code>
      
       carrying <code>
        status: 422
      </code>
      
       and the refusal's sentence.
    </td>
  </tr>
</tbody>
</table>

## Status codes

<table>
<thead>
  <tr>
    <th>
      Status
    </th>
    
    <th>
      Meaning
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        200
      </code>
    </td>
    
    <td>
      One answer or one refusal per question, in order
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        400
      </code>
    </td>
    
    <td>
      Malformed batch (no questions, or more than 24)
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        409
      </code>
    </td>
    
    <td>
      Idempotency-Key conflict — the key is already in flight, or was reused with a different request payload.
    </td>
  </tr>
</tbody>
</table>
</template>

<template v-slot:request="">

```bash [Request]
curl -X POST "$TMBILLING_URL/v1/analytics/query" \
  -H "Authorization: Bearer $TMBILLING_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "queries": [
      {
        "measure": "mrr",
        "currency": "USD"
      }
    ],
    "currency": "USD"
  }'
```

</template>

<template v-slot:response="">
<code-group>

```json [200 OK]
{
  "object": "analytics_batch",
  "as_of": "2026-03-14T09:30:00Z",
  "data": [
    {
      "object": "analytics_result",
      "measure": "mrr",
      "name": "MRR",
      "description": "Everything a growing team needs.",
      "kind": "stock",
      "unit": "money"
    }
  ]
}
```

```json [400 Bad Request]
{
  "error": {
    "type": "invalid_request_error",
    "message": "Malformed batch (no questions, or more than 24)"
  }
}
```

```json [409 Conflict]
{
  "error": {
    "type": "idempotency_error",
    "message": "Idempotency-Key conflict — the key is already in flight, or was reused with a different request payload."
  }
}
```

</code-group>
</template>
</api-operation>


## Sitemap

See the full [sitemap](https://twominutebilling.com/sitemap.md) for all pages.
