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



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

Lists every ad set under the project's connected ad accounts, across platforms. TikTok calls these "ad groups," Apple calls these "ad groups" — Layers normalizes both to "adsets" so you can render one table without per-platform branches. Each row carries its parent campaign, current status, optimization goal, targeting summary, and budget.

<Callout type="warn">
  Read-only today. Creating, updating, pausing, or deleting ad sets is planned. For now, mutate in the platform's ad manager — the next call picks up the change.
</Callout>

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

<Parameters
  title="Query"
  rows="[
  { name: 'campaignId', type: 'string (UUID)', description: 'Restrict to ad sets on one campaign.' },
  { name: 'platforms', type: 'string[]', description: 'Restrict to one or more platforms.', enum: ['meta_ads', 'tiktok_ads', 'apple_ads'] },
  { name: 'adAccountId', type: 'string (UUID)', description: 'Restrict to ad sets on one ad account.' },
  { name: 'status', type: 'string[]', description: 'Filter by platform-native status.', enum: ['active', 'paused', 'archived', 'deleted', 'in_review', 'rejected'] },
  { name: 'cursor', type: 'string', description: 'Opaque pagination cursor. Forged or malformed cursors are rejected and treated as no cursor (first page).' },
  { name: 'limit', type: 'number', description: 'Page size, 1–200.', default: '100' },
]"
/>

## Example request [#example-request]

<Tabs items="['curl', 'TypeScript', 'Python']">
  <Tab value="curl">
    ```bash
    curl "https://api.layers.com/v1/projects/prj_01HX9Y7K8M2P4RSTUV56789AB/ads/adsets?campaignId=cmp_01HXG9&status=active" \
      -H "Authorization: Bearer lp_live_01HX9Y6K7EJ4T2_4QZpN..."
    ```
  </Tab>

  <Tab value="TypeScript">
    ```ts
    const res = await fetch(
      `https://api.layers.com/v1/projects/${projectId}/ads/adsets?campaignId=${campaignId}&status=active`,
      { headers: { Authorization: `Bearer ${apiKey}` } },
    );
    const { items, nextCursor } = await res.json();
    ```
  </Tab>

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

    r = httpx.get(
        f"https://api.layers.com/v1/projects/{project_id}/ads/adsets",
        params={"campaignId": campaign_id, "status": "active"},
        headers={"Authorization": f"Bearer {api_key}"},
    )
    items = r.json()["items"]
    ```
  </Tab>
</Tabs>

## Response [#response]

<Response status="200" description="OK">
  ```json
  {
    "items": [
      {
        "adsetId": "adg_01HXG9...",
        "campaignId": "cmp_01HXG9...",
        "adAccountId": "aa_01HXF1...",
        "platform": "meta_ads",
        "externalId": "23852014...",
        "name": "Q2 Mobile Ordering — Prospecting — Lookalike 1%",
        "status": "active",
        "optimizationGoal": "offsite_conversions",
        "billingEvent": "impressions",
        "dailyBudget": { "amount": 75.00, "currency": "USD" },
        "lifetimeBudget": null,
        "startTime": "2026-04-01T00:00:00Z",
        "endTime": null,
        "targetingSummary": {
          "geo": ["US"],
          "age": { "min": 25, "max": 45 },
          "customAudiences": ["aud_lookalike_1pct_purchasers"],
          "placements": ["facebook_feed", "instagram_feed", "instagram_reels"]
        },
        "createdAt": "2026-03-29T15:04:00Z",
        "metricsSnapshot7d": {}
      },
      {
        "adsetId": "adg_01HXGB...",
        "campaignId": "cmp_01HXGB...",
        "adAccountId": "aa_01HXF2...",
        "platform": "tiktok_ads",
        "externalId": "1785502...",
        "name": "Q2 Retargeting — 30d Site Visitors",
        "status": "paused",
        "optimizationGoal": "conversion",
        "billingEvent": "oCPM",
        "dailyBudget": null,
        "lifetimeBudget": { "amount": 1500.00, "currency": "USD" },
        "startTime": "2026-04-10T00:00:00Z",
        "endTime": "2026-05-10T00:00:00Z",
        "targetingSummary": {
          "geo": ["US"],
          "age": { "min": 18, "max": 54 },
          "customAudiences": ["aud_site_visitors_30d"],
          "placements": ["tiktok_feed"]
        },
        "createdAt": "2026-04-08T21:18:00Z",
        "metricsSnapshot7d": {}
      }
    ],
    "nextCursor": null
  }
  ```
</Response>

## Notes [#notes]

* `optimizationGoal` and `billingEvent` are platform-native strings. Meta and TikTok use different vocabularies; do not assume `conversion` on TikTok means the same thing as `offsite_conversions` on Meta.
* `targetingSummary` is a summary, not the full targeting spec. Platforms encode targeting in very different JSON shapes; Layers surfaces the pieces almost every agent cares about (geo, age, custom audiences, placements) and skips the rest. For the raw spec, fall through to the platform's own API.
* `metricsSnapshot7d` is reserved for a future inline rollup. It is always `{}` today; call [`GET /v1/projects/:projectId/ads-metrics`](/docs/api/reference/ads/ads-metrics) with `scope=adset` and an explicit window for paid performance.
* `endTime` of `null` means the ad set runs indefinitely (on platforms that allow it). TikTok lifetime-budget adsets always have an `endTime`.
* Soft-deleted ad sets are included by default. Filter them out with `status` if you only want live work.

## See also [#see-also]

* [`GET /v1/projects/:projectId/ads/campaigns`](/docs/api/reference/ads/list-campaigns) — parent campaigns
* [`GET /v1/projects/:projectId/ads/ads`](/docs/api/reference/ads/list-ads) — ads one level deeper
* [`GET /v1/projects/:projectId/ads-metrics`](/docs/api/reference/ads/ads-metrics) — paid performance at any scope
