Claude
Skills
Sign in
Back

export

Included with Lifetime
$97 forever

Export Spotify Ads API campaign data to CSV — full campaign hierarchies with ad sets, ads, targeting, budgets, and performance metrics for offline review, campaign analysis, or budget reconciliation.

Ads & Marketing

What this skill does


# Spotify Ads API — Campaign Data Export

Export campaign hierarchies to CSV for offline review, combining entity data with optional performance metrics.

## Setup

1. Read `access_token`, `ad_account_id`, and `auto_execute` from the active platform settings file:
   - Codex: prefer `.codex/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`.
   - Claude: prefer `.claude/spotify-ads-api.local.md`, then fall back to `.codex/spotify-ads-api.local.md`.
2. Base URL: `https://api-partner.spotify.com/ads/v3`
3. If neither settings file exists, instruct the user to run `/spotify-ads-api:configure` first.
4. Read the active platform manifest for the plugin `version`: `.codex-plugin/plugin.json` on Codex or `.claude-plugin/plugin.json` on Claude.
5. Set `SDK_PRODUCT` to `codex-plugin` on Codex or `claude-code-plugin` on Claude. Set `SDK_HEADER="X-Spotify-Ads-Sdk: $SDK_PRODUCT/$PLUGIN_VERSION"` and include `-H "$SDK_HEADER"` on all API requests.

## Parsing Arguments

- No argument → Export all campaigns
- `<campaign_id>` (UUID) → Export a specific campaign
- `--metrics` → Include performance metrics (impressions, spend, reach, etc.)
- `--date-range <start> <end>` → Metric date range (ISO 8601). Default: last 30 days.
- If ambiguous, ask the user.

---

## Step 1: Ask Export Preferences

Ask the user to confirm:
- **Scope**: All campaigns or a specific campaign?
- **Include metrics?** Entity data only, or include performance metrics?
- **Output path**: Default `./spotify-ads-export-YYYY-MM-DD.csv`, or user-specified path.

---

## Step 2: Fetch Entity Data

Fetch all entity data with full pagination. Unlike other skills that show the first page, export must retrieve **every** entity to produce a complete file.

### Fetch campaigns

```bash
curl -s -w "\nHTTP_STATUS:%{http_code}" -H "Authorization: Bearer $TOKEN" \
  -H "$SDK_HEADER" \
  "$BASE_URL/ad_accounts/$AD_ACCOUNT_ID/campaigns?limit=50&offset=0"
```

Check `paging.total_results` in the response. If `total_results > 50`, make additional requests incrementing `offset` by 50 until all campaigns are fetched. For a single-campaign export, use:

```bash
curl -s -w "\nHTTP_STATUS:%{http_code}" -H "Authorization: Bearer $TOKEN" \
  -H "$SDK_HEADER" \
  "$BASE_URL/ad_accounts/$AD_ACCOUNT_ID/campaigns/$CAMPAIGN_ID"
```

### Fetch ad sets

```bash
curl -s -w "\nHTTP_STATUS:%{http_code}" -H "Authorization: Bearer $TOKEN" \
  -H "$SDK_HEADER" \
  "$BASE_URL/ad_accounts/$AD_ACCOUNT_ID/ad_sets?limit=50&offset=0"
```

For a single campaign: add `&campaign_ids=$CAMPAIGN_ID`. Paginate with `offset` until all ad sets are fetched.

### Fetch ads

```bash
curl -s -w "\nHTTP_STATUS:%{http_code}" -H "Authorization: Bearer $TOKEN" \
  -H "$SDK_HEADER" \
  "$BASE_URL/ad_accounts/$AD_ACCOUNT_ID/ads?limit=50&offset=0"
```

For a single campaign: add `&campaign_ids=$CAMPAIGN_ID`. Paginate with `offset` until all ads are fetched.

---

## Step 3: Fetch Metrics (if requested)

When metrics are included, fetch aggregate reports at each entity level. For a single-campaign export, add `entity_ids=$CAMPAIGN_ID&entity_ids_type=CAMPAIGN` to every report request so the export does not rely on the first unfiltered page containing the requested campaign's metrics.

### Campaign-level metrics

```bash
curl -s -w "\nHTTP_STATUS:%{http_code}" -H "Authorization: Bearer $TOKEN" \
  -H "$SDK_HEADER" \
  "$BASE_URL/ad_accounts/$AD_ACCOUNT_ID/aggregate_reports?\
entity_type=CAMPAIGN&\
fields=IMPRESSIONS&fields=SPEND&fields=CLICKS&fields=REACH&fields=FREQUENCY&fields=CTR&fields=COMPLETES&\
granularity=LIFETIME&\
entity_status_type=CAMPAIGN&\
limit=50"
```

If a date range is specified, switch to `granularity=DAY` and add `&report_start=<start>&report_end=<end>`.
Use UTC midnight timestamps such as `2026-05-01T00:00:00Z`. Do not send `report_start` or `report_end` with `granularity=LIFETIME`.
Paginate with `continuation_token` if present in the response.

### Ad set-level metrics

```bash
curl -s -w "\nHTTP_STATUS:%{http_code}" -H "Authorization: Bearer $TOKEN" \
  -H "$SDK_HEADER" \
  "$BASE_URL/ad_accounts/$AD_ACCOUNT_ID/aggregate_reports?\
entity_type=AD_SET&\
fields=IMPRESSIONS&fields=SPEND&fields=CLICKS&fields=REACH&fields=FREQUENCY&fields=COMPLETES&fields=COMPLETION_RATE&\
granularity=LIFETIME&\
entity_status_type=AD_SET&\
include_parent_entity=true&\
limit=50"
```

For a single-campaign export, add `&entity_ids=$CAMPAIGN_ID&entity_ids_type=CAMPAIGN`. Paginate with `continuation_token` if present in the response.

### Ad-level metrics

```bash
curl -s -w "\nHTTP_STATUS:%{http_code}" -H "Authorization: Bearer $TOKEN" \
  -H "$SDK_HEADER" \
  "$BASE_URL/ad_accounts/$AD_ACCOUNT_ID/aggregate_reports?\
entity_type=AD&\
fields=IMPRESSIONS&fields=SPEND&fields=CLICKS&fields=REACH&\
granularity=LIFETIME&\
entity_status_type=AD&\
include_parent_entity=true&\
limit=50"
```

For a single-campaign export, add `&entity_ids=$CAMPAIGN_ID&entity_ids_type=CAMPAIGN`. Paginate with `continuation_token` if present.

---

## Step 4: Build and Write CSV

### CSV columns

The CSV is **denormalized** — one row per ad, with campaign and ad set data repeated on each row. Ad sets with no ads get a row with blank ad columns.

**Entity columns:**
- `campaign_id`, `campaign_name`, `campaign_status`, `campaign_objective`
- `ad_set_id`, `ad_set_name`, `ad_set_status`, `ad_set_format`, `ad_set_budget_type`, `ad_set_budget_amount`, `ad_set_bid_strategy`, `ad_set_bid_amount`, `ad_set_start_time`, `ad_set_end_time`, `ad_set_delivery`
- `ad_set_geo_country`, `ad_set_geo_regions`, `ad_set_age_min`, `ad_set_age_max`, `ad_set_platforms`, `ad_set_placements`, `ad_set_genders`
- `ad_id`, `ad_name`, `ad_status`, `ad_delivery`, `ad_tagline`, `ad_advertiser_name`, `ad_cta_key`, `ad_cta_url`

**Metric columns (when `--metrics` is used):**
- `impressions`, `spend`, `clicks`, `reach`, `frequency`, `ctr`, `completes`, `completion_rate`

### Data transformations

- **Budget/bid amounts**: Divide `micro_amount` by 1,000,000 to display in dollars (e.g., `50000000` → `50.00`).
- **Metric SPEND**: Values from `aggregate_reports` are already in dollars — display directly.
- **Geo targeting**: Flatten to `ad_set_geo_country` = country code string, `ad_set_geo_regions` = comma-separated region/DMA/city names if available (IDs if names are not in the response).
- **Age ranges**: Extract first range's `min` and `max` into `ad_set_age_min` and `ad_set_age_max`.
- **Arrays** (platforms, placements, genders): Join with commas (e.g., `"ANDROID,DESKTOP,IOS"`).
- **CSV quoting**: Wrap values containing commas, quotes, or newlines in double quotes. Escape internal double quotes by doubling them (`""`).

### Join logic

Match entities by ID:
- Each ad belongs to an ad set (via `ad_set_id`) which belongs to a campaign (via `campaign_id`).
- Metrics join on `entity_id` from the report rows to the entity's `id`.
- If an entity has no metrics (zero impressions, new campaign), include the row with blank metric columns.

### Write the file

Write the CSV header and rows with a structured CSV writer. Prefer Python's standard `csv` module, or use `jq @csv` if all rows are already available as JSON. Do not build CSV rows with `echo` or string concatenation; that will corrupt values containing commas, quotes, or newlines.

Example Python shape:

```python
import csv

columns = [
    "campaign_id", "campaign_name", "campaign_status", "campaign_objective",
    "ad_set_id", "ad_set_name", "ad_set_status", "ad_set_format",
    "ad_set_budget_type", "ad_set_budget_amount", "ad_set_start_time",
    "ad_set_end_time", "ad_id", "ad_name", "ad_status", "ad_delivery",
    "impressions", "spend", "clicks", "reach",
]

with open(output_path, "w", newline="", encoding="utf-8") as f:
    writer = csv.DictWriter(f, fieldnames=columns, extrasaction="ignore")
    writer.writeheader()
    writer.writerows(rows)
```

Build `rows` from parsed API JSON before writing. Let the CSV writer handle quoting and escapi
Files: 1
Size: 9.4 KB
Complexity: 18/100
Category: Ads & Marketing

Related in Ads & Marketing