Layers
Partner APIAPI referenceMetrics

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

Cross-source ranking of creatives by views, engagement rate, conversions, ROAS, or watch time.

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, 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 paging through /v1/metrics and joining 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.
  • includeIneligible
    booleanoptionaldefault: false
    Opt out of the eligibility gate. By default the response only includes creatives the scoring pipeline already deemed eligible. Set to `true` to surface every creative regardless of `organic_score`.

Eligibility filter

By default, results are filtered to creatives the scoring pipeline already deemed eligibleorganic_score >= 4.0 OR override = 'include', with override = 'exclude' always-ineligible. This matches the rule documented on /v1/projects/:projectId/ads-content and is the same gate the platform applies before promoting a creative.

This applies to both the organic and paid paths:

  • Organic (metric=views, engagement_rate, watch_time_ms): UGC platform posts with organic_score < 4.0 and no include override are dropped before ranking.
  • Paid (metric=conversions, roas): mapped creatives with organic_score < 4.0 and no include override are dropped. Unmapped paid rows (no ads_content_id) pass through — we have no scoring signal to gate on.

Opting out

For forensic exploration of low-score content, set ?includeIneligible=true to bypass the gate and surface every creative regardless of organic_score. The metric value reflects exactly what the scoring pipeline saw; nothing is recomputed.

Example request

curl "https://api.layers.com/v1/projects/prj_254a4ce1-f4ca-42b1-9e36-17ca45ef3d39/top-performers?metric=roas&window=30d&limit=10" \
  -H "Authorization: Bearer lp_..."
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 item of items) {
  console.log(item.rank, item.metricValue, item.sourceType, item.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_7d18b9a1...",
      "platformPostId": null,
      "adsContentId": "adc_6f5d4c3b...",
      "title": "Before-and-after latte tutorial",
      "thumbnailUrl": "https://media.layers.com/prj_254a4ce1.../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_6f5d4c3b...",
      "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 item per creative

A single creative can run on multiple platforms and as multiple ads. This endpoint collapses that down to one item per creative, with 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