---
title: "One measure, over one window, optionally split by one dimension"
description: "The single analytics read."
canonical_url: "https://twominutebilling.com/help/developers/api-reference/analytics/one-measure"
---
# One measure, over one window, optionally split by one dimension

> The single analytics read.

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

Everything a chart needs is on the response and nothing has to be inferred:

- **Units are explicit.** `unit: money` is integer MINOR units in the stated `currency`; `unit: count` is a whole number; `unit: bps` is basis points. Never divide by 100 on a guess.
- **kind is echoed.** A `stock` is the value AT the end of each period; a `flow` is the sum ACROSS it. `total_basis` says which rule produced `total` — `value_at_end` or `sum_over_window`.
- **Freshness travels with the data**: `as_of` is when the answer was assembled, `computed_at` is when the OLDEST figure in it was computed.
- **A distinct count or cohort rate over a non-calendar window** (new customers, retention, customer churn over `range=30d` or a custom range) is computed for that exact window — `total_basis: window`, with `total_numerator` / `total_denominator` for a rate — never as a sum of its buckets. Rolling presets are precomputed hourly; a custom range is computed on demand for one business.
- **coverage appears when the history is partial.** A period it lists is unknown, not zero. Plotting one as zero is how a partial series becomes a flattering chart.
- **Money is never summed across currencies.** A measure held per currency with more than one currency in the window is refused until you name one; counts are combined, and `currency_basis` says which happened.

Anything coherent but unanswerable — a measure that cannot be cut by that dimension, a grain it does not support, a range with no periods — answers **422 with a sentence naming the fix**.

## Query parameters

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

<tbody>
  <tr>
    <td>
      <code>
        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>
      
       — e.g. <code>
        mrr
      </code>
      
      , <code>
        mrr_new
      </code>
      
      , <code>
        active_subscriptions
      </code>
      
      . An unknown id is a 422 naming the discovery endpoint.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        grain
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      How finely the window is cut into periods, independently of how far back it reaches. Weeks start on the business's <code>
        week_start
      </code>
      
       (default Sunday); quarters and years follow its <code>
        fiscal_year_start_month
      </code>
      
       and are labelled by fiscal year when it is not January ("Q1 FY2027", "FY2027" — named by the year the fiscal year ends in). The first and last period are clipped to the window, so a month in progress ends today rather than at its calendar end. At organization scope the businesses' shared calendar is used, or the default when they differ.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        range
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      A named window. With <code>
        business
      </code>
      
      , it is cut on that business's local dates. Without it, each business's figures stay on its own local dates and the window ends on the last day EVERY business has reached, so a total never mixes one business's today with another's yesterday; the returned <code>
        timezone
      </code>
      
       is then the organization's, a label. The resolved <code>
        start
      </code>
      
      , <code>
        end
      </code>
      
      , <code>
        timezone
      </code>
      
       and <code>
        timezone_source
      </code>
      
       come back on the response. <code>
        this_quarter
      </code>
      
       and <code>
        this_year
      </code>
      
       run from the start of the current FISCAL quarter/year. Mutually exclusive with <code>
        start
      </code>
      
      /<code>
        end
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        start
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      First day of an explicit window, <code>
        YYYY-MM-DD
      </code>
      
      , inclusive. Requires <code>
        end
      </code>
      
       and excludes <code>
        range
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        end
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      Last day of an explicit window, <code>
        YYYY-MM-DD
      </code>
      
      , inclusive. Requires <code>
        start
      </code>
      
       and excludes <code>
        range
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        split
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      Cut the measure by one dimension — e.g. <code>
        plan
      </code>
      
      , <code>
        plan_at_start
      </code>
      
      , <code>
        country
      </code>
      
      , <code>
        region
      </code>
      
       (UN M49 groups), <code>
        billing_interval
      </code>
      
      , <code>
        signup_cohort
      </code>
      
       (<code>
        YYYY-MM
      </code>
      
       of first payment), <code>
        currency
      </code>
      
       or <code>
        business
      </code>
      
      , subject to what that measure supports (see <code>
        dimensions
      </code>
      
       on the discovery payload). A missing value is its own <code>
        none
      </code>
      
       row, so slices sum to the total. A dimension the measure does not carry is refused by name rather than answered with an empty breakdown.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        split_limit
      </code>
    </td>
    
    <td>
      <code>
        integer
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      How many split rows to return. The rows come back biggest-first by magnitude with ties broken by key, and everything beyond the limit is folded into one explicit row with <code>
        kind: truncated
      </code>
      
       — so a split can never return unbounded rows. A separate row with <code>
        kind: unattributed
      </code>
      
       carries any part of the population the dimension does not describe, whether or not anything was truncated; read <code>
        exhaustive
      </code>
      
       for whether the rows add back up to <code>
        total
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        currency
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      ISO 4217 code to state the figures in. Nothing is converted here: this SELECTS the currency the figures were recorded in. Omit it when only one currency is present; a money measure with several is refused until you name one.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        compare
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      Add the equal-length window immediately before this one, as <code>
        compare
      </code>
      
       on the response: its dates, its total, the absolute change and the change in basis points. Refused on a measure whose <code>
        supports_compare
      </code>
      
       is false.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        segment
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      A saved customer segment's id (<code>
        seg_…
      </code>
      
      , as <code>
        GET /v1/businesses/{biz}/customer_segments
      </code>
      
       lists it): the measure for that population only, computed from the MRR movement ledger. Membership is the segment as it stands TODAY, applied to all of history. Needs <code>
        business
      </code>
      
      , cannot be combined with <code>
        split
      </code>
      
      , and is accepted only where discovery marks <code>
        segmentable
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        shape
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      <code>
        series
      </code>
      
       (default) sends every period; <code>
        totals
      </code>
      
       sends the same answer with the periods left out — <code>
        series
      </code>
      
       and each split row's <code>
        points
      </code>
      
       empty, every <code>
        total
      </code>
      
       unchanged. A split then reads as a ranked list, and a measure without one as a single figure.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        business
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      Answer for ONE business (<code>
        biz_…
      </code>
      
      ). A filter, not a different question — omit it and the same measure is answered across every business in the organization, which is also what <code>
        split=business
      </code>
      
       breaks down.
    </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_result
      </code>
      
      . One of <code>
        analytics_result
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        measure
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      The measure id that was answered.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        name
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      The measure's display name.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        description
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      One sentence saying what the number means — the same text discovery publishes.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        kind
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        stock
      </code>
      
       — a value AT the end of each period. <code>
        flow
      </code>
      
       — a sum ACROSS each period. Treating one as the other draws a chart that is wrong and looks fine. One of <code>
        stock
      </code>
      
      , <code>
        flow
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        unit
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        money
      </code>
      
       is integer MINOR units in <code>
        currency
      </code>
      
      ; <code>
        count
      </code>
      
       is a whole number; <code>
        bps
      </code>
      
       is basis points; <code>
        ratio
      </code>
      
       divides <code>
        value
      </code>
      
       by <code>
        denominator
      </code>
      
      . One of <code>
        money
      </code>
      
      , <code>
        count
      </code>
      
      , <code>
        ratio
      </code>
      
      , <code>
        bps
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        aggregate
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      How a period's figure was derived from the stored daily rows. One of <code>
        sum
      </code>
      
      , <code>
        last
      </code>
      
      , <code>
        ratio
      </code>
      
      , <code>
        none
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        currency
      </code>
    </td>
    
    <td>
      `string
    </td>
    
    <td>
      null`
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        currency_basis
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        per_currency
      </code>
      
       — one currency, nothing converted and nothing added across currencies. <code>
        across_currencies
      </code>
      
       — counts combined, which is only ever done for non-money units. One of <code>
        per_currency
      </code>
      
      , <code>
        across_currencies
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        scope
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      Whether the answer covers one business or every business in the organization. One of <code>
        business
      </code>
      
      , <code>
        organization
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        business
      </code>
    </td>
    
    <td>
      `string
    </td>
    
    <td>
      null`
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        grain
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      How the window was cut into periods. 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>
        object
      </code>
    </td>
    
    <td>
      The window as resolved, so a client never has to redo the arithmetic.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        as_of
      </code>
    </td>
    
    <td>
      <code>
        string · ISO-8601
      </code>
    </td>
    
    <td>
      When this answer was assembled.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        computed_at
      </code>
    </td>
    
    <td>
      <code>
        string · ISO-8601
      </code>
    </td>
    
    <td>
      When the OLDEST figure in the answer was computed — the age to show a reader, not <code>
        as_of
      </code>
      
      . Null when the window contains no stored figures at all.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        series
      </code>
    </td>
    
    <td>
      <code>
        array of object
      </code>
    </td>
    
    <td>
      One point per period, oldest first, with a point for every period asked for. Empty when <code>
        shape
      </code>
      
       is <code>
        totals
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        shape
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      The shape asked for, echoed so an empty <code>
        series
      </code>
      
       reads as requested, not as no data. One of <code>
        series
      </code>
      
      , <code>
        totals
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        segment
      </code>
    </td>
    
    <td>
      <code>
        object
      </code>
    </td>
    
    <td>
      Present when a segment was asked for: which one, and that membership is today's.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        total
      </code>
    </td>
    
    <td>
      `integer
    </td>
    
    <td>
      null`
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        total_basis
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      How <code>
        total
      </code>
      
       was derived: a flow is summed over the window, a stock is its value at the end. Summing a stock inflates it by roughly the number of periods. One of <code>
        sum_over_window
      </code>
      
      , <code>
        value_at_end
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        split
      </code>
    </td>
    
    <td>
      <code>
        object
      </code>
    </td>
    
    <td>
      The breakdown, present only when <code>
        split
      </code>
      
       was asked for. Bounded by construction, and explicit about the two different ways it can fall short of <code>
        total
      </code>
      
      : rows dropped by <code>
        split_limit
      </code>
      
      , and population the dimension does not describe at all. Read <code>
        kind
      </code>
      
       on each row rather than its <code>
        key
      </code>
      
       — a dimension whose own values include one called <code>
        other
      </code>
      
       makes the key alone ambiguous.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        compare
      </code>
    </td>
    
    <td>
      <code>
        object
      </code>
    </td>
    
    <td>
      The previous window, present only when <code>
        compare=previous
      </code>
      
       was asked for.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        coverage
      </code>
    </td>
    
    <td>
      <code>
        object
      </code>
    </td>
    
    <td>
      Present ONLY where the history is partial. Its absence means every period asked for has a computed figure. A listed period is unknown, not zero — do not plot it as zero and do not include it in an average.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        freshness
      </code>
    </td>
    
    <td>
      <code>
        object
      </code>
    </td>
    
    <td>
      How long this answer may be reused, matching the <code>
        cache-control
      </code>
      
       header.
    </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 measure, self-describing
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        400
      </code>
    </td>
    
    <td>
      <code>
        measure
      </code>
      
       was not given
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        422
      </code>
    </td>
    
    <td>
      A refusal with a sentence: unknown measure, unsupported dimension or grain, a range with no periods, or money in more than one currency
    </td>
  </tr>
</tbody>
</table>
</template>

<template v-slot:request="">

```bash [Request]
curl "$TMBILLING_URL/v1/analytics?measure=mrr" \
  -H "Authorization: Bearer $TMBILLING_API_KEY"
```

</template>

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

```json [200 OK]
{
  "object": "analytics_result",
  "measure": "mrr",
  "name": "MRR",
  "description": "Everything a growing team needs.",
  "kind": "stock",
  "unit": "money",
  "aggregate": "sum",
  "currency": "USD",
  "currency_basis": "per_currency",
  "scope": "business",
  "business": "biz_XXXXXXXXXXXX01",
  "grain": "day",
  "range": {
    "name": "MRR",
    "start": "2026-03-14",
    "end": "2026-04-14",
    "timezone": "America/Los_Angeles",
    "timezone_source": "business"
  },
  "as_of": "2026-03-14T09:30:00Z",
  "computed_at": "2026-03-14T09:30:00Z",
  "series": [
    {
      "t": "2026-03-14",
      "label": "Sep",
      "value": 1,
      "denominator": 1
    }
  ],
  "shape": "series",
  "segment": {
    "id": "seg_XXXXXXXXXXXX01",
    "name": "MRR",
    "membership": "current",
    "note": "Requested by the customer."
  },
  "total": 2900,
  "total_basis": "sum_over_window",
  "split": {
    "dimension": "plan",
    "limit": 10,
    "returned": 1,
    "truncated": false,
    "exhaustive": false,
    "exhaustive_basis": "rows_add_to_total",
    "note": "Requested by the customer.",
    "series": [
      {
        "key": "biz_XXXXXXXXXXXX01",
        "label": "MRR",
        "kind": "dimension",
        "total": 2900,
        "points": []
      }
    ]
  },
  "compare": {
    "basis": "previous_window",
    "start": "2026-03-14",
    "end": "2026-04-14",
    "previous_total": 2900,
    "change_minor": 2900,
    "change_bps": 1000
  },
  "coverage": {
    "complete_from": "2026-03-14",
    "missing_periods": [
      "2026-03-14"
    ],
    "note": "Requested by the customer."
  },
  "freshness": {
    "includes_current_period": false,
    "max_age_seconds": 1
  }
}
```

```json [400 Bad Request]
{
  "error": {
    "type": "invalid_request_error",
    "message": "measure was not given"
  }
}
```

```json [422 Unprocessable Entity]
{
  "error": {
    "type": "invalid_request_error",
    "message": "A refusal with a sentence: unknown measure, unsupported dimension or grain, a range with no periods, or money in more than one currency"
  }
}
```

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


## Sitemap

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