# GET /v1/projects/:projectId/ads/ad-accounts (/docs/api/reference/ads/list-ad-accounts)



<Endpoint method="GET" path="/v1/projects/{projectId}/ads/ad-accounts" auth="Bearer" scope="ads:read" phase="1" />

Returns every ad account connected to the project across Meta, TikTok, and Apple Search Ads. Each account carries the platform-native id and current credential health. Layers' ad platform integrations are bring-your-own: partners connect their own ad accounts via OAuth, and platform billing runs against the partner's own funding source.

<Parameters
  title="Path"
  rows="[
  { name: 'projectId', type: 'string (UUID)', required: true, description: 'Project to list within.' },
]"
/>

<Parameters
  title="Query"
  rows="[
  { name: 'platforms', type: 'string[]', description: 'Restrict to one or more platforms.', enum: ['meta_ads', 'tiktok_ads', 'apple_ads'] },
  { name: 'healthy', type: 'boolean', description: 'Keep only ad accounts with tokenStatus=valid.' },
]"
/>

## Example request [#example-request]

<Tabs items="['curl', 'TypeScript', 'Python']">
  <Tab value="curl">
    ```bash
    curl "https://api.layers.com/v1/projects/prj_254a4ce1-f4ca-42b1-9e36-17ca45ef3d39/ads/ad-accounts?healthy=true" \
      -H "Authorization: Bearer lp_..."
    ```
  </Tab>

  <Tab value="TypeScript">
    ```ts
    const res = await fetch(
      `https://api.layers.com/v1/projects/${projectId}/ads/ad-accounts?healthy=true`,
      { headers: { Authorization: `Bearer ${apiKey}` } },
    );
    const { items } = await res.json();
    ```
  </Tab>

  <Tab value="Python">
    ```python
    import httpx

    r = httpx.get(
        f"https://api.layers.com/v1/projects/{project_id}/ads/ad-accounts",
        params={"healthy": True},
        headers={"Authorization": f"Bearer {api_key}"},
    )
    items = r.json()["items"]
    ```
  </Tab>
</Tabs>

## Response [#response]

<Response status="200" description="OK">
  ```json
  {
    "items": [
      {
        "adAccountId": "01HXF1A2B3C4D5E6F7G8H9J0K1",
        "platform": "meta_ads",
        "externalId": "act_123456789012345",
        "name": "Acme Coffee - US",
        "currency": "USD",
        "timezone": "UTC",
        "tokenStatus": "valid",
        "tokenLastValidatedAt": null,
        "connectedAt": "2026-03-02T14:11:00Z"
      },
      {
        "adAccountId": "01HXF2A2B3C4D5E6F7G8H9J0K1",
        "platform": "tiktok_ads",
        "externalId": "7389201...",
        "name": "Acme Coffee TikTok",
        "currency": "USD",
        "timezone": "UTC",
        "tokenStatus": "valid",
        "tokenLastValidatedAt": null,
        "connectedAt": "2026-03-18T09:42:00Z"
      },
      {
        "adAccountId": "01HXF3A2B3C4D5E6F7G8H9J0K1",
        "platform": "apple_ads",
        "externalId": "org-9823412",
        "name": "Acme Coffee iOS",
        "currency": "USD",
        "timezone": "UTC",
        "tokenStatus": "expired",
        "tokenLastValidatedAt": null,
        "connectedAt": "2026-02-11T19:05:00Z"
      }
    ]
  }
  ```

  The response is unpaginated. Each project has at most one ad account connection per platform, so `items` returns the union directly with no `nextCursor`.
</Response>

All ad accounts are bring-your-own: the end-customer connects their own ad account via OAuth, and platform billing runs against the customer's own funding source on the platform.

## Token status [#token-status]

| Value     | Meaning                                                                                                                                          |
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| `valid`   | Last validation call succeeded. Safe to use.                                                                                                     |
| `expired` | Refresh attempt failed. Reconnect via [`POST /v1/projects/:projectId/ads/ad-accounts/oauth-url`](/docs/api/reference/ads/ad-accounts-oauth-url). |
| `revoked` | User revoked the grant on the platform. Reconnect required.                                                                                      |
| `pending` | Connection just initiated; first validation has not landed.                                                                                      |

`tokenLastValidatedAt` may be `null` until the first validation timestamp is available.

## Notes [#notes]

* Query the platform's own API if you need the account's spend or wallet state — Layers does not surface it.
* `externalId` is the platform-native account id - `act_...` on Meta, a numeric string on TikTok, `org-...` on Apple. Use it when linking out to the platform's ad manager.
* `timezone` may be `"UTC"` when the platform-native timezone is unavailable. If you need the local-clock timezone the ads are bidding against, query the platform's own API.
* A `tokenStatus` of `expired` or `revoked` does not immediately pause running ads - the platform keeps those running from its side. New ad creation against that ad account will fail until reconnected.
* Disconnected ad accounts are excluded from the response by default. There is no include-disconnected flag.

## See also [#see-also]

* [`POST /v1/projects/:projectId/ads/ad-accounts/oauth-url`](/docs/api/reference/ads/ad-accounts-oauth-url) - connect or reconnect
* [`GET /v1/projects/:projectId/ads/campaigns`](/docs/api/reference/ads/list-campaigns) - campaigns on these accounts
* [`GET /v1/projects/:projectId/ads/capi-status`](/docs/api/reference/ads/capi-status) - CAPI relay health per platform
