# Dashboard

> Quick performance overview of all active Spotify ad campaigns — impressions, spend, reach, clicks, and pacing at a glance.

- Skill: `spotify/dashboard` (Agent Skill)
- Install (CLI): `npx skillmds@latest add spotify/dashboard`
- Raw SKILL.md: https://api.skillmd.com/api/skills/spotify/dashboard/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: spotify (https://skillmd.com/u/spotify)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/spotify/dashboard

---


# Spotify Ads API — Campaign Dashboard

Quick performance overview with metrics, spend, and pacing for active campaigns.

## Setup

Set the plugin root and define the request wrapper:

```bash
PLUGIN_ROOT="${CODEX_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT:-.}}"
api() { "$PLUGIN_ROOT/scripts/api-request.sh" dashboard "$@"; }
```

Before the first Ads API v3 call, read and follow `$PLUGIN_ROOT/skills/api-reference/references/live-openapi.md`.

To retrieve settings values (TOKEN, AD_ACCOUNT_ID, AUTO_EXECUTE, BASE_URL, SDK_HEADER, SKILL_HEADER, PLUGIN_VERSION) for use outside API calls, run `api --env`. The output is eval-safe, so `eval $(api --env)` assigns them all.

## Parsing Arguments

- No argument → Account overview (all active campaigns)
- `<campaign_id>` (UUID) → Campaign detail view for that specific campaign
- `detail` → Extended overview with ad set breakdown for all campaigns
- If ambiguous, ask the user.

---

## Account Overview (default — no argument)

Execute two API calls to build the dashboard:

### Call 1: Get campaign metrics

```bash
api GET "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&\
statuses=ACTIVE&\
limit=50"
```

### Call 2: Get campaign details (for names, budget, pacing)

```bash
api GET "ad_accounts/{ad_account_id}/campaigns?limit=50&sort_direction=DESC"
```

### Display format

```
=== Campaign Dashboard ===
Active campaigns: 3 | Total spend: $1,234.56

| Campaign         | Impressions |    Spend |  Reach | Clicks |  CTR  | Frequency |
|------------------|-------------|----------|--------|--------|-------|-----------|
| Summer Promo     |     156,234 | $450.00  | 42,100 |  1,234 | 0.79% |      1.10 |
| Q2 Brand         |      89,456 | $225.50  | 28,200 |    567 | 0.63% |      1.14 |
| Podcast Launch   |      45,000 | $112.30  | 15,800 |    289 | 0.64% |      1.02 |
```

### Formatting rules

- **Spend from `aggregate_reports`**: Already in the ad account's billing currency — display directly as `$X.XX` (do NOT divide by 1,000,000)
- **Budget `micro_amount` from entity details** (campaigns, ad sets): In micro-units — divide by 1,000,000 to get the billing currency amount
- **Impressions/Reach/Clicks**: Format with thousands separators (e.g., `156,234`)
- **CTR**: Display as percentage with 2 decimal places (e.g., `0.79%`)
- **Frequency**: Display with 2 decimal places
- **Filter out** rows with zero impressions for cleaner output

### Pacing calculation

When budget info is available from campaign/ad set details:

- **DAILY budgets**: Compare today's spend to daily budget. Display as: `$450 / $500 daily (90%)`
- **LIFETIME budgets**: Compare total spend to total budget and % of flight elapsed. Display as: `$2,100 / $5,000 lifetime (42%, 35% of flight elapsed)`

To calculate flight elapsed percentage:
```
elapsed_pct = (today - start_date) / (end_date - start_date) * 100
```

If pacing is significantly ahead or behind (spend % differs from elapsed % by more than 20 points), flag it:
- **Overpacing**: "Spending faster than expected — may exhaust budget early"
- **Underpacing**: "Spending slower than expected — may not fully deliver"

### Pagination

If `continuation_token` is present in the response, note that more campaigns exist and suggest running again with pagination.

---

## Campaign Detail View (`<campaign_id>`)

Drill into a specific campaign with ad set breakdown.

### Call 1: Campaign details

```bash
api GET "ad_accounts/{ad_account_id}/campaigns/$CAMPAIGN_ID"
```

### Call 2: Ad set metrics

```bash
api GET "ad_accounts/{ad_account_id}/aggregate_reports?\
entity_type=AD_SET&\
fields=IMPRESSIONS&fields=SPEND&fields=CLICKS&fields=REACH&fields=FREQUENCY&fields=COMPLETES&\
granularity=LIFETIME&\
entity_ids=$CAMPAIGN_ID&\
entity_ids_type=CAMPAIGN&\
include_parent_entity=true&\
limit=50"
```

### Call 3: Ad set details (for budget/targeting info)

```bash
api GET "ad_accounts/{ad_account_id}/ad_sets?campaign_ids=$CAMPAIGN_ID&limit=50"
```

### Display format

```
=== Summer Promo (REACH) ===
Status: ACTIVE | Created: 2026-02-15

| Ad Set           | Format | Budget     | Impressions |   Spend |  Reach | Clicks | Completes |
|------------------|--------|------------|-------------|---------|--------|--------|-----------|
| US 18-34 Audio   | AUDIO  | $75/day    |      98,000 | $300.00 | 28,500 |    890 |    85,000 |
| US 25-54 Video   | VIDEO  | $50/day    |      58,234 | $150.00 | 13,600 |    344 |    42,000 |
```

Apply the same formatting rules as the account overview (spend from reports is already in the billing currency; budget micro_amount from entity details must be divided by 1,000,000).

Show targeting summary for each ad set if available (geo, age range, platforms).

---

## Extended Overview (`detail`)

Like the default account overview, but also breaks down each campaign into its ad sets.

### API calls

Use the same calls as the account overview, plus:

```bash
api GET "ad_accounts/{ad_account_id}/aggregate_reports?\
entity_type=AD_SET&\
fields=IMPRESSIONS&fields=SPEND&fields=CLICKS&fields=REACH&fields=FREQUENCY&fields=COMPLETES&\
granularity=LIFETIME&\
include_parent_entity=true&\
entity_status_type=AD_SET&\
statuses=ACTIVE&\
limit=50"
```

And:

```bash
api GET "ad_accounts/{ad_account_id}/ad_sets?limit=50&sort_direction=DESC"
```

### Display format

Group ad sets under their parent campaign:

```
=== Campaign Dashboard (Detailed) ===
Active campaigns: 2 | Total spend: $675.50

Summer Promo (REACH) — $450.00 spent
| Ad Set           | Format | Impressions |   Spend |  Reach | Clicks |
|------------------|--------|-------------|---------|--------|--------|
| US 18-34 Audio   | AUDIO  |      98,000 | $300.00 | 28,500 |    890 |
| US 25-54 Video   | VIDEO  |      58,234 | $150.00 | 13,600 |    344 |

Q2 Brand (CLICKS) — $225.50 spent
| Ad Set           | Format | Impressions |   Spend |  Reach | Clicks |
|------------------|--------|-------------|---------|--------|--------|
| US All Audio     | AUDIO  |      89,456 | $225.50 | 28,200 |    567 |
```

---

## Execution Behavior

- If `auto_execute` is `true`, execute all API calls directly and display the dashboard.
- If `auto_execute` is `false`, present the curl commands and ask for confirmation before executing.
- On error, show the error message from the response body.
- Spend values from `aggregate_reports` are already in the billing currency — display directly. Budget `micro_amount` values from entity details (campaigns, ad sets) must be divided by 1,000,000. Never show raw micro-amounts to the user.

