# How much each call costs

Every time a Met works —answers a WhatsApp message, runs a Run you started through the API, executes an automation— Energy is debited from your balance. This guide shows how to find out how much **each call** cost and how to add it up per **Run**, per **Met** or per **WhatsApp line**.

Everything in this guide is read-only and needs the `billing:read` scope (a Run's cost, `runs:read`). Amounts are the Energy debited to you, in USD: the same figure you see in the dashboard.

## Each execution, with its origin

`GET /billing/executions` lists your workspace's executions, newest first. Each one says how much it cost and where it came from:

| Field | What it says |
|---|---|
| `cost_final_usd` | The Energy debited for this execution, in USD. |
| `met_id`, `met_name` | The Met that did the work. |
| `model_display` | The intelligence level it used (`Ágil`, `Brillante`, `Genio`…). |
| `run_id` | The API Run that caused it (`run_…`), or `null` if it did not come from a Run. |
| `channel` | Where the work came in: `whatsapp`, `instagram`, `messenger`, `widget`, `panel`, `api`… |
| `whatsapp_line` | The WhatsApp line: `{ id, number_masked }`, or `null` (see [what carries a line](#what-carries-a-line-and-since-when)). |

`whatsapp_line.id` is Meta's `phone_number_id`, the same value you use in `sender_line_id` and that `GET /channels/{channelId}/whatsapp/lines` returns. The number is masked (`+57 300 ••• 4567`): enough to recognize the line without copying the full number into every row.

```ts
import Met from '@meteor.ia/sdk';

const met = new Met(process.env.MET_API_KEY, { workspaceId: 7 });

const page = await met.billing.executions({ limit: 20 });
for (const e of page.rows) {
  console.log(e.executed_at, e.met_name, e.channel, e.whatsapp_line?.number_masked ?? '-', e.cost_final_usd);
}
console.log('Filter total:', page.totals?.cost_final_usd);
```
```python
import os
from meteor_ia import Met

met = Met(os.environ["MET_API_KEY"], workspace_id=7)

page = met.billing.executions(limit=20)
for e in page["rows"]:
    line = (e.get("whatsapp_line") or {}).get("number_masked", "-")
    print(e["executed_at"], e["met_name"], e["channel"], line, e["cost_final_usd"])
print("Filter total:", (page.get("totals") or {}).get("cost_final_usd"))
```
```bash
curl "https://api.met.meteor.com.co/api/v1/billing/executions?limit=20" \
  -H "Authorization: Bearer $MET_API_KEY"
```

The response is a page `{ rows, total, limit, offset, totals }`. The maximum is **100 per page**: a larger `limit` is capped at 100 without an error, and the response's `limit` is the one applied. `totals` adds up **all** executions matching the filter, not just the ones on the page.

What never comes out: the provider's cost, the margin or the model name. Neither do the contact or the conversation: to see spend per contact, use `GET /billing/consumption?by=contact`.

## Filter by Met, line or Run

Three filters answer "how much did this cost me?":

| Filter | Example | What it returns |
|---|---|---|
| `met_id` | `met_id=41207` | Only what that Met did. |
| `line_id` | `line_id=1210946622112871` | Only what came in through that WhatsApp line. |
| `run_id` | `run_id=run_01JB8ZQ4…` | Only what that Run caused. |

They combine with each other and with `from`, `to`, `type`, `costMin` and `costMax`. `totals` honors the same filters, so a single call gives you the total:

```ts
// How much did the sales line cost in October?
const october = await met.billing.executions({
  line_id: '1210946622112871',
  from: '2026-10-01T00:00:00Z',
  to: '2026-10-31T23:59:59Z',
  limit: 1,
});
console.log(october.total, 'executions ·', october.totals?.cost_final_usd, 'USD');
```
```python
october = met.billing.executions(
    line_id="1210946622112871",
    from_="2026-10-01T00:00:00Z",
    to="2026-10-31T23:59:59Z",
    limit=1,
)
print(october["total"], "executions ·", october["totals"]["cost_final_usd"], "USD")
```
```bash
curl "https://api.met.meteor.com.co/api/v1/billing/executions?line_id=1210946622112871&from=2026-10-01T00:00:00Z&to=2026-10-31T23:59:59Z&limit=1" \
  -H "Authorization: Bearer $MET_API_KEY"
```

A malformed filter returns **400**, with the parameter in `param` (`invalid_met_id`, `invalid_line_id`, `invalid_run_id`): it is never silently ignored, because returning every execution would make you believe that line spent the whole account's Energy. `line_id` is the `phone_number_id` (digits only), not the phone number.

To walk every execution of a filter without handling `offset`:

```ts
let sum = 0;
for await (const e of met.billing.iterateExecutions({ met_id: 41207, from: '2026-10-01T00:00:00Z' })) {
  sum += e.cost_final_usd;
}
```
```python
total = sum(e["cost_final_usd"] for e in met.billing.iterate_executions(met_id=41207, from_="2026-10-01T00:00:00Z"))
```

## What a Run cost

`GET /runs/{id}` includes `cost_final_usd`: what **that** Run cost, adding up the Energy of every execution it caused (the same ones as `GET /billing/executions?run_id=…`).

```ts
const run = await met.runs.create({ input: 'Summarize today\'s leads' });
const detail = await met.runs.retrieve(run.id);
console.log(detail.status, detail.cost_final_usd);
```
```python
run = met.runs.create("Summarize today's leads")
detail = met.runs.retrieve(run["id"])
print(detail["status"], detail.get("cost_final_usd"))
```
```bash
curl https://api.met.meteor.com.co/api/v1/runs/run_01JB8ZQ4W8X3K7M2N5P6R9T0VA \
  -H "Authorization: Bearer $MET_API_KEY"
```

Three things to read it right:

- **Only in the detail.** The `POST /runs` response and the list don't include it: the cost settles after the Run answers.
- **It can grow for a few minutes.** Whatever the Run left running when it answered (a summary, an evaluation) is charged a little later and also belongs to it. Ten minutes after `completed_at` it is closed.
- **`null` is not zero.** It is `null` while the Run is still running, and when there are no executions recorded with its id: a Run from before version 3.51.0 of the contract or one that failed before reaching the Met. A test Run (`met_test_`) with executions gives `0`.

`energy`, already in the detail, adds up the Run's **conversation**: if you reused `conversation_id`, it includes other Runs. For the cost of a single one, use `cost_final_usd`.

## Consumption grouped by line

`GET /billing/consumption?by=line` splits a period's Energy by WhatsApp line, from the one that spent the most to the least:

```ts
const byLine = await met.billing.consumption({ by: 'line', from: '2026-10-01T00:00:00Z' });
for (const l of byLine) {
  console.log(l.whatsapp_line?.number_masked, l.executions, l.energia_usd);
}
```
```python
by_line = met.billing.consumption(by="line", **{"from": "2026-10-01T00:00:00Z"})
for l in by_line:
    print(l["whatsapp_line"]["number_masked"], l["executions"], l["energia_usd"])
```
```bash
curl "https://api.met.meteor.com.co/api/v1/billing/consumption?by=line&from=2026-10-01T00:00:00Z" \
  -H "Authorization: Bearer $MET_API_KEY"
```

Each row has `energia_usd`, `executions`, `time_saved_minutes` and `whatsapp_line`. Use `whatsapp_line.id` to drill down with `line_id`; `dim_key` is the line's internal id in Met (`null` if the line is no longer connected to your account). If a line was disconnected, its consumption still shows up, with `number_masked` as `null`.

The same endpoint groups by `met` (default), `contact`, `actor` (team member) and `conversation`.

## What carries a line, and since when

Met records each execution's Run and line **since version 3.51.0 of the contract**. Older data is not rebuilt: those executions have `run_id` and `whatsapp_line` as `null` and don't count in `line_id`, `run_id` or `by=line`. In old executions, `channel` may say `whatsapp` for any channel. The cost (`cost_final_usd`) is complete from the start.

These carry a line:

- the Met's answer to a message that came in through WhatsApp, with everything it did to answer it;
- the follow-ups and synthetic events of that conversation;
- flows started by a WhatsApp message (their Met nodes);
- the transcription of voice notes that arrive through WhatsApp.

These don't carry a line, even when the spend is WhatsApp's:

- manually re-invoking the Met on a conversation;
- summaries that run outside an answer;
- background jobs;
- everything before version 3.51.0.

That is why `whatsapp_line` can be `null` on WhatsApp spend, and the `by=line` total can be lower than what the whole channel spent. For the channel's total, add up `cost_final_usd` of the executions whose `channel` is `whatsapp`.
