Apple Ads
The apple_ads connector reads campaigns, ad groups, targeting keywords, and performance reports from the Apple Ads Platform API. It runs on StreamWright's declarative HTTP Transport (transport = "http").
Auth & HTTP Configuration
Apple Ads uses OAuth Bearer tokens and requires an X-AP-Context header scoping requests to your ad account ID. Endpoints use POST-based queries with body-embedded pagination (in: body):
kind: source
name: apple_ads_demo
spec:
config:
ad_account_id: { type: string, description: "Apple Ads ad account ID (sent as X-AP-Context)" }
campaign_ids: { type: list, items: string, required: false, description: "List of campaign IDs for child streams" }
secrets:
apple_ads_token: { type: string }
auth:
provider: apple_ads
type: bearer
token: "{{ secrets.apple_ads_token }}"
http:
base_url: https://api.ads.apple.com
headers:
X-AP-Context: "adAccountId={{ config.ad_account_id }}"
paginator:
type: offset
in: body
offset_param: pagination.offset
limit_param: pagination.limit
page_size: 100
total_path: pagination.totalCount
records:
path: result
The access token is treated as a secret and automatically redacted in logs.
Reading data
Each stream queries its corresponding Apple Ads POST query endpoint, inheriting account headers and body pagination:
description: Apple Ads campaigns and budget configurations.
requests:
- name: raw_campaigns
http:
path: /v1/campaigns/query
method: POST
json:
pagination:
pageSize: 100
fetchTotalCount: true
transform:
mode: page
steps:
- name: campaigns
select: |
SELECT (record->>'adAccountId')::BIGINT AS ad_account_id,
(record->>'id')::BIGINT AS campaign_id,
record->>'name' AS campaign_name,
record->>'status' AS status,
record->>'servingStatus' AS serving_status,
(record->>'$.dailyBudgetAmount.amount')::DOUBLE AS daily_budget,
record->>'$.dailyBudgetAmount.currency' AS currency,
record->>'startTime' AS start_time,
record->>'endTime' AS end_time
FROM raw_campaigns
export:
campaigns:
step: campaigns
primary_key: [campaign_id]
Reporting requests
Query reporting aggregates using POST reporting endpoints with custom date ranges and result.rows records path override:
description: Apple Ads campaign reporting analytics (impressions, taps, installs, spend).
requests:
- name: raw_campaign_reports
records:
path: result.rows
http:
path: /v1/reports/apps/campaigns/query
method: POST
json:
pagination:
pageSize: 100
fetchTotalCount: true
timeRange:
startTime: "2026-01-01"
endTime: "2026-01-07"
granularity: DAILY
transform:
mode: page
steps:
- name: campaign_reports
select: |
SELECT COALESCE(record->'metadata'->>'id', record->>'campaignId')::BIGINT AS campaign_id,
record->'metadata'->>'name' AS campaign_name,
COALESCE((record->'totalMetrics'->>'impressions')::BIGINT, (record->>'impressions')::BIGINT) AS impressions,
COALESCE((record->'totalMetrics'->>'taps')::BIGINT, (record->>'taps')::BIGINT) AS taps,
COALESCE((record->'totalMetrics'->>'totalInstalls')::BIGINT, (record->>'installs')::BIGINT) AS installs,
COALESCE((record->'totalMetrics'->'localSpend'->>'amount')::DOUBLE, (record->>'$.localSpend.amount')::DOUBLE) AS spend
FROM raw_campaign_reports
export:
campaign_reports:
step: campaign_reports
primary_key: [campaign_id]
Reference streams (examples)
The starter template (examples/sources/ads/apple_ads) includes the following reference streams:
| Stream | Description | Method | Endpoint |
|---|---|---|---|
campaigns | Apple Ads campaigns & daily budgets | POST | /v1/campaigns/query |
ad_groups | Ad groups and default bids across campaigns | POST | /v1/adgroups/query |
keywords | Targeted keywords, match types, and bid amounts | POST | /v1/keywords/query |
campaign_reports | Performance metrics (impressions, taps, installs, spend) | POST | /v1/reports/apps/campaigns/query |
These streams are reference examples. Because streams in StreamWright are declarative, you can customize columns, adjust DuckDB queries, or add new endpoints to match your upstream reporting needs.