> ## Documentation Index
> Fetch the complete documentation index at: https://docs.appeeky.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Apple Search Ads on your own key

> Pull your app's Apple Search Ads spend, installs, keywords and search terms straight from Apple into the repo, with no Appeeky account, and turn paid searches into listing targets.

`search-ads pull` reads Apple Search Ads with your own API key and writes this app's numbers to `appeeky/search-ads/`. Use it to see what you pay per install, which keywords waste money, and which searches people typed before installing, so you can target them in the listing and rank for them for free.

No Appeeky account is needed. Apple's numbers stay in your repo.

## 1. Create the API key in Apple Search Ads

1. In [Apple Search Ads](https://app.searchads.apple.com), open **Account Settings → User Management** and invite a user with an API role with read access, for example **API Account Read Only**.

2. Sign in as that user and open **Account Settings → API**.

3. Make an EC key pair and upload the public key:

   ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
   openssl ecparam -genkey -name prime256v1 -noout -out searchads-private-key.pem
   openssl ec -in searchads-private-key.pem -pubout -out searchads-public-key.pem
   ```

4. Apple shows a client id, a team id and a key id. Keep the private key file outside the repo.

## 2. Save the key

### On your App Store Connect CLI account (your machine)

Save the Search Ads key on the same account that holds your App Store Connect key. Both keys then live in one place, and the private key never leaves the CLI:

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
appstore accounts search-ads my-team \
  --client-id SEARCHADS.… --team-id SEARCHADS.… --key-id … \
  --private-key-path ./searchads-private-key.pem
```

Spec uses the account in `stores.appStore.account`, or the CLI's default account. Use the EC private key, not the public key you uploaded. More options: [Apple Search Ads in the App Store Connect CLI](https://docs.appeeky.com/docs/store-cli-app-store-connect-search-ads.md).

### As environment variables (CI, Docker)

In CI there is no CLI account, so set these as secrets:

| Variable | Meaning |
| - | - |
| `ASA_CLIENT_ID` | Client id |
| `ASA_TEAM_ID` | Team id (defaults to the client id) |
| `ASA_KEY_ID` | Key id |
| `ASA_PRIVATE_KEY` | The private key PEM, or a path to it (`\n` escapes are fine) |
| `ASA_ORG_ID` | Optional: the organization id |

When `ASA_CLIENT_ID`, `ASA_KEY_ID` or `ASA_PRIVATE_KEY` is set, the environment wins over the CLI account. Set all three, or Spec tells you which one is missing.

## 3. Point the spec at the app

```yaml theme={"theme":{"light":"github-light","dark":"github-dark"}}
stores:
  appStore:
    appId: "1234567890"         # required: campaigns are matched to the app by this id
    searchAds:
      orgId: "1234567"          # only when the key reaches more than one organization
      campaigns: ["987654"]     # optional: only these campaigns
```

| Key | Meaning |
| - | - |
| `stores.appStore.appId` | The numeric App Store id. Only campaigns that promote this app are read; ads for your other apps in the same organization stay out |
| `stores.appStore.searchAds.orgId` | The organization, when the key reaches more than one. Without it, Spec lists the organizations and stops |
| `stores.appStore.searchAds.campaigns` | Only these campaign ids. Default: every campaign for this app |

## 4. Pull

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
npx @appeeky/spec search-ads pull
```

```
✓ appeeky/search-ads/  US Exact, Discovery
  30 days: 412.80 EUR · 236 installs · 1.75 EUR each · 980 taps
  41 keywords, 318 search terms. Read appeeky/search-ads/summary.md.
```

`--days <n>` changes the window (default 30, up to yesterday). With no campaign for the app, it says `No campaign for App Store id 1234567890 in org 1234567.` A report that cannot be read is listed with `!`; the rest are still written.

## What it writes

| File | What is in it |
| - | - |
| `daily.csv` | `date,spend,installs,taps,impressions,cpi` per day, for all of this app's campaigns together. Kept across pulls: older days stay, the pulled days are replaced |
| `keywords.csv` | Each bid keyword: match type, campaign, impressions, taps, installs, spend and cost per install, over the window. Most installs first |
| `search-terms.csv` | What people actually typed, with the same columns (no match type) |
| `summary.md` | The campaigns and their status, spend, installs, taps, and two tables you act on (below) |

Keyword and search term reports come from campaigns that show in App Store search results; other placements only add to `daily.csv`.

## Read summary.md

**Search terms that brought installs and are not targets.** People typed these and installed. Spec compares them with every storefront's `targets` in `appeeky.spec.yaml` and lists up to 15. Each one is a candidate for `targets` and for the keyword field: ranking for it organically costs nothing.

**Keywords that spent and brought no install.** Up to 10, most spend first. Lower the bid, pause the keyword, or check whether the listing answers that search at all.

| summary.md says | What to do |
| - | - |
| `rain sounds for sleep`: 14 installs, not a target | Add it to `targets`, then fit it into the subtitle or keyword field with your coding agent |
| `white noise machine`: 38.20 EUR, 0 installs | Pause or lower the bid in Apple Search Ads |
| Most installs come from the brand term | Paid installs you might get organically anyway; compare `ad_installs` with organic downloads |

## Daily routine

1. Run `search-ads pull` once a day, or let CI do it: the daily workflow and the Docker `daily` routine run it when `ASA_CLIENT_ID` is set. See [GitHub Actions & CI](https://docs.appeeky.com/docs/spec-ci.md).
2. Commit `appeeky/search-ads/` so the history builds up.
3. Once a week, read `summary.md`. Move the best new search terms into `targets`, run `check`, and open a pull request.
4. After the push, `verify` tells you on day 7 and day 14 whether organic ranks moved for those terms.

## Metrics

`ads`, `verify` and experiments read `daily.csv` as two metrics, with no Appeeky connection:

| Metric | From `daily.csv` | Better |
| - | - | - |
| `ad_installs` | Installs per day | Up |
| `cpa` | Spend ÷ installs | Down |

When the repo has `daily.csv`, it wins over Search Ads data from Appeeky. See [Paid channels](https://docs.appeeky.com/docs/spec-ads.md).

Related: [Paid channels](https://docs.appeeky.com/docs/spec-ads.md) · [On your own keys](https://docs.appeeky.com/docs/spec-own-keys.md) · [Listing](https://docs.appeeky.com/docs/spec-listing.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.