Layers
Partner APIAPI referenceMetrics

GET /v1/projects/:projectId/top-performers

Cross-source ranking of creatives by views, engagement rate, conversions, ROAS, or watch time — one row per creative, pre-ranked.

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

Returns the top-N creatives in a project, ranked by a single metric across an explicit window. Generated containers, UGC posts, and manual uploads are pooled together — one row per creative, with both the organic signal (views, engagement) and the paid signal (conversions, ROAS) attached. This is the shortcut for "what should I promote right now?" without having to page through /v1/metrics and join across sources yourself.

The list is already sorted by the metric you asked for. No client-side sort needed. No per-creative metric roll-ups needed. Pre-ranking is why this endpoint exists as a separate call rather than a flag on unified metrics.

Path
  • projectId
    string (UUID)required
    Project to rank within.
Query
  • metric
    stringrequired
    Ranking dimension.
    One of: views, engagement_rate, conversions, roas, watch_time_ms
  • window
    stringoptionaldefault: 30d
    Window to evaluate over.
    One of: 7d, 30d, 90d
  • sourceType
    string[]optional
    Restrict to one or more sources.
    One of: content_container, platform_post, manual
  • platform
    string[]optional
    Restrict to posts on these platforms. Ignores paid-only sources.
    One of: instagram, tiktok, youtube, meta_ads, tiktok_ads, apple_ads
  • limit
    numberoptionaldefault: 25
    Number of rows to return, 1–100.

Example request

curl "https://api.layers.com/v1/projects/prj_01HX9Y7K8M2P4RSTUV56789AB/top-performers?metric=roas&window=30d&limit=10" \
  -H "Authorization: Bearer lp_live_01HX9Y6K7EJ4T2_4QZpN..."
const res = await fetch(
  `https://api.layers.com/v1/projects/${projectId}/top-performers?metric=roas&window=30d&limit=10`,
  { headers: { Authorization: `Bearer ${apiKey}` } },
);
const { items } = await res.json();

// Already sorted — just walk it.
for (const row of items) {
  console.log(row.rank, row.metricValue, row.sourceType, row.sourceId);
}
import httpx

r = httpx.get(
    f"https://api.layers.com/v1/projects/{project_id}/top-performers",
    params={"metric": "roas", "window": "30d", "limit": 10},
    headers={"Authorization": f"Bearer {api_key}"},
)
items = r.json()["items"]

Response

200OK — already sorted by the requested metric, descending.
{
  "metric": "roas",
  "window": "30d",
  "items": [
    {
      "rank": 1,
      "sourceType": "content_container",
      "sourceId": "cnt_01HXC8...",
      "platformPostId": null,
      "adsContentId": "adc_01HXC9...",
      "title": "Before-and-after latte tutorial",
      "thumbnailUrl": "https://media.layers.com/prj_.../thumb_01HXC8.jpg",
      "metricValue": 5.2,
      "organic": {
        "views": 148200,
        "engagementRate": 0.061,
        "platforms": ["instagram", "tiktok"]
      },
      "paid": {
        "spend": 412.00,
        "conversions": 88,
        "roas": 5.2,
        "cpa": 4.68
      }
    },
    {
      "rank": 2,
      "sourceType": "platform_post",
      "sourceId": null,
      "platformPostId": "pp_01HXD1...",
      "adsContentId": "adc_01HXD2...",
      "title": "Creator pour-over UGC",
      "thumbnailUrl": "https://media.meetsift.com/.../cover.jpg",
      "metricValue": 4.7,
      "organic": {
        "views": 88100,
        "engagementRate": 0.079,
        "platforms": ["tiktok"]
      },
      "paid": {
        "spend": 210.00,
        "conversions": 41,
        "roas": 4.7,
        "cpa": 5.12
      }
    }
  ]
}
422Validation failed — metric missing, unknown enum, or `metric=roas` paired with an organic-only source mix.
{ "error": { "code": "VALIDATION", "message": "metric=roas requires paid data. Filter sourceType to content_container or manual, or pick an organic metric." } }

One row per creative

A single creative can run on multiple platforms and as multiple ads. This endpoint collapses that down: one row per ads_content row, organic and paid metrics summed across surfaces in the window. Use /v1/metrics?scope=platform_post if you need per-platform breakouts.

The window parameter is explicit — there is no "all time." Windows are evaluated at query time against platform and ad sync data, so results can shift between calls if a sync completes mid-flight.

Metric availability by source

Sourceviewsengagement_ratewatch_time_msconversionsroas
content_containerYesYesYesOnly if promotedOnly if promoted
platform_postYesYesYes (TikTok/Reels)Only if promotedOnly if promoted
manualNoNoNoOnly if promotedOnly if promoted

Ranking by a paid metric implicitly filters out creatives that were never promoted. Ranking by an organic metric includes everything with platform metrics.

Notes

  • thumbnailUrl is permanent for UGC and generated content. Do not cache ad-platform thumbnail URLs from other endpoints — those expire.
  • metricValue is the same number as the field inside organic or paid — surfaced at the top level so your UI can render "5.2x" without guessing which subtree it came from.
  • Ties break on adsContentId descending, which stabilizes the order between requests.
  • Windows 7d/30d/90d end at query time. There is no offset or custom range; use unified metrics for that.

See also

On this page