Layers
Partner APIAPI referenceMetrics

GET /v1/projects/:projectId/metrics

Unified organic metrics across post, account, project, and layer scopes. One query shape, one response shape.

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

Organic metrics for anything you can identify - a single platform post, a social account, a project layer, a whole project, or your organization. scope controls what kind of thing you are asking about; id is the specific one; metrics, since, until, and granularity control what you get back.

Metrics always come back as (series, totals). series is the bucketed time series at your requested granularity; totals is the aggregate over the full window. You get both so your UI can render the chart and the big number without a second call.

For ranked creatives rather than raw time series, use GET /v1/projects/:projectId/top-performers.

Query
  • scope
    stringrequired
    What you are asking about.
    One of: platform_post, social_account, project, project_layer, organization
  • id
    stringrequired
    Id of the scoped entity. platform_post takes a platform_post_id; social_account takes a social_account_id; project takes a project_id; project_layer takes a project_layer_id; organization takes the org id from /v1/whoami.
  • metrics
    string[]optional
    Which metrics to return. Omit for the default bundle for the scope.
    One of: views, reach, impressions, likes, comments, shares, saves, watch_time_ms, engagement_rate, followers, follower_delta, posts_published
  • since
    string (ISO 8601, UTC Z)optionaldefault: 30 days ago
    Window start. Inclusive.
  • until
    string (ISO 8601, UTC Z)optionaldefault: now
    Window end. Exclusive.
  • granularity
    stringoptionaldefault: day
    Series bucket size.
    One of: hour, day, week

Example request

curl "https://api.layers.com/v1/projects/prj_254a4ce1-f4ca-42b1-9e36-17ca45ef3d39/metrics?scope=project&id=prj_254a4ce1-f4ca-42b1-9e36-17ca45ef3d39&metrics=views&metrics=engagement_rate&since=2026-04-01T00:00:00Z&until=2026-04-18T00:00:00Z&granularity=day" \
  -H "Authorization: Bearer lp_..."
const params = new URLSearchParams({
  scope: "project",
  id: "prj_254a4ce1-f4ca-42b1-9e36-17ca45ef3d39",
  since: "2026-04-01T00:00:00Z",
  until: "2026-04-18T00:00:00Z",
  granularity: "day",
});
params.append("metrics", "views");
params.append("metrics", "engagement_rate");

const res = await fetch(
  `https://api.layers.com/v1/projects/prj_254a4ce1-f4ca-42b1-9e36-17ca45ef3d39/metrics?${params}`,
  { headers: { Authorization: `Bearer ${apiKey}` } },
);
const { series, totals } = await res.json();
import httpx

params = [
    ("scope", "project"),
    ("id", "prj_254a4ce1-f4ca-42b1-9e36-17ca45ef3d39"),
    ("since", "2026-04-01T00:00:00Z"),
    ("until", "2026-04-18T00:00:00Z"),
    ("granularity", "day"),
    ("metrics", "views"),
    ("metrics", "engagement_rate"),
]
r = httpx.get(
    "https://api.layers.com/v1/projects/prj_254a4ce1-f4ca-42b1-9e36-17ca45ef3d39/metrics",
    params=params,
    headers={"Authorization": f"Bearer {api_key}"},
)
payload = r.json()

Response

200OK
{
  "scope": "project",
  "id": "prj_254a4ce1-f4ca-42b1-9e36-17ca45ef3d39",
  "window": {
    "since": "2026-04-01T00:00:00Z",
    "until": "2026-04-18T00:00:00Z",
    "granularity": "day"
  },
  "series": [
    { "bucket": "2026-04-01", "views": 12840, "engagement_rate": 0.041 },
    { "bucket": "2026-04-02", "views": 17220, "engagement_rate": 0.052 },
    { "bucket": "2026-04-03", "views": 9880,  "engagement_rate": 0.037 }
  ],
  "totals": {
    "views": 240911,
    "engagement_rate": 0.047,
    "post_count": 23
  }
}
422scope/id mismatch or unknown metric.
{ "error": { "code": "VALIDATION", "message": "Unknown metric: conversions. Use /v1/projects/:projectId/ads-metrics for paid metrics." } }
404Scoped entity not found or not in your organization.

Defaults by scope

ScopeDefault metricsNotes
platform_postviews, likes, comments, shares, saves, engagement_rateSingle post; granularity=day or hour.
social_accountfollowers, follower_delta, posts_published, viewsAccount-level roll-up.
project_layerviews, engagement_rate, posts_publishedPer-layer so you can compare Social Content vs Managed UGC side by side.
projectSame as project_layer, summed across layers.
organizationviews, posts_published across all projects.Coarse - use for an overview pane.

Notes

  • Engagement rate is weighted-cumulative: (sum likes + sum comments + sum shares + sum saves) / max(sum views, 1) over every post in the bucket (or in the totals window). This is the same math the Layers customer-facing dashboard renders. It is NOT the unweighted mean of per-post engagement_rate values — that aggregation overweights low-view posts and gave different numbers than the UI for the same query, so we picked weighted-cumulative as the single source of truth across both surfaces. Per-bucket and totals are computed the same way.
  • watch_time_ms is only populated for platforms that expose it (TikTok, Instagram Reels). Zero on static formats.
  • Paid metrics (spend, CPA, ROAS) live on GET /v1/projects/:projectId/ads-metrics. Asking for spend here returns VALIDATION.
  • Windows longer than 90 days with granularity=hour get clamped. Either narrow the window or step up to day.
  • scope=social_account and scope=project reads expand to sibling credentials automatically — when an account is reconnected, OAuth produces a new social_accounts row and posts attach to whichever row was live at ingestion. The endpoint unions all rows for the same (platform, platform_user_id) within the project so totals match the dashboard.
  • Posts are bucketed by published_at (the publish moment), not by metric snapshot time. Windows and buckets are UTC; the dashboard renders in the project timezone, so day-boundary numbers can differ by up to 24h for projects in non-UTC zones. If you need timezone-aligned daily numbers, request granularity=day and group client-side, or window precisely to your local day boundaries.

See also

On this page