# GET /v1/projects/:projectId/apple-ads-attribution (/docs/api/reference/telemetry/apple-ads-attribution)



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

Returns Apple Search Ads (ASA) attribution records the Layers SDK captured on iOS installs. Each record carries the campaign and ad group the install attributed to, plus the install timestamp.

The Layers SDK collects the Apple attribution token at first launch. Layers exchanges that token with Apple and returns the retained campaign, ad group, country, and install timestamp fields through this endpoint. Apple-side fields that Layers does not retain are returned as zero or `null` placeholders.

Records appear only for users who installed via an ASA ad and whose device returned a valid token within the Apple-defined attribution window. Organic installs and users who opted out of tracking (`att_status != authorized`) do not appear here.

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

<Parameters
  title="Query"
  rows="[
  { name: 'userId', type: 'string', description: 'Filter to a single `app_user_id`.' },
  { name: 'since', type: 'string (ISO 8601, UTC Z)', description: 'Inclusive lower bound on `installedAt`.' },
  { name: 'until', type: 'string (ISO 8601, UTC Z)', description: 'Exclusive upper bound on `installedAt`.' },
  { name: 'cursor', type: 'string', description: 'Opaque pagination token.' },
  { name: 'limit', type: 'integer', description: 'Page size, 1–200.', default: '50' },
]"
/>

## 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/apple-ads-attribution?since=2026-04-11T00:00:00Z" \
      -H "Authorization: Bearer lp_..."
    ```
  </Tab>

  <Tab value="TypeScript">
    ```ts
    const { records, nextCursor } = await layers.appleAdsAttribution.list(
      "prj_254a4ce1-f4ca-42b1-9e36-17ca45ef3d39",
      { since: "2026-04-11T00:00:00Z" }
    );
    ```
  </Tab>

  <Tab value="Python">
    ```python
    result = layers.apple_ads_attribution.list(
        project_id="prj_254a4ce1-f4ca-42b1-9e36-17ca45ef3d39",
        since="2026-04-11T00:00:00Z",
    )
    ```
  </Tab>
</Tabs>

## Response [#response]

<Response status="200" description="OK">
  ```json
  {
    "records": [
      {
        "recordId": "01HXA7K8M2P4RSTUV56789AB",
        "appUserId": "user_a7f3c1d9",
        "appId": "01HXA1NPQR5TVWXYZABCDEFGH",
        "attribution": "true",
        "orgId": 0,
        "campaignId": 1440210011,
        "adGroupId": 1440210072,
        "keywordId": 0,
        "creativeSetId": 0,
        "countryOrRegion": "US",
        "conversionType": "Download",
        "clickDate": null,
        "installedAt": "2026-04-11T16:06:02Z",
        "reDownload": false,
        "exchangedAt": "2026-04-11T16:06:02Z"
      }
    ],
    "nextCursor": null
  }
  ```

  `recordId` and `appId` are returned as raw UUIDs without partner prefixes. `orgId`, `keywordId`, `creativeSetId`, and `clickDate` may be zero or `null`; treat the `campaignId` + `adGroupId` pair as the canonical attribution key. `exchangedAt` mirrors `installedAt`.
</Response>

## Errors [#errors]

| Status | Code              | When                           |
| ------ | ----------------- | ------------------------------ |
| 400    | `VALIDATION`      | Bad `since`/`until` or cursor. |
| 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 (includes non-iOS platforms)
* [`GET /v1/projects/:projectId/conversions`](/docs/api/reference/telemetry/conversions) - aggregated conversions
