# Acquisition

Source: https://docs.mirafive.io/rest-api/acquisition

> Read where a MIRA FIVE project's people come from by channel, and its search engine and ad platform summaries.

The acquisition endpoints say where people come from: visits and people per channel, how the site does in Google and Bing search, and what ad spend brought. Search and ads read the Google Search Console, Bing Webmaster Tools, Google Ads and Meta Ads imports connected in MIRA FIVE; without them their numbers are `null` and `provider.state` says why.

## List channels

```text
GET /api/v1/projects/{project_id}/acquisition/channels
```

Visits per channel, and the people credited to each. Each person is credited to one channel, so people add up across channels. The list does not page.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `touch` | string | `first` | `first` credits the visit that first brought a person; `last` their latest visit from outside (not `direct`), else their latest visit. |
| `period` | string | `30d` | `1h`, `24h`, `7d`, `30d`, `90d`, `12m` or `custom`. See [Periods](https://docs.mirafive.io/rest-api#periods). |
| `from` | string | none | First day of a custom period, `YYYY-MM-DD`. **Required** with `period=custom`. |
| `to` | string | none | Last day of a custom period, inclusive. **Required** with `period=custom`. |
| `filters[i][field]`, `filters[i][op]`, `filters[i][value]` | string | none | Event filters, all of which must hold. See [Filters](https://docs.mirafive.io/rest-api#filters). |

`compare` is refused.

```bash
curl -G https://app.mirafive.io/api/v1/projects/01932c4e-8a7b-7c3d-9e2f-4b5a6c7d8e9f/acquisition/channels \
  -H "Authorization: Bearer $MIRAFIVE_API_KEY" \
  --data-urlencode 'period=30d' \
  --data-urlencode 'touch=first'
```

```json title="200 OK"
{
  "data": [
    {
      "id": "organic_search",
      "type": "channels",
      "attributes": {
        "visits": 11820,
        "people": 6204,
        "newPeople": 4410,
        "buyers": 212,
        "customers": 498,
        "conversion": 0.0803
      }
    },
    {
      "id": "paid_social",
      "type": "channels",
      "attributes": {
        "visits": 5310,
        "people": 3102,
        "newPeople": 2870,
        "buyers": 96,
        "customers": 141,
        "conversion": 0.0455
      }
    },
    {
      "id": "email",
      "type": "channels",
      "attributes": {
        "visits": 2044,
        "people": 611,
        "newPeople": 38,
        "buyers": 81,
        "customers": 263,
        "conversion": 0.4304
      }
    },
    {
      "id": "none",
      "type": "channels",
      "attributes": {
        "visits": 0,
        "people": 27,
        "newPeople": 27,
        "buyers": 27,
        "customers": 27,
        "conversion": 1
      }
    }
  ],
  "meta": {
    "period": {
      "preset": "30d",
      "from": "2026-08-29T00:00:00+02:00",
      "to": "2026-09-28T00:00:00+02:00",
      "timezone": "Europe/Berlin",
      "interval": "day",
      "comparison": null,
      "clampedToRetention": false
    },
    "touch": "first",
    "visits": 19174,
    "people": {
      "people": 9944,
      "newPeople": 7345,
      "buyers": 416,
      "customers": 929,
      "conversion": 0.0934
    },
    "coverage": 0.62
  }
}
```

| Field | Type | Description |
| --- | --- | --- |
| `id` | string | The channel: `paid_search`, `paid_social`, `email`, `ai_assistant`, `organic_search`, `organic_social`, `campaign`, `referral` or `direct`. `none` holds people only a server has seen, who have no visit. |
| `attributes.visits` | integer | Visits that came in through the channel. |
| `attributes.people` | integer | People credited to the channel by `touch`. |
| `attributes.newPeople` | integer | Of them, people first seen in the period. |
| `attributes.buyers` | integer | Of them, people who bought in the period. |
| `attributes.customers` | integer | Of them, people who had bought by the end of the period. |
| `attributes.conversion` | number or null | `customers` over `people`, 0–1. `null` when `people` is 0. |
| `meta.touch` | string | `first` or `last`. |
| `meta.visits` | integer | Visits over all channels. |
| `meta.people` | object | `people`, `newPeople`, `buyers`, `customers` and `conversion` over all channels. |
| `meta.coverage` | number or null | Share (0–1) of pageviews collected with consent. People counts rest on these; visits count without consent too. |

## Get the search summary

```text
GET /api/v1/projects/{project_id}/acquisition/search
```

One search engine's clicks, impressions, click-through rate and average position over the period, against the period before: Google from Google Search Console (the default), or Bing from Bing Webmaster Tools with `engine=bing`. Each engine counts its own way, so do not add their numbers together. Both publish about two days late, so `through` names the last day there is.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `period` | string | `30d` | `1h`, `24h`, `7d`, `30d`, `90d`, `12m` or `custom`. See [Periods](https://docs.mirafive.io/rest-api#periods). |
| `from` | string | none | First day of a custom period, `YYYY-MM-DD`. **Required** with `period=custom`. |
| `to` | string | none | Last day of a custom period, inclusive. **Required** with `period=custom`. |
| `compare` | string | none | `previous`. Accepted; the summary always carries the previous values. |
| `engine` | string | `google` | `google` or `bing`. Anything else is refused with `422` and `The engine must be one of: google, bing.` |

Event filters do not apply to the engines' numbers and are not read.

With `engine=bing`, clicks and impressions count every Bing surface, Copilot included. Bing's daily numbers carry no position, so `position` comes from its pages, which Bing reports a week at a time: a week counts in the period that holds its last day, once that day is published, and `position` is `0` while no such week exists.

```bash
curl -G https://app.mirafive.io/api/v1/projects/01932c4e-8a7b-7c3d-9e2f-4b5a6c7d8e9f/acquisition/search \
  -H "Authorization: Bearer $MIRAFIVE_API_KEY" \
  --data-urlencode 'period=7d'
```

```json title="200 OK"
{
  "data": {
    "id": "01932c4e-8a7b-7c3d-9e2f-4b5a6c7d8e9f",
    "type": "search-summaries",
    "attributes": {
      "engine": "google",
      "provider": {
        "source": "google_search_console",
        "state": "ready",
        "reconnect": false,
        "accounts": ["sc-domain:nordlicht.example"],
        "lastImportedAt": "2026-09-27T06:12:48.201+02:00"
      },
      "through": "2026-09-25",
      "clicks": { "value": 2140, "previous": 1987 },
      "impressions": { "value": 61830, "previous": 58412 },
      "ctr": { "value": 0.0346, "previous": 0.034 },
      "position": { "value": 14.2, "previous": 15.1 }
    }
  },
  "meta": {
    "period": {
      "preset": "7d",
      "from": "2026-09-21T00:00:00+02:00",
      "to": "2026-09-28T00:00:00+02:00",
      "timezone": "Europe/Berlin",
      "interval": "day",
      "comparison": {
        "from": "2026-09-14T00:00:00+02:00",
        "to": "2026-09-21T00:00:00+02:00"
      },
      "clampedToRetention": false
    }
  }
}
```

| Field | Type | Description |
| --- | --- | --- |
| `id` | string | The project's id. |
| `attributes.engine` | string | The engine read: `google` or `bing`. |
| `attributes.provider.source` | string | `google_search_console` or `bing_webmaster`. |
| `attributes.provider.state` | string | `ready` (imported); `importing` (connected, first import not finished); `not_connected`; `not_available` (this installation cannot connect it); `dataset_off` (the dataset is switched off); `refused` (the grant was refused before the first import). |
| `attributes.provider.reconnect` | boolean | The grant was lost after importing: the numbers stay readable but stop updating until it is reconnected in MIRA FIVE. |
| `attributes.provider.accounts` | array | The connected Search Console properties or Bing sites. |
| `attributes.provider.lastImportedAt` | string or null | When the last import finished. |
| `attributes.through` | string or null | The last day the engine has published, `YYYY-MM-DD`. |
| `attributes.clicks`, `attributes.impressions` | object or null | `value` for the period, `previous` for the period before (`null` when outside the history). `null` until an import is ready. |
| `attributes.ctr` | object or null | Click-through rate, 0–1, shaped the same. |
| `attributes.position` | object or null | Average position, 1 is the top, shaped the same. |
| `meta.period` | object | The window read and the one compared with. |

## Get the ads summary

```text
GET /api/v1/projects/{project_id}/acquisition/ads
```

Google Ads and Meta Ads over the period: spend, clicks and impressions from the imports, and the buyers and return on ad spend MIRA FIVE counted from paid visits. A purchase is credited to the latest paid click before it (last touch).

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `period` | string | `30d` | `1h`, `24h`, `7d`, `30d`, `90d`, `12m` or `custom`. See [Periods](https://docs.mirafive.io/rest-api#periods). |
| `from` | string | none | First day of a custom period, `YYYY-MM-DD`. **Required** with `period=custom`. |
| `to` | string | none | Last day of a custom period, inclusive. **Required** with `period=custom`. |

`compare` is refused. Filters are not read.

```bash
curl -G https://app.mirafive.io/api/v1/projects/01932c4e-8a7b-7c3d-9e2f-4b5a6c7d8e9f/acquisition/ads \
  -H "Authorization: Bearer $MIRAFIVE_API_KEY" \
  --data-urlencode 'period=30d'
```

```json title="200 OK"
{
  "data": {
    "id": "01932c4e-8a7b-7c3d-9e2f-4b5a6c7d8e9f",
    "type": "ads-summaries",
    "attributes": {
      "providers": [
        {
          "source": "google_ads",
          "state": "ready",
          "reconnect": false,
          "accounts": ["Nordlicht · Google Ads"],
          "lastImportedAt": "2026-09-27T05:40:17.664+02:00"
        },
        {
          "source": "meta_ads",
          "state": "ready",
          "reconnect": false,
          "accounts": ["Nordlicht · Meta"],
          "lastImportedAt": "2026-09-27T05:41:02.090+02:00"
        }
      ],
      "spend": [{ "currency": "EUR", "amount": 4820.35 }],
      "clicks": 6912,
      "impressions": 412870,
      "ctr": 0.0167,
      "roas": [{ "currency": "EUR", "value": 3.42 }],
      "buyers": 118,
      "revenueTracked": true
    }
  },
  "meta": {
    "period": {
      "preset": "30d",
      "from": "2026-08-29T00:00:00+02:00",
      "to": "2026-09-28T00:00:00+02:00",
      "timezone": "Europe/Berlin",
      "interval": "day",
      "comparison": null,
      "clampedToRetention": false
    }
  }
}
```

| Field | Type | Description |
| --- | --- | --- |
| `id` | string | The project's id. |
| `attributes.providers` | array | One entry per ad platform, shaped like the search summary's `provider`. `source` is `google_ads` or `meta_ads`. |
| `attributes.spend` | array or null | Spend per currency, `{ currency, amount }`, in each ad account's currency and never added across currencies. |
| `attributes.clicks`, `attributes.impressions` | integer or null | The platforms' own totals. |
| `attributes.ctr` | number or null | `clicks` over `impressions`, 0–1. |
| `attributes.roas` | array or null | Return on ad spend per currency, `{ currency, value }`: revenue from paid visits over spend. |
| `attributes.buyers` | integer or null | People who bought after a paid click. |
| `attributes.revenueTracked` | boolean or null | `false` when the site sends no revenue, so `roas` cannot exist. |
| `meta.period` | object | The window read. |

Every number is `null` until at least one ad platform has an import ready.
