{
  "schemaVersion": "1.0",
  "item": {
    "slug": "apple-search-ads",
    "name": "Apple Search Ads",
    "source": "tencent",
    "type": "skill",
    "category": "开发工具",
    "sourceUrl": "https://clawhub.ai/ivangdavila/apple-search-ads",
    "canonicalUrl": "https://clawhub.ai/ivangdavila/apple-search-ads",
    "targetPlatform": "OpenClaw"
  },
  "install": {
    "downloadMode": "redirect",
    "downloadUrl": "/downloads/apple-search-ads",
    "sourceDownloadUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=apple-search-ads",
    "sourcePlatform": "tencent",
    "targetPlatform": "OpenClaw",
    "installMethod": "Manual import",
    "extraction": "Extract archive",
    "prerequisites": [
      "OpenClaw"
    ],
    "packageFormat": "ZIP package",
    "includedAssets": [
      "SKILL.md",
      "api-reference.md",
      "ios-integration.md",
      "memory-template.md",
      "scripts.md",
      "setup.md"
    ],
    "primaryDoc": "SKILL.md",
    "quickSetup": [
      "Download the package from Yavira.",
      "Extract the archive and review SKILL.md first.",
      "Import or place the package into your OpenClaw setup."
    ],
    "agentAssist": {
      "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
      "steps": [
        "Download the package from Yavira.",
        "Extract it into a folder your agent can access.",
        "Paste one of the prompts below and point your agent at the extracted folder."
      ],
      "prompts": [
        {
          "label": "New install",
          "body": "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."
        },
        {
          "label": "Upgrade existing",
          "body": "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."
        }
      ]
    },
    "sourceHealth": {
      "source": "tencent",
      "status": "healthy",
      "reason": "direct_download_ok",
      "recommendedAction": "download",
      "checkedAt": "2026-04-23T16:43:11.935Z",
      "expiresAt": "2026-04-30T16:43:11.935Z",
      "httpStatus": 200,
      "finalUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=4claw-imageboard",
      "contentType": "application/zip",
      "probeMethod": "head",
      "details": {
        "probeUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=4claw-imageboard",
        "contentDisposition": "attachment; filename=\"4claw-imageboard-1.0.1.zip\"",
        "redirectLocation": null,
        "bodySnippet": null
      },
      "scope": "source",
      "summary": "Source download looks usable.",
      "detail": "Yavira can redirect you to the upstream package for this source.",
      "primaryActionLabel": "Download for OpenClaw",
      "primaryActionHref": "/downloads/apple-search-ads"
    },
    "validation": {
      "installChecklist": [
        "Use the Yavira download entry.",
        "Review SKILL.md after the package is downloaded.",
        "Confirm the extracted package contains the expected setup assets."
      ],
      "postInstallChecks": [
        "Confirm the extracted package includes the expected docs or setup files.",
        "Validate the skill or prompts are available in your target agent workspace.",
        "Capture any manual follow-up steps the agent could not complete."
      ]
    },
    "downloadPageUrl": "https://openagent3.xyz/downloads/apple-search-ads",
    "agentPageUrl": "https://openagent3.xyz/skills/apple-search-ads/agent",
    "manifestUrl": "https://openagent3.xyz/skills/apple-search-ads/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/apple-search-ads/agent.md"
  },
  "agentAssist": {
    "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
    "steps": [
      "Download the package from Yavira.",
      "Extract it into a folder your agent can access.",
      "Paste one of the prompts below and point your agent at the extracted folder."
    ],
    "prompts": [
      {
        "label": "New install",
        "body": "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."
      },
      {
        "label": "Upgrade existing",
        "body": "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."
      }
    ]
  },
  "documentation": {
    "source": "clawhub",
    "primaryDoc": "SKILL.md",
    "sections": [
      {
        "title": "Apple Search Ads 🍎",
        "body": "Complete toolkit for Apple Search Ads: Campaign Management API v5, attribution integration (AdServices + SKAdNetwork), bid optimization, and strategic recommendations."
      },
      {
        "title": "What's New in v1.0.0",
        "body": "Full Campaign Management API v5 coverage\niOS app integration (AdServices framework)\nSKAdNetwork 4.0 support\nAutomated reporting scripts\nBid optimization strategies\nMulti-country campaign patterns"
      },
      {
        "title": "Contents",
        "body": "Setup\nWhen to Use\nArchitecture\nAPI Essentials\nCampaign Structure\nKeywords & Bidding\nAttribution Integration\nReports & Analytics\nStrategy Playbook\nScripts & Automation\nCommon Traps"
      },
      {
        "title": "Setup",
        "body": "On first use, read setup.md for integration guidelines."
      },
      {
        "title": "When to Use",
        "body": "User needs to run Apple Search Ads for iOS apps. Agent handles campaign creation, bid optimization, attribution tracking, performance analysis, and strategic recommendations."
      },
      {
        "title": "Architecture",
        "body": "Memory lives in ~/apple-search-ads/. See memory-template.md for structure.\n\n~/apple-search-ads/\n├── memory.md          # Active campaigns, preferences, learnings\n├── credentials.md     # OAuth config (NEVER commit real secrets)\n├── campaigns/         # Campaign-specific notes and performance\n│   └── {app-id}/\n├── reports/           # Generated reports\n└── scripts/           # Custom automation"
      },
      {
        "title": "Quick Reference",
        "body": "TopicFileSetup processsetup.mdMemory templatememory-template.mdAPI endpointsapi-reference.mdiOS integrationios-integration.mdStrategy guidestrategy.mdScript libraryscripts.md"
      },
      {
        "title": "Authentication (OAuth 2.0)",
        "body": "Apple Ads API uses OAuth with client credentials. Generate credentials at:\nhttps://app.searchads.apple.com/cm/app/settings/apicertificates\n\n# 1. Generate client secret (JWT signed with private key)\n# Header\n{\n  \"alg\": \"ES256\",\n  \"kid\": \"{KEY_ID}\"\n}\n# Payload\n{\n  \"sub\": \"{CLIENT_ID}\",\n  \"aud\": \"https://appleid.apple.com\",\n  \"iat\": {CURRENT_TIMESTAMP},\n  \"exp\": {TIMESTAMP_+180_DAYS},\n  \"iss\": \"{TEAM_ID}\"\n}\n\n# 2. Exchange for access token\ncurl -X POST \"https://appleid.apple.com/auth/oauth2/token\" \\\n  -H \"Content-Type: application/x-www-form-urlencoded\" \\\n  -d \"grant_type=client_credentials\" \\\n  -d \"client_id={CLIENT_ID}\" \\\n  -d \"client_secret={CLIENT_SECRET}\" \\\n  -d \"scope=searchadsorg\"\n\n# Response contains access_token (valid 1 hour)"
      },
      {
        "title": "Base URL & Headers",
        "body": "Base URL: https://api.searchads.apple.com/api/v5\nHeaders:\n  Authorization: Bearer {ACCESS_TOKEN}\n  X-AP-Context: orgId={ORG_ID}\n  Content-Type: application/json"
      },
      {
        "title": "Core Endpoints",
        "body": "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"
      },
      {
        "title": "Hierarchy",
        "body": "Organization (orgId)\n└── Campaign (Search Results / Search Tab / Today Tab)\n    ├── Budget & Schedule\n    ├── Countries/Regions\n    └── Ad Groups\n        ├── Keywords (targeting + negative)\n        ├── Audience (age, gender, device, etc.)\n        ├── Creatives (default or Custom Product Pages)\n        └── Bid settings"
      },
      {
        "title": "Campaign Types",
        "body": "TypePlacementBest ForSearch ResultsTop of search resultsHigh-intent users, brand defenseSearch TabSuggested apps before searchDiscovery, broad reachToday TabToday tab featuredBrand awareness, launches"
      },
      {
        "title": "Campaign Object",
        "body": "{\n  \"name\": \"MyApp - US - Brand\",\n  \"adamId\": 123456789,\n  \"countriesOrRegions\": [\"US\"],\n  \"budgetAmount\": {\"amount\": \"1000\", \"currency\": \"USD\"},\n  \"dailyBudgetAmount\": {\"amount\": \"50\", \"currency\": \"USD\"},\n  \"supplySources\": [\"APPSTORE_SEARCH_RESULTS\"],\n  \"billingEvent\": \"TAPS\",\n  \"status\": \"ENABLED\",\n  \"startTime\": \"2026-01-01T00:00:00.000\",\n  \"endTime\": null\n}"
      },
      {
        "title": "Ad Group Object",
        "body": "{\n  \"name\": \"Brand Keywords\",\n  \"campaignId\": 123456,\n  \"defaultBidAmount\": {\"amount\": \"1.50\", \"currency\": \"USD\"},\n  \"cpaGoal\": {\"amount\": \"5.00\", \"currency\": \"USD\"},\n  \"startTime\": \"2026-01-01T00:00:00.000\",\n  \"targetingDimensions\": {\n    \"age\": {\"included\": [{\"minAge\": 18}]},\n    \"gender\": {\"included\": [\"M\", \"F\"]},\n    \"deviceClass\": {\"included\": [\"IPHONE\", \"IPAD\"]},\n    \"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]}},\n    \"adminArea\": null,\n    \"locality\": null,\n    \"appDownloaders\": {\"included\": [], \"excluded\": []}\n  },\n  \"automatedKeywordsOptIn\": false,\n  \"status\": \"ENABLED\"\n}"
      },
      {
        "title": "Match Types",
        "body": "TypeBehaviorUse CaseExactQuery = keyword exactlyBrand terms, proven convertersBroadSynonyms, related termsDiscovery, expansionSearch MatchAuto-matched by AppleNew apps, keyword research"
      },
      {
        "title": "Keyword Object",
        "body": "{\n  \"text\": \"meditation app\",\n  \"matchType\": \"EXACT\",\n  \"bidAmount\": {\"amount\": \"2.00\", \"currency\": \"USD\"},\n  \"status\": \"ACTIVE\"\n}"
      },
      {
        "title": "Bid Strategy Rules",
        "body": "Brand keywords → Bid high (defend your brand)\nCompetitor keywords → Test carefully, monitor CPA\nGeneric keywords → Start low, increase for winners\nDiscovery (Search Match) → Low bids, mine for keywords\nNegative keywords → Essential to reduce waste"
      },
      {
        "title": "Bid Optimization Loop",
        "body": "Week 1: Set baseline bids (industry avg or $1-2)\n        ↓\nWeek 2: Review search term report\n        - High converts, low bid → raise bid 20-30%\n        - Low converts, high spend → lower bid or pause\n        - Irrelevant terms → add as negative\n        ↓\nWeek 3+: Repeat. Target CPA within 20% of goal."
      },
      {
        "title": "AdServices Framework (iOS 14.3+)",
        "body": "Modern attribution without user tracking. Integrates directly in iOS app.\n\nimport AdServices\n\nfunc trackAttribution() async {\n    do {\n        // 1. Get attribution token from device\n        let token = try AAAttribution.attributionToken()\n        \n        // 2. Send to Apple's attribution API\n        var request = URLRequest(url: URL(string: \"https://api-adservices.apple.com/api/v1/\")!)\n        request.httpMethod = \"POST\"\n        request.setValue(\"text/plain\", forHTTPHeaderField: \"Content-Type\")\n        request.httpBody = token.data(using: .utf8)\n        \n        let (data, _) = try await URLSession.shared.data(for: request)\n        let attribution = try JSONDecoder().decode(Attribution.self, from: data)\n        \n        // 3. attribution contains: campaignId, adGroupId, keywordId, etc.\n        // Send to your analytics backend\n        \n    } catch {\n        // Not from Apple Search Ads or error\n    }\n}\n\nstruct Attribution: Codable {\n    let attribution: Bool\n    let orgId: Int?\n    let campaignId: Int?\n    let adGroupId: Int?\n    let keywordId: Int?\n    let creativeSetId: Int?\n    let conversionType: String?\n    let clickDate: String?\n}"
      },
      {
        "title": "SKAdNetwork 4.0",
        "body": "Privacy-focused attribution for installs. Apple aggregates data, no user-level tracking.\n\n// In your app delegate\nimport StoreKit\n\nfunc application(_ application: UIApplication, \n                 didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {\n    // Register for attribution\n    SKAdNetwork.updatePostbackConversionValue(0) { error in\n        // Initial registration\n    }\n    return true\n}\n\n// When user completes valuable action (purchase, signup, etc.)\nfunc trackConversion(value: Int) {\n    // value 0-63, represents conversion value\n    SKAdNetwork.updatePostbackConversionValue(value) { error in\n        if error == nil {\n            // Updated successfully\n        }\n    }\n}"
      },
      {
        "title": "SKAdNetwork Conversion Value Strategy",
        "body": "ValueMeaningExample0Install onlyApp opened1-10Engagement tierSessions, time in app11-30Feature usageKey feature activated31-50Monetization signalTrial started, content viewed51-63Revenue tierPurchase completed"
      },
      {
        "title": "MMP Integration (AppsFlyer, Adjust, etc.)",
        "body": "If using an MMP, they handle AdServices and SKAdNetwork. Follow their SDK docs. Key integration points:\n\nInitialize MMP SDK before any tracking\nConfigure SKAdNetwork conversion values in MMP dashboard\nLink Apple Search Ads account in MMP for cost data\nUse MMP's deeplink handling for attribution"
      },
      {
        "title": "Campaign Report Request",
        "body": "{\n  \"startTime\": \"2026-01-01\",\n  \"endTime\": \"2026-01-31\",\n  \"timeZone\": \"UTC\",\n  \"granularity\": \"DAILY\",\n  \"selector\": {\n    \"orderBy\": [{\"field\": \"localSpend\", \"sortOrder\": \"DESCENDING\"}],\n    \"pagination\": {\"offset\": 0, \"limit\": 100}\n  },\n  \"returnRowTotals\": true,\n  \"returnGrandTotals\": true\n}"
      },
      {
        "title": "Key Metrics",
        "body": "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"
      },
      {
        "title": "Search Term Report",
        "body": "Critical for optimization. Shows actual queries that triggered your ads.\n\n// POST /reports/campaigns/{campaignId}/searchterms\n{\n  \"startTime\": \"2026-01-01\",\n  \"endTime\": \"2026-01-31\",\n  \"selector\": {\n    \"conditions\": [\n      {\"field\": \"impressions\", \"operator\": \"GREATER_THAN\", \"values\": [\"10\"]}\n    ],\n    \"orderBy\": [{\"field\": \"impressions\", \"sortOrder\": \"DESCENDING\"}],\n    \"pagination\": {\"offset\": 0, \"limit\": 1000}\n  }\n}\n\nWeekly ritual:\n\nPull search term report\nHigh impressions + high CVR → Add as exact keyword\nHigh impressions + low CVR → Add as negative\nIrrelevant terms → Negative immediately"
      },
      {
        "title": "Campaign Structure (Recommended)",
        "body": "Campaign: [App] - [Country] - Brand\n  └── Ad Group: Brand Exact\n      └── Keywords: app name, brand terms (exact match)\n\nCampaign: [App] - [Country] - Category\n  └── Ad Group: Category - Exact\n      └── Keywords: category terms (exact match)\n  └── Ad Group: Category - Discovery\n      └── Search Match enabled, low bid\n\nCampaign: [App] - [Country] - Competitor\n  └── Ad Group: Competitor Names\n      └── Keywords: competitor app names (exact match)"
      },
      {
        "title": "Budget Allocation",
        "body": "StageBrandCategoryCompetitorDiscoveryLaunch40%40%10%10%Growth20%50%20%10%Scale10%60%25%5%"
      },
      {
        "title": "Multi-Country Expansion",
        "body": "Start: US, UK, Canada, Australia (English)\nExpand: Germany, France, Japan, South Korea (localize)\nTest: Brazil, Mexico, India (high volume, lower CPT)\n\nLocalization checklist:\n\nApp Store listing translated\n Custom Product Pages per country\n Keywords researched per language\n Separate campaigns per country (easier optimization)"
      },
      {
        "title": "Custom Product Pages (CPP)",
        "body": "Create variations of your App Store page for different audiences.\n\n// Get available CPPs\n// GET /apps/{adamId}/customproductpages\n\n// Create ad using CPP\n{\n  \"name\": \"Fitness Ad - Summer Campaign\",\n  \"adGroupId\": 12345,\n  \"creativeType\": \"CUSTOM_PRODUCT_PAGE\",\n  \"productPageId\": \"cpp-uuid-here\"\n}\n\nBest practices:\n\nCreate CPP for each major keyword theme\nTest: Control (default) vs CPP\nRotate seasonally (holidays, events)"
      },
      {
        "title": "Scripts & Automation",
        "body": "See scripts.md for complete script library. Key scripts:"
      },
      {
        "title": "Get Access Token",
        "body": "#!/usr/bin/env bash\nset -euo pipefail\n\n# SECURITY MANIFEST:\n# Environment variables accessed: ASA_CLIENT_ID, ASA_TEAM_ID, ASA_KEY_ID, ASA_PRIVATE_KEY\n# External endpoints called: https://appleid.apple.com/auth/oauth2/token (only)\n# Local files read: none\n# Local files written: none\n\n# Requires: openssl, jq\n\nCLIENT_ID=\"${ASA_CLIENT_ID}\"\nTEAM_ID=\"${ASA_TEAM_ID}\"\nKEY_ID=\"${ASA_KEY_ID}\"\nPRIVATE_KEY=\"${ASA_PRIVATE_KEY}\"  # PEM format\n\n# Create JWT header\nHEADER=$(echo -n '{\"alg\":\"ES256\",\"kid\":\"'$KEY_ID'\"}' | base64 | tr -d '=' | tr '/+' '_-' | tr -d '\\n')\n\n# Create JWT payload\nNOW=$(date +%s)\nEXP=$((NOW + 15552000))  # 180 days\nPAYLOAD=$(echo -n '{\"sub\":\"'$CLIENT_ID'\",\"aud\":\"https://appleid.apple.com\",\"iat\":'$NOW',\"exp\":'$EXP',\"iss\":\"'$TEAM_ID'\"}' | base64 | tr -d '=' | tr '/+' '_-' | tr -d '\\n')\n\n# Sign with private key\nSIGNATURE=$(echo -n \"$HEADER.$PAYLOAD\" | openssl dgst -sha256 -sign <(echo \"$PRIVATE_KEY\") | base64 | tr -d '=' | tr '/+' '_-' | tr -d '\\n')\n\nCLIENT_SECRET=\"$HEADER.$PAYLOAD.$SIGNATURE\"\n\n# Exchange for access token\ncurl -s -X POST \"https://appleid.apple.com/auth/oauth2/token\" \\\n  -H \"Content-Type: application/x-www-form-urlencoded\" \\\n  -d \"grant_type=client_credentials&client_id=$CLIENT_ID&client_secret=$CLIENT_SECRET&scope=searchadsorg\" \\\n  | jq -r '.access_token'"
      },
      {
        "title": "Daily Performance Report",
        "body": "#!/usr/bin/env bash\nset -euo pipefail\n\n# SECURITY MANIFEST:\n# Environment variables accessed: ASA_ACCESS_TOKEN, ASA_ORG_ID\n# External endpoints called: https://api.searchads.apple.com/api/v5/reports/campaigns (only)\n# Local files read: none\n# Local files written: stdout (report data)\n\nACCESS_TOKEN=\"${ASA_ACCESS_TOKEN}\"\nORG_ID=\"${ASA_ORG_ID}\"\n\nTODAY=$(date -u +%Y-%m-%d)\nYESTERDAY=$(date -u -v-1d +%Y-%m-%d 2>/dev/null || date -u -d \"yesterday\" +%Y-%m-%d)\n\ncurl -s -X POST \"https://api.searchads.apple.com/api/v5/reports/campaigns\" \\\n  -H \"Authorization: Bearer $ACCESS_TOKEN\" \\\n  -H \"X-AP-Context: orgId=$ORG_ID\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"startTime\": \"'$YESTERDAY'\",\n    \"endTime\": \"'$TODAY'\",\n    \"granularity\": \"DAILY\",\n    \"selector\": {\n      \"orderBy\": [{\"field\": \"localSpend\", \"sortOrder\": \"DESCENDING\"}]\n    },\n    \"returnRowTotals\": true,\n    \"returnGrandTotals\": true\n  }' | jq '.data.reportingDataResponse.row[] | {\n    campaign: .metadata.campaignName,\n    spend: .total.localSpend.amount,\n    impressions: .total.impressions,\n    taps: .total.taps,\n    installs: .total.installs,\n    cpa: (if .total.installs > 0 then (.total.localSpend.amount | tonumber) / .total.installs else \"N/A\" end)\n  }'"
      },
      {
        "title": "1. Separate Campaigns by Intent",
        "body": "Brand, category, competitor, and discovery keywords in separate campaigns. Mixing makes optimization impossible."
      },
      {
        "title": "2. Start Exact, Expand Broad",
        "body": "Begin with exact match keywords you're confident about. Use Search Match and broad only for discovery with low bids."
      },
      {
        "title": "3. Mine Search Terms Weekly",
        "body": "The search term report is gold. Review weekly, add winners as exact, add losers as negatives."
      },
      {
        "title": "4. Defend Your Brand",
        "body": "Competitors WILL bid on your brand name. Always have a brand campaign with high bids to protect your real estate."
      },
      {
        "title": "5. Track Attribution Properly",
        "body": "Implement AdServices for iOS 14.3+. Without attribution, you're optimizing blind."
      },
      {
        "title": "6. One Country Per Campaign",
        "body": "Mixing countries makes bid optimization nearly impossible. Create separate campaigns per country/region."
      },
      {
        "title": "7. Budget to CPA, Not Spend",
        "body": "Set CPA goals, not just budgets. If CPA is 2x target, pause and investigate before spending more."
      },
      {
        "title": "Common Traps",
        "body": "Mixing match types in one ad group → Can't tell what's working. Separate exact, broad, search match.\nNo negative keywords → Wasting budget on irrelevant searches. Review search terms weekly.\nIgnoring Search Tab/Today Tab → Lower intent but cheaper. Good for discovery.\nSame bid across all keywords → Brand keywords worth more than generic. Bid accordingly.\nNo attribution integration → Flying blind. Implement AdServices or MMP.\nLaunching without App Store optimization → Low conversion rate. Fix ASO first.\nBidding on competitors without testing → Often unprofitable. Test small first.\nForgetting timezone in reports → Data misalignment. Always use UTC or explicit timezone.\nNot using Custom Product Pages → Missing easy wins. Create themed pages.\nScaling too fast → CPA spikes when scaling. Increase budget 20-30% at a time."
      },
      {
        "title": "External Endpoints",
        "body": "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\n\nNo other data is sent externally."
      },
      {
        "title": "Security & Privacy",
        "body": "Data that leaves your machine:\n\nCampaign configurations sent to Apple Ads API\nAttribution tokens sent to Apple (from iOS app)\n\nData that stays local:\n\nCredentials in ~/apple-search-ads/credentials.md\nReports and analysis\nStrategy notes\n\nThis skill does NOT:\n\nStore API secrets in plain text (use environment variables)\nAccess user-level data (attribution is aggregated)\nMake requests to undeclared endpoints"
      },
      {
        "title": "Trust",
        "body": "By using this skill, data is sent to Apple's Search Ads API and AdServices.\nOnly install if you trust Apple with your advertising data."
      },
      {
        "title": "Related Skills",
        "body": "Install with clawhub install <slug> if user confirms:\n\napp-store-connect — manage apps and releases\naso — App Store Optimization\nanalytics — track metrics and KPIs\nios — iOS development patterns"
      },
      {
        "title": "Feedback",
        "body": "If useful: clawhub star apple-search-ads\nStay updated: clawhub sync"
      }
    ],
    "body": "Apple Search Ads 🍎\n\nComplete toolkit for Apple Search Ads: Campaign Management API v5, attribution integration (AdServices + SKAdNetwork), bid optimization, and strategic recommendations.\n\nWhat's New in v1.0.0\nFull Campaign Management API v5 coverage\niOS app integration (AdServices framework)\nSKAdNetwork 4.0 support\nAutomated reporting scripts\nBid optimization strategies\nMulti-country campaign patterns\nContents\nSetup\nWhen to Use\nArchitecture\nAPI Essentials\nCampaign Structure\nKeywords & Bidding\nAttribution Integration\nReports & Analytics\nStrategy Playbook\nScripts & Automation\nCommon Traps\nSetup\n\nOn first use, read setup.md for integration guidelines.\n\nWhen to Use\n\nUser needs to run Apple Search Ads for iOS apps. Agent handles campaign creation, bid optimization, attribution tracking, performance analysis, and strategic recommendations.\n\nArchitecture\n\nMemory lives in ~/apple-search-ads/. See memory-template.md for structure.\n\n~/apple-search-ads/\n├── memory.md          # Active campaigns, preferences, learnings\n├── credentials.md     # OAuth config (NEVER commit real secrets)\n├── campaigns/         # Campaign-specific notes and performance\n│   └── {app-id}/\n├── reports/           # Generated reports\n└── scripts/           # Custom automation\n\nQuick Reference\nTopic\tFile\nSetup process\tsetup.md\nMemory template\tmemory-template.md\nAPI endpoints\tapi-reference.md\niOS integration\tios-integration.md\nStrategy guide\tstrategy.md\nScript library\tscripts.md\nAPI Essentials\nAuthentication (OAuth 2.0)\n\nApple Ads API uses OAuth with client credentials. Generate credentials at: https://app.searchads.apple.com/cm/app/settings/apicertificates\n\n# 1. Generate client secret (JWT signed with private key)\n# Header\n{\n  \"alg\": \"ES256\",\n  \"kid\": \"{KEY_ID}\"\n}\n# Payload\n{\n  \"sub\": \"{CLIENT_ID}\",\n  \"aud\": \"https://appleid.apple.com\",\n  \"iat\": {CURRENT_TIMESTAMP},\n  \"exp\": {TIMESTAMP_+180_DAYS},\n  \"iss\": \"{TEAM_ID}\"\n}\n\n# 2. Exchange for access token\ncurl -X POST \"https://appleid.apple.com/auth/oauth2/token\" \\\n  -H \"Content-Type: application/x-www-form-urlencoded\" \\\n  -d \"grant_type=client_credentials\" \\\n  -d \"client_id={CLIENT_ID}\" \\\n  -d \"client_secret={CLIENT_SECRET}\" \\\n  -d \"scope=searchadsorg\"\n\n# Response contains access_token (valid 1 hour)\n\nBase URL & Headers\nBase URL: https://api.searchads.apple.com/api/v5\nHeaders:\n  Authorization: Bearer {ACCESS_TOKEN}\n  X-AP-Context: orgId={ORG_ID}\n  Content-Type: application/json\n\nCore Endpoints\nResource\tMethod\tEndpoint\nApps\t\t\nSearch apps\tPOST\t/search/apps\nApp eligibility\tGET\t/apps/{adamId}/eligibilities\nCampaigns\t\t\nList campaigns\tGET\t/campaigns\nCreate campaign\tPOST\t/campaigns\nUpdate campaign\tPUT\t/campaigns/{id}\nDelete campaign\tDELETE\t/campaigns/{id}\nAd Groups\t\t\nList ad groups\tGET\t/campaigns/{id}/adgroups\nCreate ad group\tPOST\t/campaigns/{id}/adgroups\nKeywords\t\t\nList keywords\tGET\t/campaigns/{cId}/adgroups/{agId}/targetingkeywords\nAdd keywords\tPOST\t/campaigns/{cId}/adgroups/{agId}/targetingkeywords/bulk\nReports\t\t\nCampaign report\tPOST\t/reports/campaigns\nAd group report\tPOST\t/reports/campaigns/{id}/adgroups\nKeyword report\tPOST\t/reports/campaigns/{cId}/adgroups/{agId}/keywords\nSearch term report\tPOST\t/reports/campaigns/{cId}/searchterms\nImpression share\tPOST\t/reports/campaigns/{id}/impressionshare\nCampaign Structure\nHierarchy\nOrganization (orgId)\n└── Campaign (Search Results / Search Tab / Today Tab)\n    ├── Budget & Schedule\n    ├── Countries/Regions\n    └── Ad Groups\n        ├── Keywords (targeting + negative)\n        ├── Audience (age, gender, device, etc.)\n        ├── Creatives (default or Custom Product Pages)\n        └── Bid settings\n\nCampaign Types\nType\tPlacement\tBest For\nSearch Results\tTop of search results\tHigh-intent users, brand defense\nSearch Tab\tSuggested apps before search\tDiscovery, broad reach\nToday Tab\tToday tab featured\tBrand awareness, launches\nCampaign Object\n{\n  \"name\": \"MyApp - US - Brand\",\n  \"adamId\": 123456789,\n  \"countriesOrRegions\": [\"US\"],\n  \"budgetAmount\": {\"amount\": \"1000\", \"currency\": \"USD\"},\n  \"dailyBudgetAmount\": {\"amount\": \"50\", \"currency\": \"USD\"},\n  \"supplySources\": [\"APPSTORE_SEARCH_RESULTS\"],\n  \"billingEvent\": \"TAPS\",\n  \"status\": \"ENABLED\",\n  \"startTime\": \"2026-01-01T00:00:00.000\",\n  \"endTime\": null\n}\n\nAd Group Object\n{\n  \"name\": \"Brand Keywords\",\n  \"campaignId\": 123456,\n  \"defaultBidAmount\": {\"amount\": \"1.50\", \"currency\": \"USD\"},\n  \"cpaGoal\": {\"amount\": \"5.00\", \"currency\": \"USD\"},\n  \"startTime\": \"2026-01-01T00:00:00.000\",\n  \"targetingDimensions\": {\n    \"age\": {\"included\": [{\"minAge\": 18}]},\n    \"gender\": {\"included\": [\"M\", \"F\"]},\n    \"deviceClass\": {\"included\": [\"IPHONE\", \"IPAD\"]},\n    \"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]}},\n    \"adminArea\": null,\n    \"locality\": null,\n    \"appDownloaders\": {\"included\": [], \"excluded\": []}\n  },\n  \"automatedKeywordsOptIn\": false,\n  \"status\": \"ENABLED\"\n}\n\nKeywords & Bidding\nMatch Types\nType\tBehavior\tUse Case\nExact\tQuery = keyword exactly\tBrand terms, proven converters\nBroad\tSynonyms, related terms\tDiscovery, expansion\nSearch Match\tAuto-matched by Apple\tNew apps, keyword research\nKeyword Object\n{\n  \"text\": \"meditation app\",\n  \"matchType\": \"EXACT\",\n  \"bidAmount\": {\"amount\": \"2.00\", \"currency\": \"USD\"},\n  \"status\": \"ACTIVE\"\n}\n\nBid Strategy Rules\nBrand keywords → Bid high (defend your brand)\nCompetitor keywords → Test carefully, monitor CPA\nGeneric keywords → Start low, increase for winners\nDiscovery (Search Match) → Low bids, mine for keywords\nNegative keywords → Essential to reduce waste\nBid Optimization Loop\nWeek 1: Set baseline bids (industry avg or $1-2)\n        ↓\nWeek 2: Review search term report\n        - High converts, low bid → raise bid 20-30%\n        - Low converts, high spend → lower bid or pause\n        - Irrelevant terms → add as negative\n        ↓\nWeek 3+: Repeat. Target CPA within 20% of goal.\n\nAttribution Integration\nAdServices Framework (iOS 14.3+)\n\nModern attribution without user tracking. Integrates directly in iOS app.\n\nimport AdServices\n\nfunc trackAttribution() async {\n    do {\n        // 1. Get attribution token from device\n        let token = try AAAttribution.attributionToken()\n        \n        // 2. Send to Apple's attribution API\n        var request = URLRequest(url: URL(string: \"https://api-adservices.apple.com/api/v1/\")!)\n        request.httpMethod = \"POST\"\n        request.setValue(\"text/plain\", forHTTPHeaderField: \"Content-Type\")\n        request.httpBody = token.data(using: .utf8)\n        \n        let (data, _) = try await URLSession.shared.data(for: request)\n        let attribution = try JSONDecoder().decode(Attribution.self, from: data)\n        \n        // 3. attribution contains: campaignId, adGroupId, keywordId, etc.\n        // Send to your analytics backend\n        \n    } catch {\n        // Not from Apple Search Ads or error\n    }\n}\n\nstruct Attribution: Codable {\n    let attribution: Bool\n    let orgId: Int?\n    let campaignId: Int?\n    let adGroupId: Int?\n    let keywordId: Int?\n    let creativeSetId: Int?\n    let conversionType: String?\n    let clickDate: String?\n}\n\nSKAdNetwork 4.0\n\nPrivacy-focused attribution for installs. Apple aggregates data, no user-level tracking.\n\n// In your app delegate\nimport StoreKit\n\nfunc application(_ application: UIApplication, \n                 didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {\n    // Register for attribution\n    SKAdNetwork.updatePostbackConversionValue(0) { error in\n        // Initial registration\n    }\n    return true\n}\n\n// When user completes valuable action (purchase, signup, etc.)\nfunc trackConversion(value: Int) {\n    // value 0-63, represents conversion value\n    SKAdNetwork.updatePostbackConversionValue(value) { error in\n        if error == nil {\n            // Updated successfully\n        }\n    }\n}\n\nSKAdNetwork Conversion Value Strategy\nValue\tMeaning\tExample\n0\tInstall only\tApp opened\n1-10\tEngagement tier\tSessions, time in app\n11-30\tFeature usage\tKey feature activated\n31-50\tMonetization signal\tTrial started, content viewed\n51-63\tRevenue tier\tPurchase completed\nMMP Integration (AppsFlyer, Adjust, etc.)\n\nIf using an MMP, they handle AdServices and SKAdNetwork. Follow their SDK docs. Key integration points:\n\nInitialize MMP SDK before any tracking\nConfigure SKAdNetwork conversion values in MMP dashboard\nLink Apple Search Ads account in MMP for cost data\nUse MMP's deeplink handling for attribution\nReports & Analytics\nCampaign Report Request\n{\n  \"startTime\": \"2026-01-01\",\n  \"endTime\": \"2026-01-31\",\n  \"timeZone\": \"UTC\",\n  \"granularity\": \"DAILY\",\n  \"selector\": {\n    \"orderBy\": [{\"field\": \"localSpend\", \"sortOrder\": \"DESCENDING\"}],\n    \"pagination\": {\"offset\": 0, \"limit\": 100}\n  },\n  \"returnRowTotals\": true,\n  \"returnGrandTotals\": true\n}\n\nKey Metrics\nMetric\tDescription\tGood Range\nTTR\tTap-through rate\t5-10%+\nCVR\tConversion rate (installs/taps)\t30-60%\nCPA\tCost per acquisition\t< LTV/3\nCPT\tCost per tap\t$0.50-3.00 (varies)\nROAS\tReturn on ad spend\t> 100%\nImpression Share\t% of eligible impressions won\tTrack trend\nSearch Term Report\n\nCritical for optimization. Shows actual queries that triggered your ads.\n\n// POST /reports/campaigns/{campaignId}/searchterms\n{\n  \"startTime\": \"2026-01-01\",\n  \"endTime\": \"2026-01-31\",\n  \"selector\": {\n    \"conditions\": [\n      {\"field\": \"impressions\", \"operator\": \"GREATER_THAN\", \"values\": [\"10\"]}\n    ],\n    \"orderBy\": [{\"field\": \"impressions\", \"sortOrder\": \"DESCENDING\"}],\n    \"pagination\": {\"offset\": 0, \"limit\": 1000}\n  }\n}\n\n\nWeekly ritual:\n\nPull search term report\nHigh impressions + high CVR → Add as exact keyword\nHigh impressions + low CVR → Add as negative\nIrrelevant terms → Negative immediately\nStrategy Playbook\nCampaign Structure (Recommended)\nCampaign: [App] - [Country] - Brand\n  └── Ad Group: Brand Exact\n      └── Keywords: app name, brand terms (exact match)\n\nCampaign: [App] - [Country] - Category\n  └── Ad Group: Category - Exact\n      └── Keywords: category terms (exact match)\n  └── Ad Group: Category - Discovery\n      └── Search Match enabled, low bid\n\nCampaign: [App] - [Country] - Competitor\n  └── Ad Group: Competitor Names\n      └── Keywords: competitor app names (exact match)\n\nBudget Allocation\nStage\tBrand\tCategory\tCompetitor\tDiscovery\nLaunch\t40%\t40%\t10%\t10%\nGrowth\t20%\t50%\t20%\t10%\nScale\t10%\t60%\t25%\t5%\nMulti-Country Expansion\nStart: US, UK, Canada, Australia (English)\nExpand: Germany, France, Japan, South Korea (localize)\nTest: Brazil, Mexico, India (high volume, lower CPT)\n\nLocalization checklist:\n\n App Store listing translated\n Custom Product Pages per country\n Keywords researched per language\n Separate campaigns per country (easier optimization)\nCustom Product Pages (CPP)\n\nCreate variations of your App Store page for different audiences.\n\n// Get available CPPs\n// GET /apps/{adamId}/customproductpages\n\n// Create ad using CPP\n{\n  \"name\": \"Fitness Ad - Summer Campaign\",\n  \"adGroupId\": 12345,\n  \"creativeType\": \"CUSTOM_PRODUCT_PAGE\",\n  \"productPageId\": \"cpp-uuid-here\"\n}\n\n\nBest practices:\n\nCreate CPP for each major keyword theme\nTest: Control (default) vs CPP\nRotate seasonally (holidays, events)\nScripts & Automation\n\nSee scripts.md for complete script library. Key scripts:\n\nGet Access Token\n#!/usr/bin/env bash\nset -euo pipefail\n\n# SECURITY MANIFEST:\n# Environment variables accessed: ASA_CLIENT_ID, ASA_TEAM_ID, ASA_KEY_ID, ASA_PRIVATE_KEY\n# External endpoints called: https://appleid.apple.com/auth/oauth2/token (only)\n# Local files read: none\n# Local files written: none\n\n# Requires: openssl, jq\n\nCLIENT_ID=\"${ASA_CLIENT_ID}\"\nTEAM_ID=\"${ASA_TEAM_ID}\"\nKEY_ID=\"${ASA_KEY_ID}\"\nPRIVATE_KEY=\"${ASA_PRIVATE_KEY}\"  # PEM format\n\n# Create JWT header\nHEADER=$(echo -n '{\"alg\":\"ES256\",\"kid\":\"'$KEY_ID'\"}' | base64 | tr -d '=' | tr '/+' '_-' | tr -d '\\n')\n\n# Create JWT payload\nNOW=$(date +%s)\nEXP=$((NOW + 15552000))  # 180 days\nPAYLOAD=$(echo -n '{\"sub\":\"'$CLIENT_ID'\",\"aud\":\"https://appleid.apple.com\",\"iat\":'$NOW',\"exp\":'$EXP',\"iss\":\"'$TEAM_ID'\"}' | base64 | tr -d '=' | tr '/+' '_-' | tr -d '\\n')\n\n# Sign with private key\nSIGNATURE=$(echo -n \"$HEADER.$PAYLOAD\" | openssl dgst -sha256 -sign <(echo \"$PRIVATE_KEY\") | base64 | tr -d '=' | tr '/+' '_-' | tr -d '\\n')\n\nCLIENT_SECRET=\"$HEADER.$PAYLOAD.$SIGNATURE\"\n\n# Exchange for access token\ncurl -s -X POST \"https://appleid.apple.com/auth/oauth2/token\" \\\n  -H \"Content-Type: application/x-www-form-urlencoded\" \\\n  -d \"grant_type=client_credentials&client_id=$CLIENT_ID&client_secret=$CLIENT_SECRET&scope=searchadsorg\" \\\n  | jq -r '.access_token'\n\nDaily Performance Report\n#!/usr/bin/env bash\nset -euo pipefail\n\n# SECURITY MANIFEST:\n# Environment variables accessed: ASA_ACCESS_TOKEN, ASA_ORG_ID\n# External endpoints called: https://api.searchads.apple.com/api/v5/reports/campaigns (only)\n# Local files read: none\n# Local files written: stdout (report data)\n\nACCESS_TOKEN=\"${ASA_ACCESS_TOKEN}\"\nORG_ID=\"${ASA_ORG_ID}\"\n\nTODAY=$(date -u +%Y-%m-%d)\nYESTERDAY=$(date -u -v-1d +%Y-%m-%d 2>/dev/null || date -u -d \"yesterday\" +%Y-%m-%d)\n\ncurl -s -X POST \"https://api.searchads.apple.com/api/v5/reports/campaigns\" \\\n  -H \"Authorization: Bearer $ACCESS_TOKEN\" \\\n  -H \"X-AP-Context: orgId=$ORG_ID\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"startTime\": \"'$YESTERDAY'\",\n    \"endTime\": \"'$TODAY'\",\n    \"granularity\": \"DAILY\",\n    \"selector\": {\n      \"orderBy\": [{\"field\": \"localSpend\", \"sortOrder\": \"DESCENDING\"}]\n    },\n    \"returnRowTotals\": true,\n    \"returnGrandTotals\": true\n  }' | jq '.data.reportingDataResponse.row[] | {\n    campaign: .metadata.campaignName,\n    spend: .total.localSpend.amount,\n    impressions: .total.impressions,\n    taps: .total.taps,\n    installs: .total.installs,\n    cpa: (if .total.installs > 0 then (.total.localSpend.amount | tonumber) / .total.installs else \"N/A\" end)\n  }'\n\nCore Rules\n1. Separate Campaigns by Intent\n\nBrand, category, competitor, and discovery keywords in separate campaigns. Mixing makes optimization impossible.\n\n2. Start Exact, Expand Broad\n\nBegin with exact match keywords you're confident about. Use Search Match and broad only for discovery with low bids.\n\n3. Mine Search Terms Weekly\n\nThe search term report is gold. Review weekly, add winners as exact, add losers as negatives.\n\n4. Defend Your Brand\n\nCompetitors WILL bid on your brand name. Always have a brand campaign with high bids to protect your real estate.\n\n5. Track Attribution Properly\n\nImplement AdServices for iOS 14.3+. Without attribution, you're optimizing blind.\n\n6. One Country Per Campaign\n\nMixing countries makes bid optimization nearly impossible. Create separate campaigns per country/region.\n\n7. Budget to CPA, Not Spend\n\nSet CPA goals, not just budgets. If CPA is 2x target, pause and investigate before spending more.\n\nCommon Traps\nMixing match types in one ad group → Can't tell what's working. Separate exact, broad, search match.\nNo negative keywords → Wasting budget on irrelevant searches. Review search terms weekly.\nIgnoring Search Tab/Today Tab → Lower intent but cheaper. Good for discovery.\nSame bid across all keywords → Brand keywords worth more than generic. Bid accordingly.\nNo attribution integration → Flying blind. Implement AdServices or MMP.\nLaunching without App Store optimization → Low conversion rate. Fix ASO first.\nBidding on competitors without testing → Often unprofitable. Test small first.\nForgetting timezone in reports → Data misalignment. Always use UTC or explicit timezone.\nNot using Custom Product Pages → Missing easy wins. Create themed pages.\nScaling too fast → CPA spikes when scaling. Increase budget 20-30% at a time.\nExternal Endpoints\nEndpoint\tData Sent\tPurpose\nhttps://appleid.apple.com/auth/oauth2/token\tClient credentials (JWT)\tGet access token\nhttps://api.searchads.apple.com/api/v5/*\tCampaign/keyword data\tCampaign management\nhttps://api-adservices.apple.com/api/v1/\tAttribution token\tAttribution data\n\nNo other data is sent externally.\n\nSecurity & Privacy\n\nData that leaves your machine:\n\nCampaign configurations sent to Apple Ads API\nAttribution tokens sent to Apple (from iOS app)\n\nData that stays local:\n\nCredentials in ~/apple-search-ads/credentials.md\nReports and analysis\nStrategy notes\n\nThis skill does NOT:\n\nStore API secrets in plain text (use environment variables)\nAccess user-level data (attribution is aggregated)\nMake requests to undeclared endpoints\nTrust\n\nBy using this skill, data is sent to Apple's Search Ads API and AdServices. Only install if you trust Apple with your advertising data.\n\nRelated Skills\n\nInstall with clawhub install <slug> if user confirms:\n\napp-store-connect — manage apps and releases\naso — App Store Optimization\nanalytics — track metrics and KPIs\nios — iOS development patterns\nFeedback\nIf useful: clawhub star apple-search-ads\nStay updated: clawhub sync"
  },
  "trust": {
    "sourceLabel": "tencent",
    "provenanceUrl": "https://clawhub.ai/ivangdavila/apple-search-ads",
    "publisherUrl": "https://clawhub.ai/ivangdavila/apple-search-ads",
    "owner": "ivangdavila",
    "version": "1.0.0",
    "license": null,
    "verificationStatus": "Indexed source record"
  },
  "links": {
    "detailUrl": "https://openagent3.xyz/skills/apple-search-ads",
    "downloadUrl": "https://openagent3.xyz/downloads/apple-search-ads",
    "agentUrl": "https://openagent3.xyz/skills/apple-search-ads/agent",
    "manifestUrl": "https://openagent3.xyz/skills/apple-search-ads/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/apple-search-ads/agent.md"
  }
}