GET /v1/projects/:projectId/ads/ads
List individual ads across Meta, TikTok, and Apple, with creative identifiers for joining back to ads-content.
/v1/projects/{projectId}/ads/ads- Auth
- Bearer
- Scope
- ads:read
Lists individual ads under the project, across platforms. Phase 1 ships meta_ads + tiktok_ads only — Apple Search Ads has no equivalent layer junction (ASA bottoms out at the ad-group level), so it is intentionally excluded here. Each ad carries its parent adset and campaign and the adsContentId that lets you join back to ads-content when the ad was promoted from a Layers-scored creative.
Read-only today. Creating, updating, pausing, or deleting ads is planned. Mutate in the platform's ad manager for now.
projectIdstring (UUID)requiredProject to list within.
adsetIdstring (UUID)optionalRestrict to ads in one ad set.campaignIdstring (UUID)optionalRestrict to ads in one campaign.adAccountIdstring (UUID)optionalRestrict to ads on one ad account.platformsstring[]optionalRestrict to one or more platforms.One of:meta_ads,tiktok_ads,apple_adsadsContentIdstring (UUID)optionalRestrict to ads promoting this ads_content row. Useful for "where is this creative running?"statusstring[]optionalFilter by platform-native status.One of:active,paused,archived,deleted,in_review,rejectedcursorstringoptionalOpaque pagination cursor. Forged or malformed cursors are rejected and treated as no cursor (first page).limitnumberoptionaldefault: 100Page size, 1–200.
Example request
curl "https://api.layers.com/v1/projects/prj_01HX9Y7K8M2P4RSTUV56789AB/ads/ads?adsetId=adg_01HXG9&status=active" \
-H "Authorization: Bearer lp_live_01HX9Y6K7EJ4T2_4QZpN..."const res = await fetch(
`https://api.layers.com/v1/projects/${projectId}/ads/ads?adsetId=${adsetId}&status=active`,
{ headers: { Authorization: `Bearer ${apiKey}` } },
);
const { items, nextCursor } = await res.json();
// Find every platform a creative is live on.
const byCreative = new Map<string, string[]>();
for (const ad of items) {
if (!ad.adsContentId) continue;
const platforms = byCreative.get(ad.adsContentId) ?? [];
platforms.push(ad.platform);
byCreative.set(ad.adsContentId, platforms);
}import httpx
r = httpx.get(
f"https://api.layers.com/v1/projects/{project_id}/ads/ads",
params={"adsetId": adset_id, "status": "active"},
headers={"Authorization": f"Bearer {api_key}"},
)
items = r.json()["items"]Response
{
"items": [
{
"adId": "01HXGCA2B3C4D5E6F7G8H9J0K1",
"adsetId": "01HXG9A2B3C4D5E6F7G8H9J0K1",
"campaignId": "01HXG9B2B3C4D5E6F7G8H9J0K1",
"adAccountId": "01HXF1A2B3C4D5E6F7G8H9J0K1",
"platform": "meta_ads",
"externalId": "23852014...",
"name": "23852014...",
"status": "active",
"effectiveStatus": null,
"adsContentId": null,
"sourceType": null,
"creative": null,
"createdAt": "2026-03-29T15:04:00Z",
"launchedAt": null,
"metricsSnapshot7d": {}
},
{
"adId": "01HXGDA2B3C4D5E6F7G8H9J0K1",
"adsetId": "01HXGBA2B3C4D5E6F7G8H9J0K1",
"campaignId": "01HXGBB2B3C4D5E6F7G8H9J0K1",
"adAccountId": "01HXF2A2B3C4D5E6F7G8H9J0K1",
"platform": "tiktok_ads",
"externalId": "1785502...",
"name": "1785502...",
"status": "paused",
"effectiveStatus": null,
"adsContentId": "01HXD2A2B3C4D5E6F7G8H9J0K1",
"sourceType": null,
"creative": null,
"createdAt": "2026-04-08T21:18:00Z",
"launchedAt": null,
"metricsSnapshot7d": {}
}
],
"nextCursor": null
}Joining ads to creatives
Every ad in this response has at most one adsContentId. That field is the bridge to ads-content (the scored creative), top-performers (cross-source ranking), and — for generated content — the source content_container. Use it to answer:
- "Where is this creative running?" Query
/ads?adsContentId=adc_01HXC9... - "What score does this ad's creative have?" Follow
adsContentIdto/ads-content?adsContentId=...or the item directly at the row level. - "Is this ad's creative manually overridden?" Same path — the override lives on the
ads-contentrow.
adsContentId is null only for ads that existed on the platform before Layers tracked them or for creatives outside the scoring pool (rare; mostly legacy imports). Do not rely on null being stable.
Notes
statusis the partner-normalized status mapped from the layer junction's record (one ofactive,paused,archived,deleted,in_review,rejected).effectiveStatus,creative,sourceType,launchedAt: reserved for future enrichment from platform-native ad rows. They are alwaysnullin Phase 1; partners that need creative metadata or the platform-derived effective status must call the platform's own ad manager.metricsSnapshot7dis reserved (always{}today). For paid performance, callGET /v1/projects/:projectId/ads-metricswithscope=adand an explicit window.namedefaults to the platform-native ad id (externalId) when Layers doesn't have a friendlier label cached.adsContentIdis the bridge toads-content. When non-null, the ad was promoted from a Layers-scored creative; when null, the ad was created outside the Layers refresh loop.
See also
GET /v1/projects/:projectId/ads/adsets— parent adsetsGET /v1/projects/:projectId/ads-content— scored creatives (join viaadsContentId)GET /v1/projects/:projectId/ads-metrics?scope=ad— detailed paid metrics for one ad