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. Meta and TikTok ads are included; Apple Search Ads is intentionally excluded because ASA does not expose the same ad-level object. 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.
Mutating ads (push / replace / prune creative, pause / resume / archive, etc.) requires ads:write:creative and ads:write:lifecycle and goes through the bucket-mode authority gate. Apple Search Ads has no ad-level entity (D36) — Apple-side lifecycle happens at the keyword / adgroup grain.
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 creative. 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_254a4ce1-f4ca-42b1-9e36-17ca45ef3d39/ads/ads?adsetId=adg_01HXG9&status=active" \
-H "Authorization: Bearer lp_..."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_6f5d4c3b... - "What score does this ad's creative have?" Follow
adsContentIdto/ads-content?adsContentId=.... - "Is this ad's creative manually overridden?" Same path; override state is returned by
ads-content.
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 (one ofactive,paused,archived,deleted,in_review,rejected).effectiveStatus,creative,sourceType,launchedAt: may benullwhen platform creative metadata is unavailable. Use the platform ad manager for platform-native effective status.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