Layers

GET /v1/projects/:projectId/conversions

Rollup of purchase, subscribe, and custom conversion events.

View as Markdown
GET/v1/projects/:projectId/conversions
Phase 1stable
Auth
Bearer
Scope
events:read

Aggregates conversion events over a time window. Use it to render conversion widgets without walking the raw event stream and re-bucketing client-side.

Totals count every event matching the filter in the window. groups is present when groupBy is set - buckets are sparse, so zero-volume groups are omitted. value sums only events that carry a numeric value; precedence is revenue_amountprice_amountproperties.value (the first present wins, all in decimal dollars, rounded to 2dp). Currency mixing is not normalized - if events in the window carry more than one currency code, currency comes back null; pass a single-currency filter (or set a stable revenue_currency / price_currency SDK-side) if you need a clean sum.

The endpoint is backed by an in-process scan of sdk_events (no pre-aggregated rollup table yet), so reads are capped at 50,000 matching rows per request. When that cap is hit, the response carries truncated: true and totals/groups reflect only the first slice of events. Narrow the window or the eventNames filter to keep reads under the cap.

Path
  • projectId
    stringrequired
    Project ID.
Query
  • since
    string (ISO 8601, UTC Z)required
    Inclusive lower bound on `occurred_at`.
  • until
    string (ISO 8601, UTC Z)required
    Exclusive upper bound on `occurred_at`.
  • eventNames
    string[]optional
    Event names. Comma-separated (`eventNames=a,b`) or repeat the param (`eventNames=a&eventNames=b`). Defaults to the built-in conversion set: `purchase_success`, `subscribe_success`, `trial_start`.
  • groupBy
    string[]optional
    Bucketing dimensions. Comma-separated (`groupBy=day,eventName`) or repeat the param (`groupBy=day&groupBy=eventName`).
    One of: platform, appId, eventName, day, hour
  • appId
    stringoptional
    Filter to a single SDK app.

Example request

curl "https://api.layers.com/v1/projects/prj_254a4ce1-f4ca-42b1-9e36-17ca45ef3d39/conversions?since=2026-04-11T00:00:00Z&until=2026-04-18T00:00:00Z&groupBy=day,eventName" \
  -H "Authorization: Bearer lp_..."
const { totals, groups } = await layers.conversions.list(
  "prj_254a4ce1-f4ca-42b1-9e36-17ca45ef3d39",
  {
    since: "2026-04-11T00:00:00Z",
    until: "2026-04-18T00:00:00Z",
    groupBy: ["day", "eventName"],
  }
);
result = layers.conversions.list(
    project_id="prj_254a4ce1-f4ca-42b1-9e36-17ca45ef3d39",
    since="2026-04-11T00:00:00Z",
    until="2026-04-18T00:00:00Z",
    group_by=["day", "eventName"],
)

Response

200OK
{
  "window": {
    "since": "2026-04-11T00:00:00Z",
    "until": "2026-04-18T00:00:00Z"
  },
  "totals": {
    "count": 1284,
    "uniqueUsers": 912,
    "value": 22647.4,
    "currency": "USD"
  },
  "groups": [
    {
      "key": { "day": "2026-04-11", "eventName": "purchase_success" },
      "count": 142,
      "uniqueUsers": 131,
      "value": 2501.85
    },
    {
      "key": { "day": "2026-04-11", "eventName": "subscribe_success" },
      "count": 38,
      "uniqueUsers": 38,
      "value": 380.00
    },
    {
      "key": { "day": "2026-04-12", "eventName": "purchase_success" },
      "count": 201,
      "uniqueUsers": 185,
      "value": 3487.20
    }
  ],
  "truncated": false
}

Errors

StatusCodeWhen
422VALIDATIONMissing or malformed since/until, unknown groupBy dimension, window > 92 days.
401UNAUTHENTICATEDMissing or invalid key.
403FORBIDDEN_SCOPEKey lacks events:read.
404NOT_FOUNDProject does not exist.
429RATE_LIMITEDRead budget exhausted.

See also

On this page