Layers
Partner APIAPI referenceAds

GET /v1/projects/:projectId/ads-metrics

Paid performance — spend, CPA, ROAS, CPC, CPM — across Meta, TikTok, and Apple at campaign, adset, ad, or account scope.

View as Markdown
GET/v1/projects/{projectId}/ads-metrics
Phase 1stable
Auth
Bearer
Scope
metrics:read

Paid performance metrics across Meta, TikTok, and Apple Search Ads. Same shape as /v1/metrics — one query, (series, totals) back — but scoped to ad-manager entities (campaigns, ad sets, ads, ad accounts) and carrying cost-and-conversion metrics instead of organic reach.

Use this for performance analysis, not creative selection. For "what should I promote?" go to ads-content or top-performers. This endpoint answers "what did my ads do?"

Query
  • scope
    stringrequired
    Ad entity you are asking about.
    One of: ad, adset, campaign, ad_account, project
  • id
    stringrequired
    Id of the scoped entity. ad takes adId; adset takes adsetId; campaign takes campaignId; ad_account takes adAccountId; project takes projectId.
  • metrics
    string[]optional
    Metrics to return. Omit for the default bundle for the scope.
    One of: spend, impressions, clicks, conversions, cpa, cpc, cpm, ctr, roas, reach, frequency
  • platforms
    string[]optional
    Restrict to one or more platforms. Only meaningful at ad_account and project scope.
    One of: meta_ads, tiktok_ads, apple_ads
  • since
    string (ISO-8601)optionaldefault: 30 days ago
    Window start. Inclusive.
  • until
    string (ISO-8601)optionaldefault: now
    Window end. Exclusive.
  • granularity
    stringoptionaldefault: day
    Series bucket size.
    One of: day, week

Example request

curl "https://api.layers.com/v1/projects/prj_01HX9Y7K8M2P4RSTUV56789AB/ads-metrics?scope=campaign&id=cmp_01HXG9&metrics=spend&metrics=cpa&metrics=roas&since=2026-04-01&until=2026-04-18" \
  -H "Authorization: Bearer lp_live_01HX9Y6K7EJ4T2_4QZpN..."
const params = new URLSearchParams({
  scope: "campaign",
  id: "cmp_01HXG9",
  since: "2026-04-01",
  until: "2026-04-18",
  granularity: "day",
});
for (const m of ["spend", "cpa", "roas"]) params.append("metrics", m);

const res = await fetch(
  `https://api.layers.com/v1/projects/${projectId}/ads-metrics?${params}`,
  { headers: { Authorization: `Bearer ${apiKey}` } },
);
const { series, totals } = await res.json();
import httpx

params = [
    ("scope", "campaign"),
    ("id", "cmp_01HXG9"),
    ("since", "2026-04-01"),
    ("until", "2026-04-18"),
    ("granularity", "day"),
    ("metrics", "spend"),
    ("metrics", "cpa"),
    ("metrics", "roas"),
]
r = httpx.get(
    f"https://api.layers.com/v1/projects/{project_id}/ads-metrics",
    params=params,
    headers={"Authorization": f"Bearer {api_key}"},
)
payload = r.json()

Response

200OK
{
  "scope": "campaign",
  "id": "cmp_01HXG9...",
  "platform": "meta_ads",
  "window": {
    "since": "2026-04-01T00:00:00Z",
    "until": "2026-04-18T00:00:00Z",
    "granularity": "day"
  },
  "series": [
    { "bucket": "2026-04-01", "spend": 142.50, "conversions": 38, "cpa": 3.75, "roas": 3.4 },
    { "bucket": "2026-04-02", "spend": 167.22, "conversions": 45, "cpa": 3.72, "roas": 3.6 },
    { "bucket": "2026-04-03", "spend": 98.40,  "conversions": 24, "cpa": 4.10, "roas": 3.1 }
  ],
  "totals": {
    "spend": 2819.66,
    "conversions": 732,
    "cpa": 3.85,
    "roas": 3.42,
    "impressions": 842100,
    "clicks": 21408,
    "ctr": 0.0254
  }
}
400Metric unavailable for the scope or platform asked.
{ "error": { "code": "VALIDATION", "message": "Metric 'watch_time_ms' is organic-only. Use /v1/metrics." } }
404Entity not found or not in your organization.

Defaults by scope

ScopeDefault metricsNotes
adspend, impressions, clicks, conversions, cpa, roasSingle ad across its lifetime on the platform.
adsetSame as ad, summed across the adset's ads.
campaignSame as adset, summed across the campaign.
ad_accountspend, conversions, cpa, roas across all campaigns in the account.platforms parameter implicit per account.
projectspend, conversions, cpa, roas across every ad account on the project.Use platforms to slice.

Metric definitions

These are Layers' normalized definitions. Platforms report each metric differently — we compute each from normalized primitives (spend, impressions, clicks, conversions) rather than passing through platform values, so cross-platform comparisons mean what you'd expect.

  • cpa = spend / max(conversions, 1)
  • cpc = spend / max(clicks, 1)
  • cpm = spend / max(impressions, 1) * 1000
  • ctr = clicks / max(impressions, 1)
  • roas = conversionValue / max(spend, 0.01) — conversion value comes from the SDK event payload; ads without value reporting (app installs, leads) return null
  • frequency = Meta-only primitive; null on TikTok and Apple

Notes

  • Bucket edges are in UTC. A "day" bucket for 2026-04-01 includes events from 2026-04-01T00:00:00Z to 2026-04-02T00:00:00Z.
  • Ad platforms backfill. Yesterday's numbers can shift for up to 72 hours, particularly on iOS where SKAN postbacks land late. Cache with a short TTL.
  • conversions is the Layers-attributed count, sourced from the SDK → CAPI pipeline plus platform conversion APIs. It will not match the platform's own dashboard exactly — Layers dedupes across sources and applies its own attribution window.
  • Organic metrics (views, engagement_rate) are not available here. Use /v1/metrics.
  • granularity=hour is not supported — paid reporting APIs on Meta and TikTok do not expose hourly reliably. Day is the smallest bucket.

See also

On this page