asc-apple-ads
This skill enables agents to manage Apple Ads campaigns, keywords, budgets, and assets through the asc CLI tool.
Community author
- Agents
- Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode, claude.ai and API
- License
- MIT (permissive)
- Pin
- 9c7e769pinned Oct 9, 2026
- Context cost
- about 3,200 tokenswhen the skill loads
- GitHub stars
- 1,064on rorkai/app-store-connect-cli-skills
- Scripts
- None found
What it does
“Use when managing Apple Ads with asc, including OAuth profiles, ad-account discovery, Platform API v1 campaigns and targeting, reports, assets, recommendations, guarded mutations, raw requests, and Campaign Management API v5 migration.”
Generated summary from the skill's own text
This skill enables agents to manage Apple Ads campaigns, keywords, budgets, and assets through the asc CLI tool. It provides access to Apple Ads Platform API v1 with support for authentication, ad discovery, campaign management, reporting, recommendations, and legacy Campaign Management API v5 commands until January 2027 retirement.
When to use it
- Managing Apple Ads campaigns, ad groups, and keywords programmatically
- Retrieving Apple Ads reports and performance recommendations
- Handling OAuth authentication and discovering available ad accounts
- Migrating from deprecated v5 API commands to Platform API v1
When not to
- If Apple Ads credentials are unavailable or only App Store Connect login exists
- When non-interactive operations cannot support confirmation prompts for mutations
- If targeting complex v5 features with no direct v1 command equivalents
Install
These commands install from the publisher's repository and need no account.
skills CLI
npx skills add rorkai/app-store-connect-cli-skills --skill asc-apple-adsInstalls from the repository's default branch, which may have changed since the commit we checked.
GitHub CLI 2.90 or later
gh skill install rorkai/app-store-connect-cli-skills asc-apple-ads@9c7e769f09a18237c9bd9c70f70aef65f0cd0391Installs the commit we checked (9c7e769).
Copy by hand
Download the folder at 9c7e769 and put it in .claude/skills/asc-apple-ads for Claude Code and in .agents/skills/asc-apple-ads for Codex, Cursor, Gemini CLI, GitHub Copilot and OpenCode.
Copy for your agent
A prompt for any coding agent. It installs this skill from GitHub at the pinned commit, copies the files as they are, and runs nothing unless you ask.
Prompt
Install the agent skill "asc-apple-ads" by rorkai. Source, pinned to commit 9c7e769f09a18237c9bd9c70f70aef65f0cd0391: https://github.com/rorkai/app-store-connect-cli-skills/tree/9c7e769f09a18237c9bd9c70f70aef65f0cd0391/skills/asc-apple-ads 1. Download that folder at exactly this commit. Do not use the default branch. 2. Copy every file unchanged, keeping the folder structure, into .claude/skills/asc-apple-ads/ for Claude Code and .agents/skills/asc-apple-ads/ for Codex, Cursor, Gemini CLI, GitHub Copilot and OpenCode. Create the folders if they do not exist. 3. Do not edit the files, and do not run any script in the folder unless I ask you to. 4. When you are done, list the files you copied and where you put them. Listing and checks: https://www.agentnative.dev/skills/rorkai/asc-apple-ads
SKILL.md at the pin
---
name: asc-apple-ads
description: Use when managing Apple Ads with asc, including OAuth profiles, ad-account discovery, Platform API v1 campaigns and targeting, reports, assets, recommendations, guarded mutations, raw requests, and Campaign Management API v5 migration.
---
# asc Apple Ads
Run Apple Ads work through `asc ads`. Apple Ads credentials are separate from App Store Connect credentials; `asc auth login` does not configure Ads.
## Pick the API first
- Direct `asc ads <resource> ...` commands use Apple Ads Platform API v1 and an ad account ID.
- Deprecated Campaign Management API v5 commands live under `asc ads v5 ...` and use an organization ID. Apple retires v5 on January 26, 2027.
- Never substitute an org ID for an ad account ID. The CLI keeps them separate.
- Run the exact leaf command with `--help` before building a request file. Platform v1 payloads and response envelopes differ from v5; the CLI does not translate them.
- For non-interactive pipelines, pass `--file -` to read a JSON request body from stdin; the CLI rejects it when stdin is a terminal.
- Resource, report, upload, and raw commands emit lossless JSON. Use `jq` for projections instead of asking for table or markdown output.
## Authenticate and pin the account
Store both contexts when a profile must support v1 and legacy v5:
```bash
asc ads auth login \
--name "Marketing" \
--client-id "$ASC_ADS_CLIENT_ID" \
--team-id "$ASC_ADS_TEAM_ID" \
--key-id "$ASC_ADS_KEY_ID" \
--private-key "$ASC_ADS_PRIVATE_KEY_PATH" \
--ad-account "987654" \
--org "123456" \
--network
```
For CI, set Ads-specific variables and bypass the host keychain:
```bash
export ASC_ADS_CLIENT_ID="SEARCHADS_CLIENT_ID"
export ASC_ADS_TEAM_ID="SEARCHADS_TEAM_ID"
export ASC_ADS_KEY_ID="KEY_ID"
export ASC_ADS_PRIVATE_KEY_PATH="$HOME/.asc/apple-ads-private-key.pem"
export ASC_ADS_AD_ACCOUNT_ID="987654"
export ASC_ADS_BYPASS_KEYCHAIN=1
```
`ASC_ADS_PRIVATE_KEY` and `ASC_ADS_PRIVATE_KEY_B64` also work. If another trusted process minted a short-lived token, set `ASC_ADS_ACCESS_TOKEN`; scoped v1 calls still need an ad account ID.
Check auth without printing a token:
```bash
asc ads auth status --validate --output json
asc ads auth discover --ads-profile "Marketing" --output json
asc ads auth doctor --output json
```
Discovery calls Platform v1 `GET /v1/me` and `GET /v1/acls`. Compare each ACL's ad-account ID, name, organization ID, and roles; never select the first result automatically. Print the chosen account before any mutation, then pass both `--ads-profile "Marketing"` and `--ad-account "987654"` when more than one profile or account is available.
For named profiles, the profile's `ad_account_id` and `org_id` stand alone; they do not inherit context from another profile or root config. V1 context precedence is `--ad-account`, `ASC_ADS_AD_ACCOUNT_ID`, the selected profile, then profile-less root config. Legacy v5 uses the matching `--org` and `ASC_ADS_ORG_ID` chain.
## Start read-only
Identity and ACL calls need no ad account context:
```bash
asc ads me view --ads-profile "Marketing" --output json
asc ads acls list --ads-profile "Marketing" --output json
asc ads orgs view --ads-profile "Marketing" --org-id "123456" --output json
```
Then prove the selected account with a small app search:
```bash
asc ads apps search \
--ads-profile "Marketing" \
--ad-account "987654" \
--query "Example" \
--limit 1 \
--output json
```
App search requires at least one of `--query`, `--cpids`, or `--return-owned-apps`. Storefronts use comma-separated ISO alpha-2 codes. Add `--paginate` only when every search result is needed.
Use each resource's `find` command for inventory. Most v1 queries put filters, sorting, and pagination in a JSON object. A subordinate-resource filter looks like this:
```json
{
"filters": [
{"field": "campaignId", "operator": "EQUALS", "value": ["campaign-id"]}
],
"pagination": {"offset": 0, "pageSize": 100, "fetchTotalCount": true}
}
```
```bash
asc ads campaigns find --ads-profile "Marketing" --ad-account "987654" --output json
asc ads ad-groups find --ads-profile "Marketing" --ad-account "987654" --file query.json --output json
asc ads ads find --ads-profile "Marketing" --ad-account "987654" --file query.json --output json
```
Omitting `--file` from `campaigns find` requests the default first page. To control or exhaust the result set, use `pagination.offset`, `pageSize`, and `fetchTotalCount` in a query file, read the response pagination, and advance the offset until complete. This command has no `--paginate` flag. Platform filters use the singular `value`; do not copy v5 `Selector` fields such as `conditions` or plural `values`, which current `asc` rejects before auth.
The direct v1 tree also covers ad accounts and advertiser resources; app eligibility, locales, product pages, and rejection reasons; brands, business categories, locations, location groups, creatives, and assets; geographic targeting and shared budgets; reports for apps and brands; insights, suggestions, recommendations, and change history. Discover the exact leaf instead of falling back to raw HTTP:
```bash
asc ads change-history --help
asc ads suggestions --help
asc ads rejection-reasons --help
asc ads reports brands --help
```
Keyword queries need a selector file. Targeting keywords require an `id`, `adGroupId`, or `campaignId` filter. Negative keywords require `id` or `adGroupId`; campaign-level negative keywords combine `campaignId` with an `adGroupId` filter whose operator is `IS_NULL`.
```bash
asc ads targeting-keywords find --ads-profile "Marketing" --ad-account "987654" --file keyword-query.json --output json
asc ads negative-keywords find --ads-profile "Marketing" --ad-account "987654" --file negative-keyword-query.json --output json
```
## Reports and optimization
V1 reports require an endpoint-specific body. Dates live under `timeRange`, page controls use `offset` and `pageSize`, and campaign or ad-group IDs belong in `filters`:
```json
{
"pagination": {"offset": 0, "pageSize": 20},
"filters": [
{"field": "campaignId", "operator": "EQUALS", "value": ["campaign-id"]}
],
"groupBy": ["countryOrRegion"],
"timeRange": {
"start": "2026-08-01",
"end": "2026-08-14",
"timeZone": "ORTZ",
"granularity": "DAILY"
}
}
```
```bash
asc ads reports apps campaigns \
--ads-profile "Marketing" \
--ad-account "987654" \
--file report.json \
--output json
```
Report commands do not accept `--paginate`; change pagination in the body. Inspect the leaf help because report entities accept different `groupBy` and option values.
Recommendations and suggestions also use endpoint-specific bodies. Applying or dismissing recommendations can change spend and requires `--confirm`:
```bash
asc ads recommendations daily-budgets find --ads-profile "Marketing" --ad-account "987654" --file query.json
asc ads recommendations daily-budgets apply --ads-profile "Marketing" --ad-account "987654" --file recommendations.json --confirm
```
## Guard mutations
Do not mutate until the user has approved the ad account, resource type, target IDs, and reviewed payload. Keep request JSON in files; never invent fields from a related v5 schema.
Campaign creation may start spending. `CampaignCreate` requires `adAccountId`, `billingEvent`, `dailyBudget`, `name`, `promotedObjectId`, `promotedObjectType`, and `targeting`. The CLI sends the file unchanged: `--ad-account` selects the request context but does not inject `adAccountId` into the JSON. Start from this paused shape, replace every placeholder with values read from the selected account, and recheck the current Apple v1 schema for any account-specific requirements:
```json
{
"name": "ASC agent validation 2026-08-15T00:00:00Z",
"status": "PAUSED",
"adAccountId": 987654,
"promotedObjectType": "APPSTORE_APP",
"promotedObjectId": "123456789",
"billingEvent": "TAPS",
"dailyBudget": {"value": {"amount": "1", "currency": "USD"}},
"startTime": "2030-01-01T00:00:00.000",
"endTime": "2030-01-02T00:00:00.000",
"targeting": {"countryOrRegion": {"include": ["US"]}},
"bidStrategy": {"bidStrategyType": "MANUAL_CPT", "bidStrategyGoal": "TAP"}
}
```
A payload with explicit top-level `"status":"PAUSED"` can run without `--confirm`; an omitted or non-paused status requires it.
```bash
asc ads campaigns create --ads-profile "Marketing" --ad-account "987654" --file paused-campaign.json
asc ads campaigns pause --ads-profile "Marketing" --ad-account "987654" --campaign "campaign-id"
asc ads campaigns resume --ads-profile "Marketing" --ad-account "987654" --campaign "campaign-id" --confirm
```
Campaign updates need `--confirm` when they can change budget, targeting, bids, delivery, dates, or status. A name-only update or a name plus `PAUSED` status does not. Deletes, bulk creates or updates, recommendation apply or dismiss calls, budget-order writes, and other operationally risky mutations require confirmation before auth or network access.
Ad-group creation and keyword bulk writes are examples of always-confirmed delivery or targeting changes. V1 bulk keyword files use wrapper objects such as `KeywordCreateBulkRequest`, not the v5 raw-array shape:
```bash
asc ads ad-groups create --ads-profile "Marketing" --ad-account "987654" --file ad-group.json --confirm
asc ads targeting-keywords create-bulk --ads-profile "Marketing" --ad-account "987654" --file keywords.json --confirm
asc ads targeting-keywords delete --ads-profile "Marketing" --ad-account "987654" --keyword "keyword-id" --confirm
```
Shared budgets use the `budget-orders` command group. Create, update, and delete are context-free but require confirmation; view and find accept optional ad-account context.
```bash
asc ads budget-orders create --ads-profile "Marketing" --file shared-budget.json --confirm
asc ads budget-orders update --ads-profile "Marketing" --budget-order "budget-id" --file update.json --confirm
asc ads budget-orders delete --ads-profile "Marketing" --budget-order "budget-id" --confirm
```
Ad-account creation also requires `--confirm` because its account family cannot change and Apple provides no delete endpoint. An ad-account update containing `delegations` requires confirmation because it replaces the complete list.
Use the dedicated multipart command for brand assets. Poll until Apple finishes processing:
```bash
asc ads assets upload --ads-profile "Marketing" --file ./brand.png --brand "BRAND_ID" --ad-account "987654"
asc ads assets view --ads-profile "Marketing" --asset "ASSET_UUID" --ad-account "987654"
```
Only use an asset when `eligibility.status` is `ELIGIBLE`; for `LIMITED`, inspect `allowedGroups`. Do not attach `PENDING` or `INELIGIBLE` assets.
## Raw requests
Use first-class commands for routine work. Raw v1 requests accept only `v1/...` paths or `https://api.ads.apple.com/v1/...` URLs:
```bash
asc ads api request \
--method POST \
--path v1/campaigns/query \
--ads-profile "Marketing" \
--ad-account "987654" \
--file query.json \
--output json
```
Unknown mutations fail closed, and risky known mutations require `--confirm`. The raw command rejects multipart asset upload; use `asc ads assets upload`.
Keep legacy calls explicit:
```bash
asc ads v5 api request \
--method POST \
--path v5/campaigns/find \
--ads-profile "Marketing" \
--org "123456" \
--file selector.json \
--output json
```
## Migrate v5 one command at a time
Keep existing v5 payloads under `asc ads v5` until each script has a reviewed v1 body and response parser. Common moves:
| Deprecated v5 | Platform API v1 |
| --- | --- |
| `asc ads v5 campaigns list` | `asc ads campaigns find` |
| `asc ads v5 apps localized-details` | `asc ads apps locales find` |
| `asc ads v5 product-pages list` | `asc ads product-pages find` |
| `asc ads v5 reports campaigns` | `asc ads reports apps campaigns` |
| `asc ads v5 campaigns pause` / `resume` | `asc ads campaigns pause` / `resume` |
| v5 campaign or ad-group negative keywords | `asc ads negative-keywords ...` with scope in the body |
Seven v5 leaves have no one-command v1 replacement: product-page countries, product-page devices, targeting-keyword bulk delete, both negative-keyword bulk deletes, and impression-share report list and view. Do not pretend that `geo search`, `insights impression-share`, or single-resource deletes preserve those contracts.
## Finish live tests cleanly
- Start with ACL discovery and a one-result app search.
- Use a unique timestamped name and explicit `PAUSED` status for disposable campaign tests.
- Save every created ID from JSON output.
- Pause spend-bearing resources before checking anything else.
- Reread a test campaign with `asc ads campaigns view --ads-profile "Marketing" --ad-account "987654" --campaign "campaign-id" --output json`.
- Delete only test-created campaigns with `asc ads campaigns delete --ads-profile "Marketing" --ad-account "987654" --campaign "campaign-id" --confirm`; do not delete a pre-existing parent.
- Run the same `campaigns view` again. Treat Apple's not-found response as cleanup proof; report any campaign that still exists or could not be removed.
From rorkai/app-store-connect-cli-skills at 9c7e769, shown as plain text under its license: MIT (permissive). The license text is in LICENSE.
View SKILL.md on GitHub at 9c7e769Files
- SKILL.md13 KB · 2dd8b220d439
1 file, 13 KB in total. Each file has a sha256 in the pin's manifest; the first 12 characters are shown.
Skill folder on GitHub at 9c7e769Related in Marketing, sales and product
accelo-automation
This skill enables agents to automate Accelo project management tasks by discovering and executing operations through Composio's Accelo toolkit via Rube MCP.
by ComposioOfficial publisher
Agents not labelled yet · about 690 tokens · pinned 6 days ago
acculynx-automation
This skill automates Acculynx operations by discovering and executing available tools through the Rube MCP server.
by ComposioOfficial publisher
Agents not labelled yet · about 690 tokens · pinned 6 days ago
active-campaign-automation
This skill automates ActiveCampaign tasks by discovering and executing tools through Rube MCP and Composio's ActiveCampaign toolkit.
by ComposioOfficial publisher
Agents not labelled yet · about 710 tokens · pinned 6 days ago
adrapid-automation
This skill enables agents to automate Adrapid tasks through Composio's toolkit via Rube MCP.
by ComposioOfficial publisher
Agents not labelled yet · about 690 tokens · pinned 6 days ago
aeroleads-automation
This skill automates Aeroleads tasks by connecting to the Aeroleads toolkit via Composio's Rube MCP server.
by ComposioOfficial publisher
Agents not labelled yet · about 700 tokens · pinned 6 days ago
affinity-automation
This skill automates Affinity CRM operations by executing tools through Rube MCP and Composio's Affinity toolkit.
by ComposioOfficial publisher
Agents not labelled yet · about 690 tokens · pinned 6 days ago