---
title: What each provider supports
description: One table for every ad platform AdCrunch connects to — what it reads, what it can change, what you pass as the API value, and where each capability stops.
---

import Callout from '#docs/components/mdx/callout.astro';

AdCrunch supports its providers at different depths. This page is the one place that says how deep each one goes. Every other page links here instead of repeating a caveat.

## The table

| Provider | API value | Connect | Read entities | Read live [^8] | Read insights | Change what runs | Register creative |
| --- | --- | :-: | :-: | :-: | :-: | :-: | :-: |
| [Meta](/use/connect/meta) | `meta` | yes | yes | all but asset groups [^9] | yes | yes | yes |
| TikTok | `tiktok` | yes | yes | all but creatives [^10] | yes | no | yes |
| Google Ads | `gads` | yes | yes | all but creatives [^11] | partial [^1] | no | no [^2] |
| X | `x` | yes | no [^3] | no | no [^3] | no | no [^2] |
| OpenAI | `openai` | yes [^7] | no [^3] | no | no [^3] | no | no [^2] |
| Snapchat | `snapchat` | not yet [^6] | no | no | no | no | no |
| Display & Video 360 | `dv360` | not yet [^6] | no | no | no | no | no |

[^8]: Send `fresh=true` to an advertiser-scoped resource to read the provider itself rather than what AdCrunch holds — for example `GET /observe/{advertiserId}/campaigns?fresh=true`. It is the account scope only, because a credential belongs to one ad account. A provider or resource with no adapter answers `501 live_read_unsupported`, which is permanent: read the same resource without `fresh`.

[^9]: Campaigns, ad groups, ads and creatives read live on Meta. An asset group is Performance Max, so Google Ads alone has one and Meta answers `501` for it.

[^10]: Campaigns, ad groups and ads read live on TikTok. It has no standalone creative — the video, the ad text and the call to action live inside the ad — so `/observe/{advertiserId}/creatives` answers `501` there.

[^11]: Campaigns, ad groups, ads and asset groups read live on Google Ads. It keeps its creative material on the `ad_group_ad` link resource rather than in a creative of its own, so `/observe/{advertiserId}/creatives` answers `501` there. A live read of Google Ads carries no `pagination.total`: its query language reports no count unless one is asked for.

[^1]: Google Ads reports no reach, so `reach`, `cpp` and `frequency` read `0`. See [read insights](#read-insights).

[^2]: Meta and TikTok are the two providers with an adapter, and the console offers only their advertisers as a registration target. AdCrunch records a registration against any other provider as `unsupported` rather than failing it — for **every** kind of asset, images included. Google Ads would also need a second decision even with an adapter: its video assets are YouTube references and hold no bytes to place.

[^3]: X and OpenAI connect and list their ad accounts. Nothing below the account is imported, so `list_entities` and `query_insights` return nothing for `x` or `openai`, and the Performance provider filter does not offer them.

[^7]: OpenAI connects with a pasted API key — its Ads API has no OAuth. Each key is scoped to one ad account, so a connection is exactly that account.

[^6]: The console lists these two under **Not yet supported**. You cannot connect them today. They are in the `provider` enum because the data model already names them.

The **API value** column is the exact string you pass as `provider` — to [`query_insights`](/mcp/tools/query-insights), to [`list_entities`](/mcp/tools/list-entities), and to the REST API. `gads` is Google Ads. There is no `google`.

## Connect

Every ad account a connection exposes becomes an [advertiser](/use/concepts#advertiser). You do not pick them, and there is no read-only connection — what you control is which accounts you connect.

Meta has the deepest guide, and it is the one to read first even if you connect another provider: the flow is the same. For TikTok, Google Ads and X, go to **Integrations** in the console and press **Connect**.

OpenAI is the one provider with no OAuth. Press **Connect**, then paste an API key issued in the Settings tab of [OpenAI's Ads Manager](https://ads.openai.com). Each key is scoped to one ad account, so the connection is that account — connect each account with its own key.

Google Ads needs one thing the others do not: access through a manager (MCC) account. AdCrunch walks the whole account tree beneath every manager you can reach and keeps the leaf accounts as advertisers.

## Read entities

Three providers import the account structure — X and OpenAI connect but import nothing below the account. What differs is the shape of that structure, because AdCrunch keeps each provider's own words for it rather than inventing a common label.

| Level                | Meta       | TikTok     | Google Ads    |
| -------------------- | ---------- | ---------- | ------------- |
| Top container        | `campaign` | `campaign` | `campaign`    |
| Targeting and budget | `adset`    | `adgroup`  | `ad_group`    |
| Unit of delivery     | `ad`       | `ad`       | `ad_group_ad` |
| Creative payload     | `creative` | — [^4]     | — [^4]        |

[^4]: TikTok has no separate creative object — the video, the ad text and the call to action live inside the ad. Google Ads keeps its creative material on the `ad_group_ad` link resource.

Google Ads Performance Max campaigns break the pattern entirely: they have no ad groups and no ads. An `asset_group` hangs directly off the campaign, and Google's automation assembles the placements.

## Read insights

| Metric                            | Meta | TikTok | Google Ads |
| --------------------------------- | ---- | :----: | :--------: |
| `spend`, `impressions`, `clicks`  | yes  |  yes   |    yes     |
| `conversions`, `conversion_value` | yes  |  yes   |    yes     |
| `reach`, `frequency`, `cpp`       | yes  |  yes   |  no [^1]   |
| `action_*` funnel [^5]            | yes  |   no   |     no     |

[^5]: `action_add_to_cart`, `action_purchase`, `action_initiate_checkout` and their `_value` variants.

`ctr`, `cpc`, `cpm`, `cpa` and `roas` are computed from the metrics above, so they are available wherever their inputs are.

> **Do not compare a missing metric across two providers**
>
> A metric a provider does not report reads `0`, not `null`. A Google Ads
> campaign with a `reach` of `0` did reach people — Google did not say how many.
> Compare `spend`, `impressions`, `clicks` and conversions across providers.
> Compare the rest inside one provider only.

Money arrives in each advertiser's own account currency. Ask for a `currency` and AdCrunch converts every row at the European Central Bank rate for that row's own date before it adds them up. See [concepts](/use/concepts#account-currency-and-display-currency).

## Change what runs

**Meta only.** [`meta_create_campaign`](/mcp/tools/meta-create-campaign), [`meta_create_adset`](/mcp/tools/meta-create-adset), [`meta_create_creative`](/mcp/tools/meta-create-creative), [`meta_create_ad`](/mcp/tools/meta-create-ad), [`meta_update_budget`](/mcp/tools/meta-update-budget) and [`meta_set_status`](/mcp/tools/meta-set-status) are the whole write surface, and their names say so.

Two rules hold for every write, on every provider AdCrunch ever adds:

- Nothing an agent creates starts spending. A create always arrives paused.
- The ad account is derived from the advertiser you named, never from caller input.

[Auth & scopes](/mcp/auth) sets out the full boundary.

## Register creative

Registering places an [asset's](/use/concepts#asset) bytes into one advertiser's provider-side library. Meta and TikTok each have an adapter, and they work differently: Meta takes the bytes — multipart for an image, a chunked protocol for a video — while TikTok is handed a URL and fetches the file itself.

A provider with no adapter is not an error. Google Ads, X, OpenAI and Snapchat have none, so the console does not offer their advertisers as a target, and a registration created against one through the API is recorded as `unsupported` — for every kind of asset. The same outcome applies where an adapter exists but cannot accept that kind of file.

> **A Meta video is not ready when it is registered**
>
> Meta returns a video id while the file is still processing, and documents no
> duration for that phase. So an identifier is not a finished registration:
> [`asset_get`](/mcp/tools/asset-get) reports whether the provider can use it
> yet. Images come back ready; videos do not.

## Planning is not buying

A [Campaign Plan](/mcp/campaign-plans) names **channels**, and a channel is not a provider. Four Google channels sit behind one Google Ads connection, and you can plan a channel AdCrunch has no integration for at all.

| Channel | Sold by | Executable today |
| --- | --- | :-: |
| `meta` | `meta` | yes |
| `tiktok` | `tiktok` | no |
| `snapchat` | `snapchat` | no |
| `google_search`, `google_pmax`, `google_display`, `youtube` | `gads` | no |
| `programmatic_display` | `dv360` | no |
| `x` | `x` | no |
| `linkedin` | — | no |

A line item on a channel AdCrunch cannot execute is still worth writing. It is authorable, it sums into the plan, and it becomes executable the day that integration ships — without the plan changing.

## What's next

- [Connect Meta](/use/connect/meta) — the deepest provider integration.
- [Concepts](/use/concepts) — the words this table uses.
- [Auth & scopes](/mcp/auth) — what an agent can and cannot change.
