# List metrics

URL: https://docs.hanzo.ai/docs/openapi/campaign/get-campaign-by-id-metrics

> Returns a campaign's results over a window: the analytics funnel (impressions, clicks, conversions, revenue, visitors), the spend each channel's connector reports, and the derived growth KPIs — CTR, CVR, CAC and ROAS.

`GET /v1/campaign/{id}/metrics`

> [Campaign →](https://docs.hanzo.ai/docs/openapi/campaign) · [All API references →](https://docs.hanzo.ai/docs/openapi) · [Get started →](https://docs.hanzo.ai/docs/guides)

|               |                                                 |
| ------------- | ----------------------------------------------- |
| **Address**   | `https://api.hanzo.ai/v1/campaign/{id}/metrics` |
| **Method**    | `GET`                                           |
| **Operation** | `get_campaign_by_id_metrics`                    |
| **Auth**      | `Authorization: Bearer $HANZO_API_KEY`          |

Returns a campaign's results over a window: the analytics
funnel (impressions, clicks, conversions, revenue, visitors), the spend each
channel's connector reports, and the derived growth KPIs — CTR, CVR, CAC and
ROAS.

There is exactly ONE metrics plane and nothing is stored here: the funnel is an
analytics query over the campaign's utm\_campaign-tagged events, and the spend is
each provider's own number read through the org's connector. A warehouse that is
not emitting yet degrades to available:false with zeroes — honest-empty, never a
500 and never a fabricated number. When the campaign runs more than one creative
and an experiment is wired, abTest carries the A/B analysis.

## Request

4 fields.

| Field   | In    | Type   | Required | Description                                        |
| ------- | ----- | ------ | -------- | -------------------------------------------------- |
| `id`    | path  | string | yes      | ID is the campaign to report on, from the path.    |
| `range` | query | string | —        | Range is the lookback window: 24h, 7d, 30d or 90d. |
| `start` | query | string | —        | Start is an explicit RFC3339 window start.         |
| `end`   | query | string | —        | End is an explicit RFC3339 window end.             |

## Response

| Status    | Body                     | Meaning |
| --------- | ------------------------ | ------- |
| `200`     | campaign.campaignResults | ok      |
| `default` | problem-details          | refused |

`200` body — 26 fields.

| Field                   | In   | Type                      | Always | Description                                                                                                                                                                                                                               |
| ----------------------- | ---- | ------------------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `abTest`                | body | any                       | —      | ABTest is the creative A/B analysis from the experiments primitive (experiment.Analyze, pull-model), present only when the campaign runs more than one creative and an experiment is wired.                                               |
| `available`             | body | boolean                   | —      | Available is false when the analytics warehouse is not connected or the query failed: the funnel below is then zero because nothing could be read, not because nothing happened.                                                          |
| `cac`                   | body | number (double)           | —      | CAC is customer acquisition cost: spend DOLLARS per conversion, rounded to cents.                                                                                                                                                         |
| `campaignId`            | body | string                    | —      | CampaignID is the campaign these results are for, echoed from the request.                                                                                                                                                                |
| `channels`              | body | campaign.ChannelMetric\[] | —      | Channels is the per-channel spend breakdown that SpendCents sums, one row per channel on the campaign including the ones that never launched.                                                                                             |
| `channels[].externalId` | body | string                    | —      | ExternalID is the provider-side id of the execution the spend belongs to.                                                                                                                                                                 |
| `channels[].kind`       | body | string                    | —      | Kind is which channel this row is: paid, organic or email.                                                                                                                                                                                |
| `channels[].platform`   | body | string                    | —      | Platform is the provider the spend was read from: meta, google, x, instagram, or the email provider.                                                                                                                                      |
| `channels[].spendCents` | body | integer (int64)           | —      | SpendCents is what the provider itself reports this channel spent, in CENTS.                                                                                                                                                              |
| `channels[].spendError` | body | string                    | —      | SpendError is why this channel's spend could not be read (connector not connected, provider error), as one secret-free line.                                                                                                              |
| `channels[].status`     | body | string                    | —      | Status is the channel's launch state on the campaign — pending, live, paused, failed or unavailable.                                                                                                                                      |
| `clicks`                | body | integer (int64)           | —      | Clicks is the campaign's click events over the window.                                                                                                                                                                                    |
| `conversions`           | body | integer (int64)           | —      | Conversions is the terminal funnel events attributed to the campaign — orders completed, signups completed, explicit conversion events.                                                                                                   |
| `ctr`                   | body | number (double)           | —      | CTR is clicks per impression, a fraction rounded to 4 places (0.0123 = 1.23%), not a percentage.                                                                                                                                          |
| `cvr`                   | body | number (double)           | —      | CVR is conversions per click, a fraction rounded to 4 places.                                                                                                                                                                             |
| `end`                   | body | string                    | —      | End is the window's end, RFC3339 UTC — the read's own clock unless an explicit pair was given.                                                                                                                                            |
| `impressions`           | body | integer (int64)           | —      | Impressions is how many times the campaign's creatives were shown, counted from its utm\_campaign-tagged impression events.                                                                                                               |
| `name`                  | body | string                    | —      | Name is the campaign's display name at read time, so a result can be labelled without a second fetch.                                                                                                                                     |
| `range`                 | body | string                    | —      | Range is the window actually used: 24h, 7d, 30d, 90d, or "custom" when an explicit start/end pair was honored.                                                                                                                            |
| `revenue`               | body | number (double)           | —      | Revenue is the summed revenue attribute of the campaign's events, in whole CURRENCY UNITS (dollars) — the one money value here that is not in cents.                                                                                      |
| `roas`                  | body | number (double)           | —      | ROAS is return on ad spend: revenue per spend DOLLAR, rounded to 2 places (2.5 = $2.50 back per $1).                                                                                                                                      |
| `source`                | body | string                    | —      | Source names the analytics table the funnel was read from, so an operator can see exactly what was counted.                                                                                                                               |
| `spendCents`            | body | integer (int64)           | —      | SpendCents is the campaign's total spend in CENTS: the sum of what each live channel's provider reports.                                                                                                                                  |
| `start`                 | body | string                    | —      | Start is the window's inclusive start, RFC3339 UTC.                                                                                                                                                                                       |
| `status`                | body | string                    | —      | Status is the campaign's lifecycle state at read time — draft, live, paused, completed or failed.                                                                                                                                         |
| `visitors`              | body | integer (int64)           | —      | Visitors is how many distinct people the campaign reached, counted by event identity across ALL its events in the window — not a subset of Impressions, so it can exceed them for a campaign whose provider reports clicks but not views. |

Failure carries the platform error shape — see [Errors](https://docs.hanzo.ai/docs/errors).

## Examples

**CLI**

```bash
hanzo campaign metrics <id>
```

**SDK**

**.ts**

```ts
import { Configuration, CampaignApi } from 'hanzoai';

const api = new CampaignApi(new Configuration({ accessToken: process.env.HANZO_API_KEY }));
const { data } = await api.getCampaignByIdMetrics({ id: 'id' });
```

**.py**

```python
from hanzoai.cloud import ApiClient, Configuration
from hanzoai.cloud.api import CampaignApi

client = ApiClient(Configuration(access_token=os.environ["HANZO_API_KEY"]))
result = CampaignApi(client).get_campaign_by_id_metrics(id='id')
```

**.go**

```go
cfg := hanzoai.NewConfiguration()
cfg.AddDefaultHeader("Authorization", "Bearer "+os.Getenv("HANZO_API_KEY"))
client := hanzoai.NewAPIClient(cfg)

resp, _, err := client.CampaignAPI.GetCampaignByIdMetrics(context.Background()).Execute()
if err != nil {
	return err
}
```

**.rs**

```rust
use hanzo_client::apis::{configuration::Configuration, campaign_api};

let mut cfg = Configuration::new();
cfg.bearer_access_token = std::env::var("HANZO_API_KEY").ok();

let result = campaign_api::get_campaign_by_id_metrics(&cfg, Default::default()).await?;
```

**.java**

```java
import ai.hanzo.cloud.ApiClient;
import ai.hanzo.cloud.api.CampaignApi;

ApiClient client = new ApiClient();
client.setBearerToken(System.getenv("HANZO_API_KEY"));

var result = new CampaignApi(client).getCampaignByIdMetrics();
```

**HTTP**

```bash
curl https://api.hanzo.ai/v1/campaign/<id>/metrics \
  -H "Authorization: Bearer $HANZO_API_KEY"
```

**MCP**

MCP reaches **campaign** through the `campaign` tool, which names its 9 operations with its own verbs — this one among them, under a name only MCP declares. `describe` explains any of them:

```bash
curl -X POST https://api.hanzo.ai/v1/mcp \
  -H "Content-Type: application/json" \
  -d '{
       "jsonrpc": "2.0",
       "id": 1,
       "method": "tools/call",
       "params": {
         "name": "describe",
         "arguments": {
           "op": "list_campaigns"
         }
       }
     }'
```

---

[Campaign API](https://docs.hanzo.ai/docs/openapi/campaign) · [All Hanzo APIs](https://docs.hanzo.ai/docs/openapi) · [Interactive reference](https://docs.hanzo.ai/docs/openapi/reference)
