> ## Documentation Index
> Fetch the complete documentation index at: https://docs.apostra.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Per-ad delivery on Meta

> Read how each ad in a Meta campaign is doing, day by day: impressions, reach, frequency, clicks, spend and conversions, with the Apostra creative and Creative Session behind each ad.

Per-ad delivery shows how every ad in one Meta campaign performed over a date
range, one row per ad per day. Use it to see which ads are winning and which
are tiring, for example when frequency climbs while click-through falls. Each
ad names the Apostra creative it runs and, when a Creative Session made it, the
session and the variant you picked, so you can tell which concept worked.

It reads the campaign from Meta on every call, through your connected Meta ad
account. It covers Meta campaigns you bought through Apostra and Meta campaigns
you connected from your own ad account.

## Read per-ad delivery

Call `get_delivery` with `report: "live_ad_delivery"`, one campaign and a date
range of at most 90 days:

```json theme={null}
{
  "report": "live_ad_delivery",
  "filters": { "campaignId": "cmp_123" },
  "range": { "startDate": "2026-11-01", "endDate": "2026-11-07" }
}
```

The dates are calendar days in the Meta ad account's timezone, which the
response names. A lifetime range, metric selection, grouping dimensions and
other filters are refused: this report always returns every figure below for
every ad.

## What comes back

The response follows AdCP's delivery shape. Ads sit inside their ad sets, the
way AdCP nests `by_creative` inside `by_package`:

```json theme={null}
{
  "report": "live_ad_delivery",
  "campaignId": "cmp_123",
  "currency": "USD",
  "reporting_period": {
    "start": "2026-11-01T04:00:00.000Z",
    "end": "2026-11-08T05:00:00.000Z",
    "timezone": "America/New_York"
  },
  "by_package": [
    {
      "package_id": "120210000000000001",
      "pricing_model": "cpm",
      "rate": 10.28,
      "currency": "USD",
      "impressions": 2400,
      "spend": 24.68,
      "clicks": 36,
      "ctr": 0.015,
      "conversions": 6,
      "by_event_type": [{ "event_type": "purchase", "count": 6, "value": 149.94 }],
      "by_creative_truncated": false,
      "by_creative": [
        {
          "creative_id": "cr_abc",
          "platform_ad_id": "120210000000000011",
          "platform_ad_name": "Gifting, warm table",
          "platform_creative_id": "120210000000000021",
          "creative_session_id": "cs_789",
          "creative_session_variant_id": "variant_2",
          "impressions": 1200,
          "spend": 12.34,
          "clicks": 18,
          "ctr": 0.015,
          "reach": 900,
          "reach_unit": "accounts",
          "reach_window": { "kind": "period", "period": { "interval": 7, "unit": "days" } },
          "frequency": 1.33,
          "conversions": 3,
          "by_event_type": [{ "event_type": "purchase", "count": 3, "value": 74.97 }],
          "daily_breakdown": [
            {
              "date": "2026-11-01",
              "impressions": 400,
              "spend": 4.11,
              "clicks": 6,
              "ctr": 0.015,
              "reach": 350,
              "reach_unit": "accounts",
              "reach_window": { "kind": "period", "period": { "interval": 1, "unit": "days" } },
              "frequency": 1.14,
              "conversions": 1,
              "by_event_type": [{ "event_type": "purchase", "count": 1, "value": 24.99 }]
            }
          ]
        }
      ]
    }
  ],
  "page": { "limit": 100, "returned": 2, "total": 2, "truncated": false, "consistency": "live_per_call" },
  "authority": "live_provider"
}
```

### Each ad

| Field | Meaning |
| - | - |
| `creative_id` | The Apostra creative the ad runs. `null` when Apostra did not make the ad, for example an ad created in Meta's own tools. |
| `platform_ad_id`, `platform_ad_name` | Meta's id and name for the ad. |
| `platform_creative_id` | Meta's id for the creative the ad runs. |
| `creative_session_id`, `creative_session_variant_id` | The Creative Session, and the variant in it, that made the Apostra creative. `null` when no Creative Session made it. |
| `impressions`, `spend`, `clicks`, `ctr` | Totals over the whole range. Clicks are clicks that leave Meta for your site, the same clicks `live_campaign_delivery` reports. `ctr` is clicks divided by impressions. |
| `reach`, `frequency` | Unique accounts reached over the whole range, and the average number of times each saw the ad. |
| `conversions`, `by_event_type` | The conversion event the ad's ad set is optimized for, such as purchases, with its value when Meta reports one. |
| `daily_breakdown` | One row per day the ad delivered, with the same figures for that day. |

### Each ad set

Each `by_package` entry is a Meta ad set. Its figures are the sums of its ads.
It has no reach or frequency, because those do not add up across ads.

## Things to know

* **Days with no delivery are missing, not zero.** Meta returns no row for a
  day an ad did not deliver. The ad's totals already count those days as zero.
* **Reach does not add up.** Never add daily reach to get the reach for the
  range: the same person can be counted on several days. Use the ad's own
  `reach`, which counts each person once over the range.
* **Totals and reach come from two reads.** An ad's totals are the sums of its
  days. Its reach and frequency for the range come from a second Meta read made
  in the same call, so on a campaign that is delivering they can be a few
  impressions ahead or behind.
* **Conversions are the ad set's optimization event only.** An ad set that is
  not optimized for a conversion event has no `conversions`. Meta can still
  revise conversions within its attribution window, so a later read can differ.
* **Creative Session names.** When a Creative Session finishes a creative,
  Apostra records the session and variant on the creative, so its ads name them
  for as long as they run. A creative finished before Apostra kept that record
  (November 2026) names them only while its session is still kept, which is 7
  days after the session was created.
* **Meta only.** A campaign on any other platform is refused with
  `VALIDATION_ERROR`.
* **Live, not billing.** These are Meta's figures at the time of the call. They
  are not Apostra's reporting figures and are not used for billing.

## Paging

Ad sets come in `package_id` order and their ads in `platform_ad_id` order. A
page holds whole ads, up to `limit` ads (100 by default) and as many as fit in
one response. When more ads remain, `page.truncated` is `true` and
`page.nextCursor` is set: call again with the same request and
`cursor` set to that value. An ad set split across pages appears on each page
with the same totals and `by_creative_truncated: true`. Each page reads Meta
again.

## Errors

| Code | When |
| - | - |
| `VALIDATION_ERROR` | No `campaignId`, a lifetime range or one longer than 90 days, metrics, dimensions or other filters sent, or a campaign that is not on Meta. |
| `NOT_FOUND` | The campaign does not exist in your account. |
| `INVALID_CURSOR` | The cursor came from a different request, or was changed. Send it back exactly as you received it, with the same campaign, dates and limit, or start again without it. |
| `SERVICE_UNAVAILABLE` | Meta did not answer, or the Meta connection needs attention. Retry shortly; if it keeps failing, check the connection in Settings. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.