---
title: meta_list_campaigns
description: Read the campaigns of one Meta ad account from Meta now, with the status and the budget of each.
---

Ask what runs on one Meta ad account at this moment. The agent asks Meta, and not the copy that AdCrunch stores. So you see a change that someone made in Meta Ads Manager a minute ago.

> Which campaigns run on Northwind now, and what is the daily budget of each?

The agent calls `meta_list_campaigns` with the Northwind advertiser. You see each campaign with its status and its budget, in the currency of the ad account. [Read your accounts](/mcp/tools/read-your-accounts) shows the full job.

## Reference

**Available on:** [![Meta](/providers/meta.svg)](https://docs.adcrunch.dev/connect/providers)

Meta: list the campaigns of one ad account, as Meta holds them now. Each call asks Meta now, and it counts against the rate limits of Meta. AdCrunch stores nothing from this read. To read one, send its id in `ids`. `budget` is in whole units of `currency`: 50 is 50.00. `budgetLevel` tells whether the campaign or the ad set carries the budget. `createdAt`, `updatedAt` and `deletedAt` are null on each row, because AdCrunch stored nothing. For a read across ad accounts, or of what AdCrunch stores, call `list_campaigns`. The answer holds at most `limit` rows (default 100, maximum 500). When it carries `nextCursor`, more rows exist: call this tool again with the same arguments and `cursor` set to that value. When it carries no `nextCursor`, you have every row.

### Input

| Argument | Type | Required | Description |
| --- | --- | --- | --- |
| `advertiserId` | string | yes | The Meta ad account to read, prefixed `acc_`. It must belong to the active organization. Find it with `list_advertisers`. |
| `ids` | array of (string, at least 1 character), at least 1 item | no | Read only the campaigns with these ids: the value that each row answers as `id`. To read one, send one id. |
| `status` | one of `ACTIVE`, `PAUSED`, `DELETED`, `ARCHIVED` | no | Read only the entities that are set to this status, not the entities that deliver. Each row of the answer carries this status. Meta takes no status yet: a call with `status` fails with `live_read_unsupported`. In its place, call `list_campaigns` with `advertiserId` and `status` to read what AdCrunch stores. |
| `cursor` | string, at least 1 character | no | The `nextCursor` of the previous page. Omit it to get the first page. Send it with no change, and with the same filters as the request that answered it: a cursor from a different query gets a 400 `invalid_cursor`. Do not build or change a cursor. |
| `limit` | integer, 1 to 500 | no | The greatest number of rows on the page, from 1 to 500. The default is 100. A greater value gets a 400, with `error` of `invalid_request`. Default: `100`. |

### Output

A successful call returns this object in `structuredContent`.

| Field | Type | Always present | Description |
| --- | --- | --- | --- |
| `campaigns` | array of object | yes | The campaigns of the ad account, as Meta holds them now, in the order of Meta. An empty array can carry `nextCursor`: then more rows can exist. |
| `campaigns[].advertiserId` | string | yes | The advertiser that owns it, prefixed `acc_`. |
| `campaigns[].createdAt` | number or null | yes | When AdCrunch first stored this row. **Null when the row was read live**: AdCrunch holds no copy of it. `createdAt`, `updatedAt` and `deletedAt` are AdCrunch’s own times, so all three are null together on a live row. |
| `campaigns[].createdTime` | number or null | yes | When the provider created the entity. This is the provider’s own time, so a live row carries it. Null where the provider reports none. |
| `campaigns[].currency` | string or null | yes | The account currency, ISO 4217. Every row of one advertiser carries the same one. Null when AdCrunch does not know it yet. |
| `campaigns[].deletedAt` | number or null | yes | When AdCrunch marked the row deleted. A listing never carries a deleted row, so this is null. |
| `campaigns[].id` | string | yes | The id the provider gives, with no prefix. It is unique for one provider and one type, and it may legitimately recur across two types or two providers. |
| `campaigns[].name` | string | yes | The name at the provider. |
| `campaigns[].path` | string | yes | The ancestors and this entity, ids joined by `/`, oldest first. This is what makes a subtree one string comparison. |
| `campaigns[].provider` | string | yes | The ad platform: `meta`, `tiktok` or `gads`. Those three are the providers whose entities AdCrunch reads, and https://docs.adcrunch.dev/connect/providers says how deep each one goes. |
| `campaigns[].status` | one of `ACTIVE`, `PAUSED`, `DELETED`, `ARCHIVED` | yes | The status, normalized across the providers. TikTok `ENABLE` and `DISABLE` read here as `ACTIVE` and `PAUSED`. |
| `campaigns[].updatedAt` | number or null | yes | When AdCrunch last rewrote this row. Null if it never changed. |
| `campaigns[].updatedTime` | number or null | yes | When the provider last edited the entity. Null where the provider reports none. |
| `campaigns[].budget` | number or null | yes | The budget this entity carries itself, in **whole units** of `currency`: `50` is 50.00, never 50 cents. It is the unit of `spend` on an Insight, so a budget and a spend compare directly. AdCrunch converts each provider’s own unit — Meta’s minor unit, TikTok’s whole units and Google Ads’ micros — so every provider answers the same unit here. Null when the entity carries no budget of its own: `budgetLevel` then says where its budget lives. A campaign answers a budget only when its `budgetLevel` is `campaign`, so a budget always paces delivery at the level that answers it. Null also when AdCrunch does not know the account currency yet, because nobody can read a budget with no currency. At Google Ads one budget may be shared by several campaigns, and each of them answers the whole shared amount. **A stored budget can be older than the rest of the row.** Meta does not mark a campaign changed when only its budget changes, and AdCrunch does not yet follow a Google Ads budget change, so the ingestion can miss a budget edited in the provider’s own interface. A live read answers the budget the provider holds now. AdCrunch takes a budget in the same whole units when it writes one, so a budget you read here goes back with no conversion. |
| `campaigns[].budgetLevel` | one of `campaign`, `ad_group` or null | yes | Where the budget of this entity’s campaign lives. `campaign` when the campaign carries it — Meta Advantage campaign budget, TikTok Campaign Budget Optimization, and every Google Ads campaign. `ad_group` when each ad group carries its own. Null on a TikTok campaign whose payload does not say. |
| `campaigns[].budgetType` | one of `daily`, `lifetime` or null | yes | `daily` for a budget spent each day, `lifetime` for one spent over the whole life of the entity. Null exactly when `budget` has no provider value to describe. |
| `campaigns[].objective` | string or null | yes | The campaign’s objective, kept as the provider writes it: Meta `OUTCOME_SALES`, TikTok `TRAFFIC`. It is the provider’s word, for the same reason `type` is. Null on every Google Ads campaign, because Google Ads has no objective. |
| `campaigns[].type` | string | yes | The type the provider uses, kept as the provider writes it: `campaign` at every provider. The resource names the concept, and the row keeps the provider’s own word, because a Meta `adset` and a Google Ads `ad_group` are not the same object. |
| `nextCursor` | string | no | Send this value as `cursor` to get the next page. It is absent on the last page. |

### Failure codes

A failed call has `isError` set, and `structuredContent.error` holds one of these codes. [Errors](/mcp/errors) describes the shape of a failed call.

- `not_found`
- `provider_not_connected`
- `provider_error`
- `live_read_unsupported`
- `invalid_cursor`
- `forbidden`
- `invalid_request`
- `internal_error`

### Scope

The token must hold `observe:read`. [Auth & scopes](/mcp/auth) lists each scope.

### Annotations

A client reads these hints. A hint that the tool does not declare has the default value of the MCP specification.

- **Read-only.** The tool changes nothing.
- **Open world.** The tool reaches a system outside AdCrunch, such as an ad platform.

### Example

The arguments:

```json
{
  "advertiserId": "acc_1203456789012345",
  "limit": 1
}
```

The result, in `structuredContent`:

```json
{
  "campaigns": [
    {
      "advertiserId": "acc_1203456789012345",
      "budget": 250,
      "budgetLevel": "campaign",
      "budgetType": "daily",
      "createdAt": null,
      "createdTime": 1789600000000,
      "currency": "EUR",
      "deletedAt": null,
      "id": "120210000000000",
      "name": "Spring sale — prospecting",
      "objective": "OUTCOME_SALES",
      "path": "120210000000000",
      "provider": "meta",
      "status": "ACTIVE",
      "type": "campaign",
      "updatedAt": null,
      "updatedTime": 1789900000000
    }
  ],
  "nextCursor": "eyJwIjp7ImFmdGVyIjoiUVZGSVVteFpBVmhSUTJ0QlVXdFIiLCJwcm92aWRlciI6Im1ldGEifSwicSI6IjF6ZnVpaHR5aGJiIiwibCI6dHJ1ZX0"
}
```
