# GET /v1/projects/:projectId/conversions (/docs/api/reference/telemetry/conversions)



<Endpoint method="GET" path="/v1/projects/:projectId/conversions" auth="Bearer" scope="events:read" phase="1" />

Aggregates conversion events over a time window. Use it to render conversion widgets without walking the raw [event stream](/docs/api/reference/telemetry/events) 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_amount` → `price_amount` → `properties.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.

<Parameters
  title="Path"
  rows="[
  { name: 'projectId', type: 'string', required: true, description: 'Project ID.' },
]"
/>

<Parameters
  title="Query"
  rows="[
  { name: 'since', type: 'string (ISO 8601, UTC Z)', required: true, description: 'Inclusive lower bound on `occurred_at`.' },
  { name: 'until', type: 'string (ISO 8601, UTC Z)', required: true, description: 'Exclusive upper bound on `occurred_at`.' },
  { name: 'eventNames', type: 'string[]', description: '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`.' },
  { name: 'groupBy', type: 'string[]', description: 'Bucketing dimensions. Comma-separated (`groupBy=day,eventName`) or repeat the param (`groupBy=day&groupBy=eventName`).', enum: ['platform', 'appId', 'eventName', 'day', 'hour'] },
  { name: 'appId', type: 'string', description: 'Filter to a single SDK app.' },
]"
/>

## Example request [#example-request]

<Tabs items="['curl', 'TypeScript', 'Python']">
  <Tab value="curl">
    ```bash
    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_..."
    ```
  </Tab>

  <Tab value="TypeScript">
    ```ts
    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"],
      }
    );
    ```
  </Tab>

  <Tab value="Python">
    ```python
    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"],
    )
    ```
  </Tab>
</Tabs>

## Response [#response]

<Response status="200" description="OK">
  ```json
  {
    "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
  }
  ```
</Response>

## Errors [#errors]

| Status | Code              | When                                                                                 |
| ------ | ----------------- | ------------------------------------------------------------------------------------ |
| 422    | `VALIDATION`      | Missing or malformed `since`/`until`, unknown `groupBy` dimension, window > 92 days. |
| 401    | `UNAUTHENTICATED` | Missing or invalid key.                                                              |
| 403    | `FORBIDDEN_SCOPE` | Key lacks `events:read`.                                                             |
| 404    | `NOT_FOUND`       | Project does not exist.                                                              |
| 429    | `RATE_LIMITED`    | Read budget exhausted.                                                               |

## See also [#see-also]

* [`GET /v1/projects/:projectId/events`](/docs/api/reference/telemetry/events) - raw event stream
* [`GET /v1/projects/:projectId/apple-ads-attribution`](/docs/api/reference/telemetry/apple-ads-attribution) - ASA-attributed installs
