# Query analytics with the API

The Team API has one analytics endpoint. You send a query as JSON, and you get the numbers in one response.

```text
POST https://api.lettermint.co/v1/analytics
```

## Before you start

You need:

- A team on the Pro plan.
- A [Team API token](/platform/teams/api-tokens) with the `read:analytics` ability.

:::warning
A Team API token can read the analytics of every project in your team. Keep the token on your server. Do not put it in a browser or a mobile app.
:::

## Send your first query

The only required field is `metrics`. This query returns the delivered recipients, the bounced recipients, and the bounce rate of the last 30 days.

The examples use version 3 of the Lettermint SDKs. See [SDKs](/sdks) to install one. Without an SDK, send the same fields as JSON, as the cURL example shows.

<CodeTabs syncKey="lettermint-language">

```typescript title="Node.js"
import { Lettermint } from "lettermint";

const lettermint = new Lettermint({
  teamToken: process.env.LETTERMINT_TEAM_TOKEN!,
});

const result = await lettermint.analytics({
  metrics: ["delivered", "bounced", "bounce_rate"],
});

console.log(result.data.summary?.metrics);
```

```php title="PHP"
<?php

use Lettermint\Lettermint;

$lettermint = new Lettermint(teamToken: getenv('LETTERMINT_TEAM_TOKEN'));

$result = $lettermint->analytics([
    'metrics' => ['delivered', 'bounced', 'bounce_rate'],
]);

echo $result->data->summary->metrics->delivered;
```

```python title="Python"
import os

from lettermint import Lettermint

with Lettermint(team_token=os.environ["LETTERMINT_TEAM_TOKEN"]) as lettermint:
    result = lettermint.analytics({"metrics": ["delivered", "bounced", "bounce_rate"]})

    print(result["data"]["summary"]["metrics"])
```

```go title="Go"
package main

import (
	"context"
	"fmt"
	"log"
	"os"

	lettermint "github.com/lettermint/lettermint-go/v3"
)

func main() {
	client, err := lettermint.New(lettermint.WithTeamToken(os.Getenv("LETTERMINT_TEAM_TOKEN")))
	if err != nil {
		log.Fatal(err)
	}

	result, err := client.Analytics(context.Background(), lettermint.AnalyticsQuery{
		Metrics: []lettermint.AnalyticsMetric{
			lettermint.AnalyticsMetricDelivered,
			lettermint.AnalyticsMetricBounced,
			lettermint.AnalyticsMetricBounceRate,
		},
	})
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(result.Data.Summary.Metrics.Delivered)
}
```

```java title="Java"
import co.lettermint.Lettermint;
import co.lettermint.types.AnalyticsMetric;
import co.lettermint.types.AnalyticsQuery;
import co.lettermint.types.AnalyticsResponse;
import java.util.List;

Lettermint lettermint = Lettermint.builder()
    .teamToken(System.getenv("LETTERMINT_TEAM_TOKEN"))
    .build();

AnalyticsResponse result = lettermint.analytics(AnalyticsQuery.builder()
    .metrics(List.of(
        AnalyticsMetric.DELIVERED,
        AnalyticsMetric.BOUNCED,
        AnalyticsMetric.BOUNCE_RATE))
    .build());

System.out.println(result.data().summary().metrics().delivered());
```

```bash title="cURL"
curl -X POST "https://api.lettermint.co/v1/analytics" \
  -H "Authorization: Bearer $LETTERMINT_TEAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{"metrics": ["delivered", "bounced", "bounce_rate"]}'
```

</CodeTabs>

**Response:**

```json
{
  "data": {
    "summary": {
      "metrics": {
        "delivered": 48210,
        "bounced": 312,
        "bounce_rate": 0.006423586090465504
      },
      "rate_bases": {
        "bounce_rate": { "numerator": 312, "denominator": 48571 }
      }
    }
  },
  "meta": {
    "time_basis": "event",
    "timezone": "UTC",
    "interval": "day",
    "from": "2026-10-13T00:00:00.000000Z",
    "to": "2026-11-12T00:00:00.000000Z",
    "effective_to": "2026-11-11T09:41:07.512000Z",
    "alignment": "hour",
    "generated_at": "2026-11-11T09:41:07.512000Z",
    "available_since": "2026-10-12T00:00:00.000000Z",
    "partial": false,
    "ongoing": true,
    "collection_completeness": "best_effort",
    "last_ingested_at": "2026-11-11T09:40:31.000000Z",
    "metric_definition_version": "analytics-v2",
    "ranked_group_limit": 1000
  },
  "pagination": {
    "total_groups": 0,
    "returned_groups": 0,
    "next_cursor": null,
    "truncated": false
  }
}
```

The bounce rate is a fraction. `0.0064` is 0.64 percent. `rate_bases` shows the two counts behind the rate: 312 bounces out of 48,571 recipients with a final result.

## The fields of a query

| Field | Purpose | Default | Limits |
| --- | --- | --- | --- |
| `metrics` | The [metrics](/platform/analytics/metrics) to return | Required | 1 to 100 |
| `from`, `to` | The time window | The last 30 days, with today | Send both or none. 90 days at most. |
| `timezone` | The time zone for dates and buckets | `UTC` | An IANA name, such as `Europe/Amsterdam` |
| `include` | The sections of the result | `["summary"]` | `summary`, `time_series`, `breakdown` |
| `interval` | The size of a time-series bucket | `day` | `hour` or `day` |
| `group_by` | The [dimensions](/platform/analytics/dimensions) of the breakdown | None | 3 at most |
| `filters` | Conditions that limit the query | None | 20 at most, with 100 values each |
| `compare` | A comparison with an earlier period | None | `previous_period` |
| `include_trend` | A time series for each row of the breakdown | `false` | Needs a breakdown |
| `sort` | The order of the breakdown rows | The first metric, highest first | A metric from `metrics` |
| `limit` | The number of breakdown rows in one page | `50` | 1 to 200 |
| `cursor` | The next page of a breakdown | None | Valid for 60 seconds |

The API rejects a field that is not in this table.

## Write a query with an SDK

Each SDK has an `analytics` method. It takes the fields of the table above and returns the response as typed data.

The rest of this page shows each query as JSON, because the fields are the same in every language. This example shows one query with a window, a breakdown, a filter, a sort order, and a comparison in each SDK.

<CodeTabs syncKey="lettermint-language">

```typescript title="Node.js"
import { Lettermint } from "lettermint";

const lettermint = new Lettermint({
  teamToken: process.env.LETTERMINT_TEAM_TOKEN!,
});

const result = await lettermint.analytics({
  metrics: ["delivery_attempts", "delivered", "deferral_rate"],
  from: "2026-11-04",
  to: "2026-11-10",
  timezone: "Europe/Amsterdam",
  include: ["summary", "breakdown"],
  group_by: ["provider_family"],
  filters: [
    { dimension: "message_type", operator: "eq", values: ["transactional"] },
  ],
  sort: { metric: "deferral_rate", direction: "desc" },
  compare: "previous_period",
  limit: 10,
});

for (const row of result.data.breakdown ?? []) {
  console.log(row.dimensions.provider_family, row.metrics.deferral_rate);
}
```

```php title="PHP"
<?php

use Lettermint\Lettermint;

$lettermint = new Lettermint(teamToken: getenv('LETTERMINT_TEAM_TOKEN'));

$result = $lettermint->analytics([
    'metrics' => ['delivery_attempts', 'delivered', 'deferral_rate'],
    'from' => '2026-11-04',
    'to' => '2026-11-10',
    'timezone' => 'Europe/Amsterdam',
    'include' => ['summary', 'breakdown'],
    'group_by' => ['provider_family'],
    'filters' => [
        ['dimension' => 'message_type', 'operator' => 'eq', 'values' => ['transactional']],
    ],
    'sort' => ['metric' => 'deferral_rate', 'direction' => 'desc'],
    'compare' => 'previous_period',
    'limit' => 10,
]);

foreach ($result->data->breakdown as $row) {
    echo $row->dimensions['provider_family'].' '.$row->metrics->deferral_rate.PHP_EOL;
}
```

```python title="Python"
import os

from lettermint import Lettermint

with Lettermint(team_token=os.environ["LETTERMINT_TEAM_TOKEN"]) as lettermint:
    result = lettermint.analytics(
        {
            "metrics": ["delivery_attempts", "delivered", "deferral_rate"],
            "from": "2026-11-04",
            "to": "2026-11-10",
            "timezone": "Europe/Amsterdam",
            "include": ["summary", "breakdown"],
            "group_by": ["provider_family"],
            "filters": [
                {"dimension": "message_type", "operator": "eq", "values": ["transactional"]},
            ],
            "sort": {"metric": "deferral_rate", "direction": "desc"},
            "compare": "previous_period",
            "limit": 10,
        }
    )

    for row in result["data"]["breakdown"]:
        print(row["dimensions"]["provider_family"], row["metrics"]["deferral_rate"])
```

```go title="Go"
package main

import (
	"context"
	"fmt"
	"log"
	"os"

	lettermint "github.com/lettermint/lettermint-go/v3"
)

func main() {
	client, err := lettermint.New(lettermint.WithTeamToken(os.Getenv("LETTERMINT_TEAM_TOKEN")))
	if err != nil {
		log.Fatal(err)
	}

	result, err := client.Analytics(context.Background(), lettermint.AnalyticsQuery{
		Metrics: []lettermint.AnalyticsMetric{
			lettermint.AnalyticsMetricDeliveryAttempts,
			lettermint.AnalyticsMetricDelivered,
			lettermint.AnalyticsMetricDeferralRate,
		},
		From:     lettermint.Ptr("2026-11-04"),
		To:       lettermint.Ptr("2026-11-10"),
		Timezone: lettermint.Ptr("Europe/Amsterdam"),
		Include: []lettermint.AnalyticsSection{
			lettermint.AnalyticsSectionSummary,
			lettermint.AnalyticsSectionBreakdown,
		},
		GroupBy: []lettermint.AnalyticsGroupDimension{
			lettermint.AnalyticsCatalogueGroupDimensionProviderFamily,
		},
		Filters: []lettermint.AnalyticsFilter{{
			Dimension: lettermint.AnalyticsCatalogueDimensionMessageType,
			Operator:  lettermint.AnalyticsFilterOperatorEq,
			Values:    []string{"transactional"},
		}},
		Sort: &lettermint.AnalyticsSort{
			Metric:    lettermint.AnalyticsMetricDeferralRate,
			Direction: lettermint.AnalyticsSortDirectionDesc,
		},
		Compare: lettermint.Ptr(lettermint.AnalyticsComparisonPreviousPeriod),
		Limit:   lettermint.Ptr(10),
	})
	if err != nil {
		log.Fatal(err)
	}

	for _, row := range result.Data.Breakdown {
		provider := "unknown"
		if value := row.Dimensions["provider_family"]; value != nil {
			provider = *value
		}

		fmt.Println(provider, row.Metrics.DeferralRate)
	}
}
```

```java title="Java"
import co.lettermint.Lettermint;
import co.lettermint.types.AnalyticsBreakdownRow;
import co.lettermint.types.AnalyticsComparison;
import co.lettermint.types.AnalyticsDimension;
import co.lettermint.types.AnalyticsFilter;
import co.lettermint.types.AnalyticsFilterOperator;
import co.lettermint.types.AnalyticsGroupDimension;
import co.lettermint.types.AnalyticsMetric;
import co.lettermint.types.AnalyticsQuery;
import co.lettermint.types.AnalyticsResponse;
import co.lettermint.types.AnalyticsSection;
import co.lettermint.types.AnalyticsSort;
import co.lettermint.types.AnalyticsSortDirection;
import java.util.List;

Lettermint lettermint = Lettermint.builder()
    .teamToken(System.getenv("LETTERMINT_TEAM_TOKEN"))
    .build();

AnalyticsResponse result = lettermint.analytics(AnalyticsQuery.builder()
    .metrics(List.of(
        AnalyticsMetric.DELIVERY_ATTEMPTS,
        AnalyticsMetric.DELIVERED,
        AnalyticsMetric.DEFERRAL_RATE))
    .from("2026-11-04")
    .to("2026-11-10")
    .timezone("Europe/Amsterdam")
    .include(List.of(AnalyticsSection.SUMMARY, AnalyticsSection.BREAKDOWN))
    .groupBy(List.of(AnalyticsGroupDimension.PROVIDER_FAMILY))
    .filters(List.of(AnalyticsFilter.builder()
        .dimension(AnalyticsDimension.MESSAGE_TYPE)
        .operator(AnalyticsFilterOperator.EQ)
        .values(List.of("transactional"))
        .build()))
    .sort(AnalyticsSort.builder()
        .metric(AnalyticsMetric.DEFERRAL_RATE)
        .direction(AnalyticsSortDirection.DESC)
        .build())
    .compare(AnalyticsComparison.PREVIOUS_PERIOD)
    .limit(10)
    .build());

for (AnalyticsBreakdownRow row : result.data().breakdown()) {
    System.out.println(row.dimensions().get("provider_family") + " " + row.metrics().deferralRate());
}
```

```bash title="cURL"
curl -X POST "https://api.lettermint.co/v1/analytics" \
  -H "Authorization: Bearer $LETTERMINT_TEAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "metrics": ["delivery_attempts", "delivered", "deferral_rate"],
    "from": "2026-11-04",
    "to": "2026-11-10",
    "timezone": "Europe/Amsterdam",
    "include": ["summary", "breakdown"],
    "group_by": ["provider_family"],
    "filters": [
      {"dimension": "message_type", "operator": "eq", "values": ["transactional"]}
    ],
    "sort": {"metric": "deferral_rate", "direction": "desc"},
    "compare": "previous_period",
    "limit": 10
  }'
```

</CodeTabs>

The SDKs differ in how you write the query and read the result.

| SDK | Query | Result |
| --- | --- | --- |
| Node.js | An object with the JSON field names. The type is `AnalyticsQuery`. | `result.data.summary?.metrics.delivered` |
| PHP | An array with the JSON field names. | `$result->data->summary->metrics->delivered` |
| Python | A `dict` with the JSON field names. The type is `AnalyticsQuery`. | `result["data"]["summary"]["metrics"]["delivered"]` |
| Go | The struct `AnalyticsQuery`. Metrics, sections, and operators are constants, such as `AnalyticsMetricDelivered`. Wrap an optional field in `lettermint.Ptr`. | `result.Data.Summary.Metrics.Delivered` |
| Java | The builder `AnalyticsQuery.builder()`. Metrics, sections, and operators are constants, such as `AnalyticsMetric.DELIVERED`. | `result.data().summary().metrics().delivered()` |

Two details apply to Go and Java:

- A [tag dimension](/platform/analytics/dimensions#tags) has no constant. In Go, write `lettermint.AnalyticsGroupDimension("tag:campaign")`. In Java, write `AnalyticsGroupDimension.of("tag:campaign")`. For a filter, use `AnalyticsDimension` in the same way.
- A metric can have [no value](#null-values). In Java, the accessor returns `null`. In Go, a metric is a `Nullable` value, and its `Get` method returns the value and `false` when there is no value.

A section that you did not ask for in `include` is absent from the result. In Go, `result.Data.Summary` is then `nil`.

## Set the time window

You can give the window as two dates or as two instants. Do not mix the two forms.

**Dates.** Use `YYYY-MM-DD`. Both dates are included. Lettermint reads the dates in the time zone of the query.

```json
{
  "metrics": ["delivered"],
  "from": "2026-11-01",
  "to": "2026-11-07",
  "timezone": "Europe/Amsterdam"
}
```

This window starts on 1 November at 00:00 Amsterdam time. It ends on 8 November at 00:00.

**Instants.** Use RFC 3339 with an offset, such as `2026-11-01T08:00:00Z`. The `to` instant is not included.

A window starts and ends at a whole UTC hour. If an instant is not at a whole hour, Lettermint moves `from` back and `to` forward to the nearest whole UTC hour. `meta.from` and `meta.to` show the window that Lettermint used.

A window that includes the present time is correct, but not finished. `meta.ongoing` is `true`, and `meta.effective_to` shows where the data ends.

## Add a time series

Add `time_series` to `include` to get one row for each bucket.

```json
{
  "metrics": ["delivered", "deferral_rate"],
  "from": "2026-11-10",
  "to": "2026-11-10",
  "timezone": "Europe/Amsterdam",
  "include": ["summary", "time_series"],
  "interval": "hour"
}
```

| Interval | Bucket | Longest window |
| --- | --- | --- |
| `hour` | One hour | 31 days, which is 744 buckets |
| `day` | One calendar day in the time zone of the query | 90 days |

Each bucket has this form:

```json
{
  "from": "2026-11-10T09:00:00+01:00",
  "to": "2026-11-10T10:00:00+01:00",
  "available": true,
  "partial": false,
  "metrics": { "delivered": 1874, "deferral_rate": 0.0211864406779661 },
  "rate_bases": { "deferral_rate": { "numerator": 40, "denominator": 1888 } }
}
```

| Field | Meaning |
| --- | --- |
| `available` | `false` when Lettermint has no data for the bucket. The metrics are then `null`. This applies to a bucket in the future and to a bucket before `meta.available_since`. |
| `partial` | `true` when the bucket covers less than its full length. An example is the current hour. |

The series has a row for each bucket, also when nothing happened. A bucket without events has the count `0`.

### Time zones

Daily buckets follow the calendar days of the time zone. A day with a change to or from daylight saving time has 23 or 25 hours.

Some time zones have an offset that is not a whole hour, such as `Asia/Kolkata`. In these zones, an hourly bucket starts at 30 minutes past the local hour. The first and the last daily bucket can be partial.

## Break down the results

A breakdown splits each metric by the values of one or more dimensions. Add `breakdown` to `include`, and put the dimensions in `group_by`. You must send both.

```json
{
  "metrics": ["delivery_attempts", "delivered", "deferral_rate"],
  "from": "2026-11-04",
  "to": "2026-11-10",
  "include": ["summary", "breakdown"],
  "group_by": ["provider_family"],
  "sort": { "metric": "deferral_rate", "direction": "desc" },
  "limit": 5
}
```

Each row has the values of its dimensions and the metrics for that group:

```json
{
  "dimensions": { "provider_family": "Microsoft" },
  "metrics": { "delivery_attempts": 9120, "delivered": 8350, "deferral_rate": 0.08333333333333333 },
  "rate_bases": { "deferral_rate": { "numerator": 760, "denominator": 9120 } }
}
```

With two or three dimensions, each row is one combination of values. A value of `null` in `dimensions` means that the email has no value for that dimension.

Some dimensions change what a metric counts, and not every dimension works with every metric. See [Dimensions](/platform/analytics/dimensions).

### Sort the rows

`sort` takes a metric from `metrics` and a direction, `asc` or `desc`. Without `sort`, Lettermint orders the rows by the first metric, highest first. Rows where the metric is `null` come last.

### Get more rows

`pagination` tells you how many groups there are.

| Field | Meaning |
| --- | --- |
| `total_groups` | The number of groups that match the query |
| `returned_groups` | The number of rows in this response |
| `next_cursor` | A value for the next page, or `null` when there are no more rows |
| `truncated` | `true` when there are more groups than Lettermint ranks |

To get the next page, send the same query again and add `cursor`. Do not change another field. A cursor is valid for 60 seconds. After that time, send the query again without `cursor`.

Lettermint ranks 1,000 groups at most, in the order of `sort`. When `truncated` is `true`, the breakdown holds the first 1,000 groups only. Add a filter, or sort by the metric that matters to you.

### Add a trend to each row

Set `include_trend` to `true` to get a time series in each row, in the field `trend`. The rows of one page multiplied by the number of buckets must stay below 10,000. With `compare`, the buckets of the two periods both count. Use a lower `limit` or a shorter window if the API rejects the query.

## Filter the query

A filter limits all sections of the result to the email that matches. This query counts the bounces at two providers, for one project:

```json
{
  "metrics": ["bounced", "bounce_rate"],
  "filters": [
    { "dimension": "provider_family", "operator": "in", "values": ["Google", "Microsoft"] },
    { "dimension": "project_id", "operator": "eq", "values": ["0b8f4a7e-5a52-4d0c-9d0e-6f3c3f2b1a10"] }
  ]
}
```

A query combines its filters with AND. See [Filters](/platform/analytics/dimensions#filters) for the operators.

## Compare two periods

Set `compare` to `previous_period`. Lettermint runs the query a second time for the period of the same length that ends where your window starts.

```json
{
  "metrics": ["delivered", "bounce_rate"],
  "from": "2026-11-04",
  "to": "2026-11-10",
  "compare": "previous_period"
}
```

Here the window has seven days, so the previous period is 28 October to 3 November. The summary, each bucket, and each breakdown row get two more fields:

```json
{
  "metrics": { "delivered": 48210, "bounce_rate": 0.006423586090465504 },
  "rate_bases": { "bounce_rate": { "numerator": 312, "denominator": 48571 } },
  "previous": {
    "metrics": { "delivered": 46680, "bounce_rate": 0.006168637794605632 },
    "rate_bases": { "bounce_rate": { "numerator": 290, "denominator": 47012 } }
  },
  "change": {
    "delivered": { "absolute": 1530, "relative": 0.032776349614395885 },
    "bounce_rate": { "absolute": 0.0002549482958598718, "relative": 0.041329756155049295, "percentage_points": 0.02549482958598718 }
  }
}
```

| Field | Meaning |
| --- | --- |
| `absolute` | The current value minus the previous value |
| `relative` | The change as a fraction of the previous value. `0.03` is an increase of 3 percent. |
| `percentage_points` | For rates only. The difference between the two rates in percentage points. |

A change is `null` when Lettermint cannot compare the two periods fairly:

- One of the periods starts before `meta.available_since`.
- One of the two values is `null`.
- The previous value is `0`. Only `relative` is `null` in this case.

When your window is not finished, Lettermint compares the same part of the previous period. On Wednesday at 14:00, a query for this week is compared with last week until Wednesday at 14:00.

`meta.comparison` shows the previous period that Lettermint used.

## Read the metadata

`meta` describes the data behind the numbers.

| Field | Meaning |
| --- | --- |
| `from`, `to` | The window that Lettermint used |
| `effective_to` | The end of the data. For a finished window, this is equal to `to`. |
| `timezone`, `interval` | The time zone and the bucket size of the query |
| `alignment` | `hour` or `day`. The unit that Lettermint moved the window limits to. `day` applies to queries with `from_address` or `subject`. |
| `ongoing` | `true` when the window ends in the future |
| `partial` | `true` when a part of the window has no data, because it starts before `available_since` |
| `available_since` | The start of the analytics history that you can query |
| `generated_at` | The time at which Lettermint ran the query |
| `last_ingested_at` | The time of the newest data that the query read. `null` when the query matched no data. |
| `ranked_group_limit` | The largest number of groups that this query can rank |
| `comparison` | The previous period. Present only with `compare`. |

### Null values

`null` and `0` are different. `0` means that Lettermint counted and found nothing. `null` means that Lettermint cannot give a value.

| A metric is `null` when | Example |
| --- | --- |
| The bucket has no data | A bucket in the future, or before `available_since` |
| The rate has a denominator of zero | `bounce_rate` in an hour without a final delivery result |
| The latency has no samples | `delivery_latency_p95_ms` in an hour without deliveries |
| The metric has no value for the dimensions of the query | `accepted` with a filter on `provider_family` |

## Limits

| Limit | Value |
| --- | --- |
| Queries for each team | 60 in one minute, and 2 at the same time |
| Time for one query | 10 seconds |
| Window | 90 days |
| Hourly buckets | 744 |
| Dimensions in `group_by` | 3 |
| Filters | 20, with 100 values each |
| Rows in one page | 200 |
| Groups that Lettermint ranks | 1,000 |
| Rows multiplied by buckets, with `include_trend` | 10,000 |
| Lifetime of a cursor | 60 seconds |

The API and the MCP server share the query limits of a team. A request for the next page counts as one query.

## Errors

| Status | Cause | What to do |
| --- | --- | --- |
| `400` | The body is not valid JSON. | Correct the body. |
| `401` | The token is missing or not valid. | Check the `Authorization` header. |
| `403` | The team is not on the Pro plan, the token does not have `read:analytics`, or the query uses personal data without `read:messages`. | Read the message. It names the cause. |
| `415` | The `Content-Type` header is not `application/json`. | Send JSON. |
| `422` | The query is not valid. | Read `errors`. It names the field. |
| `429` | The team sent too many queries. | Wait for the number of seconds in the `Retry-After` header. |
| `503` | Analytics are not available at this moment. | Try again later. |
| `504` | The query needed more than 10 seconds. | Use a shorter window, fewer dimensions, or fewer rows. |

A `422` response names the problem:

```json
{
  "message": "The metric human_opens cannot be combined with dimension tag:campaign.",
  "errors": {
    "metrics": ["The metric human_opens cannot be combined with dimension tag:campaign."]
  }
}
```

:::tip
A query that reached the time limit will probably reach it again. Change the query before you send it again.
:::

## Next steps

- [Examples](/platform/analytics/examples). Copy complete queries for common questions.
- [Metrics](/platform/analytics/metrics) and [Dimensions](/platform/analytics/dimensions). Look up a name or a rule.
- [API reference](/api-reference/team). See the full schema of the request and the response.
