Requirements
- Target platform
- OpenClaw
- Install method
- Manual import
- Extraction
- Extract archive
- Prerequisites
- OpenClaw
- Primary doc
- SKILL.md
Create, optimize, and scale Apple Search Ads campaigns with API automation, attribution integration, and bid strategy recommendations.
Create, optimize, and scale Apple Search Ads campaigns with API automation, attribution integration, and bid strategy recommendations.
Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.
I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Tell me what you changed and call out any manual steps you could not complete.
I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Summarize what changed and any follow-up checks I should run.
Complete toolkit for Apple Search Ads: Campaign Management API v5, attribution integration (AdServices + SKAdNetwork), bid optimization, and strategic recommendations.
Full Campaign Management API v5 coverage iOS app integration (AdServices framework) SKAdNetwork 4.0 support Automated reporting scripts Bid optimization strategies Multi-country campaign patterns
Setup When to Use Architecture API Essentials Campaign Structure Keywords & Bidding Attribution Integration Reports & Analytics Strategy Playbook Scripts & Automation Common Traps
On first use, read setup.md for integration guidelines.
User needs to run Apple Search Ads for iOS apps. Agent handles campaign creation, bid optimization, attribution tracking, performance analysis, and strategic recommendations.
Memory lives in ~/apple-search-ads/. See memory-template.md for structure. ~/apple-search-ads/ โโโ memory.md # Active campaigns, preferences, learnings โโโ credentials.md # OAuth config (NEVER commit real secrets) โโโ campaigns/ # Campaign-specific notes and performance โ โโโ {app-id}/ โโโ reports/ # Generated reports โโโ scripts/ # Custom automation
TopicFileSetup processsetup.mdMemory templatememory-template.mdAPI endpointsapi-reference.mdiOS integrationios-integration.mdStrategy guidestrategy.mdScript libraryscripts.md
Apple Ads API uses OAuth with client credentials. Generate credentials at: https://app.searchads.apple.com/cm/app/settings/apicertificates # 1. Generate client secret (JWT signed with private key) # Header { "alg": "ES256", "kid": "{KEY_ID}" } # Payload { "sub": "{CLIENT_ID}", "aud": "https://appleid.apple.com", "iat": {CURRENT_TIMESTAMP}, "exp": {TIMESTAMP_+180_DAYS}, "iss": "{TEAM_ID}" } # 2. Exchange for access token curl -X POST "https://appleid.apple.com/auth/oauth2/token" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=client_credentials" \ -d "client_id={CLIENT_ID}" \ -d "client_secret={CLIENT_SECRET}" \ -d "scope=searchadsorg" # Response contains access_token (valid 1 hour)
Base URL: https://api.searchads.apple.com/api/v5 Headers: Authorization: Bearer {ACCESS_TOKEN} X-AP-Context: orgId={ORG_ID} Content-Type: application/json
ResourceMethodEndpointAppsSearch appsPOST/search/appsApp eligibilityGET/apps/{adamId}/eligibilitiesCampaignsList campaignsGET/campaignsCreate campaignPOST/campaignsUpdate campaignPUT/campaigns/{id}Delete campaignDELETE/campaigns/{id}Ad GroupsList ad groupsGET/campaigns/{id}/adgroupsCreate ad groupPOST/campaigns/{id}/adgroupsKeywordsList keywordsGET/campaigns/{cId}/adgroups/{agId}/targetingkeywordsAdd keywordsPOST/campaigns/{cId}/adgroups/{agId}/targetingkeywords/bulkReportsCampaign reportPOST/reports/campaignsAd group reportPOST/reports/campaigns/{id}/adgroupsKeyword reportPOST/reports/campaigns/{cId}/adgroups/{agId}/keywordsSearch term reportPOST/reports/campaigns/{cId}/searchtermsImpression sharePOST/reports/campaigns/{id}/impressionshare
Organization (orgId) โโโ Campaign (Search Results / Search Tab / Today Tab) โโโ Budget & Schedule โโโ Countries/Regions โโโ Ad Groups โโโ Keywords (targeting + negative) โโโ Audience (age, gender, device, etc.) โโโ Creatives (default or Custom Product Pages) โโโ Bid settings
TypePlacementBest ForSearch ResultsTop of search resultsHigh-intent users, brand defenseSearch TabSuggested apps before searchDiscovery, broad reachToday TabToday tab featuredBrand awareness, launches
{ "name": "MyApp - US - Brand", "adamId": 123456789, "countriesOrRegions": ["US"], "budgetAmount": {"amount": "1000", "currency": "USD"}, "dailyBudgetAmount": {"amount": "50", "currency": "USD"}, "supplySources": ["APPSTORE_SEARCH_RESULTS"], "billingEvent": "TAPS", "status": "ENABLED", "startTime": "2026-01-01T00:00:00.000", "endTime": null }
{ "name": "Brand Keywords", "campaignId": 123456, "defaultBidAmount": {"amount": "1.50", "currency": "USD"}, "cpaGoal": {"amount": "5.00", "currency": "USD"}, "startTime": "2026-01-01T00:00:00.000", "targetingDimensions": { "age": {"included": [{"minAge": 18}]}, "gender": {"included": ["M", "F"]}, "deviceClass": {"included": ["IPHONE", "IPAD"]}, "daypart": {"userTime": {"included": [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23]}}, "adminArea": null, "locality": null, "appDownloaders": {"included": [], "excluded": []} }, "automatedKeywordsOptIn": false, "status": "ENABLED" }
TypeBehaviorUse CaseExactQuery = keyword exactlyBrand terms, proven convertersBroadSynonyms, related termsDiscovery, expansionSearch MatchAuto-matched by AppleNew apps, keyword research
{ "text": "meditation app", "matchType": "EXACT", "bidAmount": {"amount": "2.00", "currency": "USD"}, "status": "ACTIVE" }
Brand keywords โ Bid high (defend your brand) Competitor keywords โ Test carefully, monitor CPA Generic keywords โ Start low, increase for winners Discovery (Search Match) โ Low bids, mine for keywords Negative keywords โ Essential to reduce waste
Week 1: Set baseline bids (industry avg or $1-2) โ Week 2: Review search term report - High converts, low bid โ raise bid 20-30% - Low converts, high spend โ lower bid or pause - Irrelevant terms โ add as negative โ Week 3+: Repeat. Target CPA within 20% of goal.
Modern attribution without user tracking. Integrates directly in iOS app. import AdServices func trackAttribution() async { do { // 1. Get attribution token from device let token = try AAAttribution.attributionToken() // 2. Send to Apple's attribution API var request = URLRequest(url: URL(string: "https://api-adservices.apple.com/api/v1/")!) request.httpMethod = "POST" request.setValue("text/plain", forHTTPHeaderField: "Content-Type") request.httpBody = token.data(using: .utf8) let (data, _) = try await URLSession.shared.data(for: request) let attribution = try JSONDecoder().decode(Attribution.self, from: data) // 3. attribution contains: campaignId, adGroupId, keywordId, etc. // Send to your analytics backend } catch { // Not from Apple Search Ads or error } } struct Attribution: Codable { let attribution: Bool let orgId: Int? let campaignId: Int? let adGroupId: Int? let keywordId: Int? let creativeSetId: Int? let conversionType: String? let clickDate: String? }
Privacy-focused attribution for installs. Apple aggregates data, no user-level tracking. // In your app delegate import StoreKit func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Register for attribution SKAdNetwork.updatePostbackConversionValue(0) { error in // Initial registration } return true } // When user completes valuable action (purchase, signup, etc.) func trackConversion(value: Int) { // value 0-63, represents conversion value SKAdNetwork.updatePostbackConversionValue(value) { error in if error == nil { // Updated successfully } } }
ValueMeaningExample0Install onlyApp opened1-10Engagement tierSessions, time in app11-30Feature usageKey feature activated31-50Monetization signalTrial started, content viewed51-63Revenue tierPurchase completed
If using an MMP, they handle AdServices and SKAdNetwork. Follow their SDK docs. Key integration points: Initialize MMP SDK before any tracking Configure SKAdNetwork conversion values in MMP dashboard Link Apple Search Ads account in MMP for cost data Use MMP's deeplink handling for attribution
{ "startTime": "2026-01-01", "endTime": "2026-01-31", "timeZone": "UTC", "granularity": "DAILY", "selector": { "orderBy": [{"field": "localSpend", "sortOrder": "DESCENDING"}], "pagination": {"offset": 0, "limit": 100} }, "returnRowTotals": true, "returnGrandTotals": true }
MetricDescriptionGood RangeTTRTap-through rate5-10%+CVRConversion rate (installs/taps)30-60%CPACost per acquisition< LTV/3CPTCost per tap$0.50-3.00 (varies)ROASReturn on ad spend> 100%Impression Share% of eligible impressions wonTrack trend
Critical for optimization. Shows actual queries that triggered your ads. // POST /reports/campaigns/{campaignId}/searchterms { "startTime": "2026-01-01", "endTime": "2026-01-31", "selector": { "conditions": [ {"field": "impressions", "operator": "GREATER_THAN", "values": ["10"]} ], "orderBy": [{"field": "impressions", "sortOrder": "DESCENDING"}], "pagination": {"offset": 0, "limit": 1000} } } Weekly ritual: Pull search term report High impressions + high CVR โ Add as exact keyword High impressions + low CVR โ Add as negative Irrelevant terms โ Negative immediately
Campaign: [App] - [Country] - Brand โโโ Ad Group: Brand Exact โโโ Keywords: app name, brand terms (exact match) Campaign: [App] - [Country] - Category โโโ Ad Group: Category - Exact โโโ Keywords: category terms (exact match) โโโ Ad Group: Category - Discovery โโโ Search Match enabled, low bid Campaign: [App] - [Country] - Competitor โโโ Ad Group: Competitor Names โโโ Keywords: competitor app names (exact match)
StageBrandCategoryCompetitorDiscoveryLaunch40%40%10%10%Growth20%50%20%10%Scale10%60%25%5%
Start: US, UK, Canada, Australia (English) Expand: Germany, France, Japan, South Korea (localize) Test: Brazil, Mexico, India (high volume, lower CPT) Localization checklist: App Store listing translated Custom Product Pages per country Keywords researched per language Separate campaigns per country (easier optimization)
Create variations of your App Store page for different audiences. // Get available CPPs // GET /apps/{adamId}/customproductpages // Create ad using CPP { "name": "Fitness Ad - Summer Campaign", "adGroupId": 12345, "creativeType": "CUSTOM_PRODUCT_PAGE", "productPageId": "cpp-uuid-here" } Best practices: Create CPP for each major keyword theme Test: Control (default) vs CPP Rotate seasonally (holidays, events)
See scripts.md for complete script library. Key scripts:
#!/usr/bin/env bash set -euo pipefail # SECURITY MANIFEST: # Environment variables accessed: ASA_CLIENT_ID, ASA_TEAM_ID, ASA_KEY_ID, ASA_PRIVATE_KEY # External endpoints called: https://appleid.apple.com/auth/oauth2/token (only) # Local files read: none # Local files written: none # Requires: openssl, jq CLIENT_ID="${ASA_CLIENT_ID}" TEAM_ID="${ASA_TEAM_ID}" KEY_ID="${ASA_KEY_ID}" PRIVATE_KEY="${ASA_PRIVATE_KEY}" # PEM format # Create JWT header HEADER=$(echo -n '{"alg":"ES256","kid":"'$KEY_ID'"}' | base64 | tr -d '=' | tr '/+' '_-' | tr -d '\n') # Create JWT payload NOW=$(date +%s) EXP=$((NOW + 15552000)) # 180 days PAYLOAD=$(echo -n '{"sub":"'$CLIENT_ID'","aud":"https://appleid.apple.com","iat":'$NOW',"exp":'$EXP',"iss":"'$TEAM_ID'"}' | base64 | tr -d '=' | tr '/+' '_-' | tr -d '\n') # Sign with private key SIGNATURE=$(echo -n "$HEADER.$PAYLOAD" | openssl dgst -sha256 -sign <(echo "$PRIVATE_KEY") | base64 | tr -d '=' | tr '/+' '_-' | tr -d '\n') CLIENT_SECRET="$HEADER.$PAYLOAD.$SIGNATURE" # Exchange for access token curl -s -X POST "https://appleid.apple.com/auth/oauth2/token" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=client_credentials&client_id=$CLIENT_ID&client_secret=$CLIENT_SECRET&scope=searchadsorg" \ | jq -r '.access_token'
#!/usr/bin/env bash set -euo pipefail # SECURITY MANIFEST: # Environment variables accessed: ASA_ACCESS_TOKEN, ASA_ORG_ID # External endpoints called: https://api.searchads.apple.com/api/v5/reports/campaigns (only) # Local files read: none # Local files written: stdout (report data) ACCESS_TOKEN="${ASA_ACCESS_TOKEN}" ORG_ID="${ASA_ORG_ID}" TODAY=$(date -u +%Y-%m-%d) YESTERDAY=$(date -u -v-1d +%Y-%m-%d 2>/dev/null || date -u -d "yesterday" +%Y-%m-%d) curl -s -X POST "https://api.searchads.apple.com/api/v5/reports/campaigns" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "X-AP-Context: orgId=$ORG_ID" \ -H "Content-Type: application/json" \ -d '{ "startTime": "'$YESTERDAY'", "endTime": "'$TODAY'", "granularity": "DAILY", "selector": { "orderBy": [{"field": "localSpend", "sortOrder": "DESCENDING"}] }, "returnRowTotals": true, "returnGrandTotals": true }' | jq '.data.reportingDataResponse.row[] | { campaign: .metadata.campaignName, spend: .total.localSpend.amount, impressions: .total.impressions, taps: .total.taps, installs: .total.installs, cpa: (if .total.installs > 0 then (.total.localSpend.amount | tonumber) / .total.installs else "N/A" end) }'
Brand, category, competitor, and discovery keywords in separate campaigns. Mixing makes optimization impossible.
Begin with exact match keywords you're confident about. Use Search Match and broad only for discovery with low bids.
The search term report is gold. Review weekly, add winners as exact, add losers as negatives.
Competitors WILL bid on your brand name. Always have a brand campaign with high bids to protect your real estate.
Implement AdServices for iOS 14.3+. Without attribution, you're optimizing blind.
Mixing countries makes bid optimization nearly impossible. Create separate campaigns per country/region.
Set CPA goals, not just budgets. If CPA is 2x target, pause and investigate before spending more.
Mixing match types in one ad group โ Can't tell what's working. Separate exact, broad, search match. No negative keywords โ Wasting budget on irrelevant searches. Review search terms weekly. Ignoring Search Tab/Today Tab โ Lower intent but cheaper. Good for discovery. Same bid across all keywords โ Brand keywords worth more than generic. Bid accordingly. No attribution integration โ Flying blind. Implement AdServices or MMP. Launching without App Store optimization โ Low conversion rate. Fix ASO first. Bidding on competitors without testing โ Often unprofitable. Test small first. Forgetting timezone in reports โ Data misalignment. Always use UTC or explicit timezone. Not using Custom Product Pages โ Missing easy wins. Create themed pages. Scaling too fast โ CPA spikes when scaling. Increase budget 20-30% at a time.
EndpointData SentPurposehttps://appleid.apple.com/auth/oauth2/tokenClient credentials (JWT)Get access tokenhttps://api.searchads.apple.com/api/v5/*Campaign/keyword dataCampaign managementhttps://api-adservices.apple.com/api/v1/Attribution tokenAttribution data No other data is sent externally.
Data that leaves your machine: Campaign configurations sent to Apple Ads API Attribution tokens sent to Apple (from iOS app) Data that stays local: Credentials in ~/apple-search-ads/credentials.md Reports and analysis Strategy notes This skill does NOT: Store API secrets in plain text (use environment variables) Access user-level data (attribution is aggregated) Make requests to undeclared endpoints
By using this skill, data is sent to Apple's Search Ads API and AdServices. Only install if you trust Apple with your advertising data.
Install with clawhub install <slug> if user confirms: app-store-connect โ manage apps and releases aso โ App Store Optimization analytics โ track metrics and KPIs ios โ iOS development patterns
If useful: clawhub star apple-search-ads Stay updated: clawhub sync
Code helpers, APIs, CLIs, browser automation, testing, and developer operations.
Largest current source with strong distribution and engagement signals.