URL: /code/apps/analytics-and-experiments/query · Id: `page:/code/apps/analytics-and-experiments/query`

# Metric.query

> Read what your ads, posts and emails did, across every connected account.

`POST https://api.sb/{your-domain}/metrics/query`

Read what your ads, posts and emails did, across every connected account.

- **Action class:** None
- **Approval:** Runs on its own
- **Emits:** `Metric.queried`
- **Sites:** [13 sites](#on-each-venue)
- **Price:** Free

```text title="Run it · Ask"
Which of the three LinkedIn test ads brought a lead in its first week?
```

```ts title="Run it · SDK"
import { $ } from 'startup-builder'

const result = await $.try.Metric.query({ metric: 'new-leads', shape: 'trend', breakdown: 'ad', window: '7d' })
```

```json title="Run it · Response"
{
  "api": "https://api.sb/leaseline.app",
  "metric": {
    "$id": "https://api.sb/leaseline.app/metrics/new-leads",
    "$type": "Metric",
    "unit": "count"
  },
  "from": "2026-10-28",
  "rows": [
    {
      "ad": "late-rent",
      "value": 1,
      "series": [
        0,
        0,
        1,
        0,
        0,
        0,
        0
      ],
      "readable": false
    },
    {
      "ad": "owner-statement",
      "value": 0,
      "series": [
        0,
        0,
        0,
        0,
        0,
        0,
        0
      ],
      "readable": false
    },
    {
      "ad": "renewal-notice",
      "value": 0,
      "series": [
        0,
        0,
        0,
        0,
        0,
        0,
        0
      ],
      "readable": false
    }
  ],
  "action": {
    "operation": "Metric.query",
    "mode": "try",
    "status": "done"
  }
}
```

Reads a number from your Events as a trend, funnel, retention curve or path, split by any property. [The hub’s definition of `Metric.query`](https://startupbuilder.dev/business/nouns/metric/query).</HubDefinition>

## Inputs
| Name | Type | Default | Meaning |
| --- | --- | --- | --- |
| `metric` | `Metric` | `null` | The Metric to read; omitted, the query names Events directly. |
| `shape` | `string` | Required | The analysis to run. |
| `breakdown` | `string` | `null` | The property to split by, such as an Organization or a Motion. Unset: one row for the whole window. |
| `window` | `string` | `'30d'` | The time range, such as `30d`. Unset: the last 30 days. |

## Outputs
| Field | Type | What `null` means |
| --- | --- | --- |
| `rows` | `json` | Never null: empty when nothing in the window matches. |
| `metric` | `Metric` | Null when the query names Events directly. |
| `rows[].value` | `number`, in the Metric’s unit | No reading: the source sent nothing for that split. Zero is a reading, never null. |
| `rows[].series` | `number[]` | Null unless `shape` is `trend`: then one value per day of the window, oldest first. |
| `rows[].readable` | `boolean` | Never null: `false` when the counts are too small to rank this row against the others. |

## Effects
**Action class**

None: no approval rule names it, so it runs within its seat’s Permissions and Limits

**Modes**

- `try`: Runs the query and returns real rows: it reads, so try and do return the same.
- `do`: Runs the query and returns the rows.

**Idempotency**

Yes: a read changes nothing, so a repeat returns the rows as they stand then.

**Saved or shared**

Nothing is saved. Spend and results read from your own accounts are never shared with other startups.

**Emits**

`Metric.queried` with `{ metric, shape, breakdown, window }`

**Performed by**

[Analyst](https://startupbuilder.dev/agents/marketing/analyst)

**Price**

Free on your own account: the platform charges nothing for the call.

## On each Venue
| Site | Fields | Formats |
| --- | --- | --- |
| [Bing](#bing-webmaster) | `breakdown` | 1 |
| [Facebook](#facebook) | None. | 1 |
| [Google Search and Google Display and YouTube](#google-ads) | None. | 2 |
| [Google Analytics](#google-analytics) | `breakdown`, `metric`, `window` | None. |
| [Google Business Profile](#google-business-profile) | None. | None. |
| [Google Search Console](#google-search-console) | `breakdown`, `window`, `metric` | None. |
| [Instagram](#instagram) | None. | 4 |
| [LinkedIn](#linkedin-ads) | None. | 3 |
| [Facebook and Instagram](#meta-ads) | None. | 5 |
| [Bing](#microsoft-ads) | None. | 1 |
| [Reddit](#reddit-ads) | None. | 3 |
| [TikTok](#tiktok-ads) | None. | 2 |
| [TikTok](#tiktok) | None. | 2 |
| [X](#x-ads) | None. | 2 |
| [X](#x) | None. | 2 |
| [YouTube](#youtube) | None. | 1 |

### Bing
Through [Bing Webmaster](/code/integrations/bing-webmaster).

| Our field | Bing Webmaster’s field |
| --- | --- |
| `Metric.breakdown` | `query or page` |

- **The key.** 8 to 128 characters of letters, digits and dashes, in a file on your Domain; a missing file turns pushes away with `needs-reauth`.

Responsive search ad

### Facebook
Through [Facebook](/code/integrations/facebook).

- **Your role on the page.** Posting needs a role that can post; a role removed in Meta’s settings ends the connection with `needs-reauth`.

Feed image ad, 1440 × 1800, 4:5

### Google Search and Google Display and YouTube
Through [Google Ads](/code/integrations/google-ads).

Google Search: Responsive search ad YouTube: Video

### Google Analytics
Through [Google Analytics](/code/integrations/google-analytics).

| Our field | Google Analytics’s field |
| --- | --- |
| `Metric.breakdown` | `dimensions` (sessionSource, sessionMedium, landingPage and the rest) |
| `Metric.metric` | `metrics` (sessions, activeUsers, keyEvents) |
| `Metric.window` | `dateRanges` |

- **Realtime.** Covers the last 30 minutes; anything older reads from the day’s reports.

### Google Business Profile
Through [Google Business Profile](/code/integrations/google-business-profile).

### Google Search Console
Through [Google Search Console](/code/integrations/google-search-console).

| Our field | Google Search Console’s field |
| --- | --- |
| `Metric.breakdown` | `dimensions` (query, page, country, device or date) |
| `Metric.window` | `startDate and endDate` |
| `Metric.metric` | `clicks, impressions, ctr or position` |

- **Search data.** Settles about 2 days behind, so a readout of yesterday reads the last settled day.
- **Request indexing.** Has no API: for a key page still out of the index after a week, the browser agent presses it in Search Console, signed in as you.

### Instagram
Through [Instagram](/code/integrations/instagram).

- **Account type.** A professional account; a personal account refuses with `account-not-connected` until it switches.

Feed image, 320 to 1,440 pixels wide, 4:5 to 1.91:1 Carousel Reel, 9:16 Story, 9:16

### LinkedIn
Through [LinkedIn Ads](/code/integrations/linkedin-ads).

- **Results.** Read through the reporting API each morning; a day’s numbers settle overnight, so a stop rule fires on yesterday’s.

Feed post Single image ad, 1200 × 628, 1200 × 1200 or 720 × 900 Document ad

### Facebook and Instagram
Through [Meta Ads](/code/integrations/meta-ads).

Facebook: Feed image ad, 1440 × 1800, 4:5 Instagram: Feed image, 320 to 1,440 pixels wide, 4:5 to 1.91:1 Instagram: Carousel Instagram: Reel, 9:16 Instagram: Story, 9:16

### Bing
Through [Microsoft Ads](/code/integrations/microsoft-ads).

Responsive search ad

### Reddit
Through [Reddit Ads](/code/integrations/reddit-ads).

- **Comments on a promoted post.** Stay open; each one reaches you as a Message, and your agent drafts the answer.

Text or link post Comment Promoted post

### TikTok
Through [TikTok Ads](/code/integrations/tiktok-ads).

Video Spark Ad

### TikTok
Through [TikTok](/code/integrations/tiktok).

Video Spark Ad

### X
Through [X Ads](/code/integrations/x-ads).

Standard post Promoted post

### X
Through [X account](/code/integrations/x).

- **Replies to strangers.** Only to posts that describe your problem, and never twice in one thread without an answer back.

Standard post Promoted post

### YouTube
Through [YouTube](/code/integrations/youtube).

- **Comments.** Read every 30 minutes, since YouTube sends no webhook for comments.

Video

## What try does
| Account | What try does |
| --- | --- |
| [Bing Webmaster](/code/integrations/bing-webmaster#try) | Returns the URLs IndexNow would receive and the key file it would check; nothing is pushed. |
| [Facebook](/code/integrations/facebook#try) | Returns the post as your page will show it, with the time it goes live; nothing is posted or scheduled. |
| [Google Ads](/code/integrations/google-ads#try) | Sends every change with `validate_only` set: Google Ads checks the whole build and returns any error, and creates nothing. |
| [Google Analytics](/code/integrations/google-analytics#try) | Reads run for real, because a report changes nothing; `try` stores no rows, builds no audience and marks no event. |
| [Google Business Profile](/code/integrations/google-business-profile#try) | Returns the update or the answer as it will show on your profile; nothing posts. |
| [Google Search Console](/code/integrations/google-search-console#try) | Reads run for real, because a read changes nothing in Search Console; `try` stores no rows and submits no sitemap. |
| [Instagram](/code/integrations/instagram#try) | Checks each image and video against Instagram’s specs and returns the post as it will show; no container is staged and nothing publishes. |
| [LinkedIn Ads](/code/integrations/linkedin-ads#try) | Reads your ad account, the matched count of each audience and the targeting facets, and checks the campaign against LinkedIn’s budget minimums; LinkedIn has no validate-only call, so nothing is sent to it and nothing is created. |
| [Meta Ads](/code/integrations/meta-ads#try) | Sends the whole build with Meta’s `validate_only` execution option: Meta runs every check and creates nothing, and the response names any field it would reject. |
| [Microsoft Ads](/code/integrations/microsoft-ads#try) | Returns what the import would bring over, term by term with the budget it would set, and the steps the browser agent would take; no session opens and nothing changes. |
| [Reddit Ads](/code/integrations/reddit-ads#try) | Returns the campaign and the promoted post as Ads Manager will hold them, with the steps the browser agent would take; no session opens and nothing is created. |
| [TikTok Ads](/code/integrations/tiktok-ads#try) | Returns the Spark Ad and its ad group as Ads Manager will hold them, checked against TikTok’s budget floors, with the steps the browser agent would take; nothing is created. |
| [TikTok](/code/integrations/tiktok#try) | Checks the clip and caption against what your account reports it allows, and returns the post as it will show; nothing reaches your inbox. |
| [X Ads](/code/integrations/x-ads#try) | Returns the promotion as X Ads will hold it, with the steps the browser agent would take; no session opens and nothing is promoted. |
| [X account](/code/integrations/x#try) | Renders each post at X’s weighted count, with links at 23 characters, and returns the price X would bill; nothing is posted and nothing is billed. |
| [YouTube](/code/integrations/youtube#try) | Checks the file, title, description and tags against YouTube’s limits and returns the video page as it will read; nothing uploads and no quota is spent. |

## Refuses when
| Check | Error | Fix | Then call |
| --- | --- | --- | --- |
| A revenue figure from any source but a live-mode Stripe query | [`stripe-revenue`](/code/refusals#check-stripe-revenue) | Query the revenue from live-mode Stripe, then compile the log. | [`Metric.query`](/code/apps/analytics-and-experiments/query) |

Every call through an account can also meet [the shared account refusals](/code/refusals#accounts): [`grant-missing`](/code/refusals#grant-missing), [`needs-reauth`](/code/refusals#needs-reauth), [`account-not-connected`](/code/refusals#account-not-connected), [`over-mandate`](/code/refusals#over-mandate), [`quota-reached`](/code/refusals#quota-reached), [`venue-rejected`](/code/refusals#venue-rejected).

```json
{
  "type": "https://startupbuilder.dev/errors/stripe-revenue",
  "title": "Revenue not from Stripe",
  "status": 422,
  "detail": "A revenue figure from any source but a live-mode Stripe query.",
  "fix": "Query the revenue from live-mode Stripe, then compile the log.",
  "next": {
    "verb": "Metric.query",
    "input": {}
  },
  "instance": "https://api.sb/leaseline.app/actions/action_1"
}
```

## When it waits
Returns in one response; nothing waits.

## Example

**Ask**

```text
Which of the three LinkedIn test ads brought a lead in its first week?
```

**API**

```bash
curl -X POST "https://api.sb/leaseline.app/metrics/query?mode=try" \
  -H "Authorization: Bearer $SB_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"metric":"new-leads","shape":"trend","breakdown":"ad","window":"7d"}'
```

The response is the one in the Run it window above.

**CLI**

```bash
npx startup-builder do Metric.query --try --data '{"metric":"new-leads","shape":"trend","breakdown":"ad","window":"7d"}'
```

**MCP**

```json
{
  "name": "do",
  "arguments": {
    "mode": "try",
    "code": "return await $.Metric.query({ metric: 'new-leads', shape: 'trend', breakdown: 'ad', window: '7d' })"
  }
}
```

**SDK**

```ts
import { $ } from 'startup-builder'

const result = await $.try.Metric.query({
  metric: 'new-leads',
  shape: 'trend',
  breakdown: 'ad',
  window: '7d',
})
```

To retry, send the same `Idempotency-Key`: the retry returns the first answer for $0. A new key reads again.

A `sk_test_` key answers these the same way every time, at $0 ([every test value](/code/data#test-values)).

| Test value | Always returns | What it is for |
| --- | --- | --- |
| `metric: 'test-flat'` | `{"rows":[{"value":7,"series":[1,1,1,1,1,1,1],"readable":true}]}` | One a day for seven days, every time, so a readout or a stop rule can be tested against a known line. |
| `maxCost: 0.0001` | Refuses with [`max-cost`](/code/refusals#max-cost) | Any priced call with a maxCost of 0.0001 is refused before it spends: the price is above the cap. |
| `rate-limited.test` | Refuses with [`rate-limited`](/code/refusals#rate-limited) | Any call naming this hostname answers 429 with Retry-After, so your retry logic runs. |
| `sources-busy.test` | Refuses with [`sources-busy`](/code/refusals#sources-busy) | Any call naming this hostname answers 503 as if every route were busy, and nothing is spent. |

## Used by
- **Workflows:** [Search readout](/capabilities/measure/search-readout) · [Weekly readout](/capabilities/measure/weekly-readout) · [Silence watch](/capabilities/keep-and-grow/silence-watch)
- **Agent tasks:** [Referral ask](/capabilities/keep-and-grow/referral-ask) · [Launch day](/capabilities/earn-first-links/launch-day)
- **Services:** [Demand generation](/capabilities/find-customers/demand-generation)
- **Jobs:** [Convert visitors](/capabilities/convert-visitors) · [Keep and grow customers](/capabilities/keep-and-grow) · [Measure](/capabilities/measure) · [Run ads](/capabilities/run-ads)
- **Integrations:** [Bing Webmaster](/code/integrations/bing-webmaster) · [Facebook](/code/integrations/facebook) · [Google Ads](/code/integrations/google-ads) · [Google Analytics](/code/integrations/google-analytics) · [Google Business Profile](/code/integrations/google-business-profile) · [Google Search Console](/code/integrations/google-search-console) · [Instagram](/code/integrations/instagram) · [LinkedIn Ads](/code/integrations/linkedin-ads) · [Meta Ads](/code/integrations/meta-ads) · [Microsoft Ads](/code/integrations/microsoft-ads) · [Reddit Ads](/code/integrations/reddit-ads) · [TikTok Ads](/code/integrations/tiktok-ads) · [TikTok](/code/integrations/tiktok) · [X Ads](/code/integrations/x-ads) · [X account](/code/integrations/x) · [YouTube](/code/integrations/youtube)

## Related
None.