この内容は古いバージョンです。最新バージョンを表示するには、戻るボタンを押してください。
バージョン:6
ページ更新者:guest
更新日時:2026-06-11 07:12:00

タイトル: App Store Connect API
SEOタイトル: App Store Connect API 完全ガイド

この記事の要点
  • App Store Connect API は Apple Developer 公式の REST API。アプリ管理 / TestFlight / 売上を自動化
  • 認証は JWTAPI Key (Key ID) + Issuer ID + Private Key (.p8) で署名
  • アプリメタデータ更新、レビュー状況取得、TestFlight ベータテスター追加、売上レポート取得
  • Fastlanedeliver / pilot は内部でこの API を利用
  • Python: appstoreconnect-api、Ruby: spaceship / fastlane
  • レート制限あり (組織単位)。指数バックオフ + キャッシュで対応

App Store Connect API の概要

2018 年に Apple が公開した REST API。それ以前は非公式の iTMSTransporter や spaceship (リバースエンジニアリング) に頼っていた処理を、公式 API で行えるようになりました。

主なユースケース:

  • CI/CD でのアプリ申請自動化 (Fastlane など)
  • TestFlight のベータテスター一括追加 / 削除
  • アプリのメタデータ (スクリーンショット / 説明文) 一括更新
  • 売上 / 利用統計レポートのダウンロード
  • レビュー状況の監視 (Slack 通知連携)
  • プロビジョニングプロファイル / 証明書管理

API キーの取得

  1. App Store Connect にログイン
  2. ユーザとアクセス → キータブ
  3. App Store Connect API → 「キーを生成」
  4. 役割 (Admin / Developer / App Manager 等) を選択
  5. 生成された .p8 ファイルをダウンロード (再ダウンロード不可)
  6. Key ID と Issuer ID をメモ

取得物:

  • Key ID — 10 文字程度の英数字
  • Issuer ID — UUID 形式
  • Private KeyAuthKey_XXXXXX.p8 (PEM 形式 EC 鍵)

JWT 認証

毎回のリクエストで JWT を作って Authorization: Bearer ヘッダに付けます。有効期限は最大 20 分。

Python での JWT 生成

# pip install pyjwt cryptography requests
import time
import jwt
import requests

KEY_ID    = "ABC123DEFG"
ISSUER_ID = "57246542-96fe-1a63-e053-0824d011072a"
PRIVATE_KEY_PATH = "AuthKey_ABC123DEFG.p8"

def generate_token():
    with open(PRIVATE_KEY_PATH, "r") as f:
        private_key = f.read()

    headers = {
        "alg": "ES256",
        "kid": KEY_ID,
        "typ": "JWT",
    }
    payload = {
        "iss": ISSUER_ID,
        "iat": int(time.time()),
        "exp": int(time.time()) + 20 * 60,    # 20 分有効
        "aud": "appstoreconnect-v1",
    }
    return jwt.encode(payload, private_key, algorithm="ES256", headers=headers)

# 利用
token = generate_token()
res = requests.get(
    "https://api.appstoreconnect.apple.com/v1/apps",
    headers={"Authorization": f"Bearer {token}"},
)
print(res.json())

Ruby (spaceship) での JWT 生成

require 'spaceship'

Spaceship::ConnectAPI::Token.create(
  key_id:       'ABC123DEFG',
  issuer_id:    '57246542-96fe-...',
  filepath:     'AuthKey_ABC123DEFG.p8',
)

apps = Spaceship::ConnectAPI::App.all
apps.each { |a| puts "#{a.id} #{a.name}" }

主要エンドポイント

エンドポイント用途
GET /v1/appsアプリ一覧
GET /v1/apps/{id}/appStoreVersionsバージョン一覧
POST /v1/appStoreVersions新規バージョン作成
GET /v1/apps/{id}/buildsビルド一覧 (TestFlight)
POST /v1/betaTestersベータテスター追加
GET /v1/betaGroupsテスターグループ
POST /v1/appPreviewSetsスクリーンショット
GET /v1/salesReports売上レポート (gzip)
GET /v1/financeReports会計レポート
GET /v1/devices登録デバイス
POST /v1/certificates証明書作成
POST /v1/profilesプロビジョニングプロファイル作成

Python ライブラリ (appstoreconnect-api)

# pip install appstoreconnect
from appstoreconnect import Api, UserRole

api = Api(KEY_ID, PRIVATE_KEY_PATH, ISSUER_ID)

# アプリ一覧
apps = api.read_apps()
for app in apps:
    print(app.id, app.name, app.bundleId)

# 売上レポート (gzip)
import datetime
data = api.download_sales_and_trends_reports(
    filters={'frequency': 'DAILY', 'reportDate': '2026-06-01',
             'vendorNumber': '12345678'},
    save_to='./sales.gz',
)

# TestFlight ベータテスター追加
api.modify_registered_devices('DEVICE_ID', name='New iPhone')

# レビュー状況
versions = api.read_app_store_versions(app_id=app.id)
for v in versions:
    print(v.versionString, v.appStoreState)
    # IN_REVIEW / WAITING_FOR_REVIEW / READY_FOR_SALE / DEVELOPER_REJECTED

Fastlane との関係

Fastlane は内部で App Store Connect API を利用しています。app_store_connect_api_key アクションで API キーを設定すると、deliver / pilot / match などが API 経由で動作します。

# Fastfile
lane :release do
  api_key = app_store_connect_api_key(
    key_id: "ABC123DEFG",
    issuer_id: "57246542-96fe-...",
    key_filepath: "./AuthKey_ABC123DEFG.p8",
  )

  # ビルドアップロード
  upload_to_app_store(
    api_key: api_key,
    submit_for_review: true,
    automatic_release: true,
  )

  # TestFlight 配信
  pilot(
    api_key: api_key,
    distribute_external: true,
    groups: ["Beta Testers"],
  )
end

レート制限

制限
1 時間あたりリクエスト数 (組織単位)3,600 (おおよそ)
JWT 有効期限最大 20 分
1 回のレスポンスサイズ~10 MB
ファイルアップロード上限~4 GB

超過時は 429 Too Many Requests。指数バックオフでリトライ。

売上レポートの取得

import requests, gzip

token = generate_token()

# 日次レポート (gzip 圧縮 TSV)
params = {
    "filter[frequency]": "DAILY",
    "filter[reportDate]": "2026-06-01",
    "filter[reportType]": "SALES",
    "filter[reportSubType]": "SUMMARY",
    "filter[vendorNumber]": "12345678",
}
res = requests.get(
    "https://api.appstoreconnect.apple.com/v1/salesReports",
    headers={"Authorization": f"Bearer {token}"},
    params=params,
    stream=True,
)
with open("sales.tsv.gz", "wb") as f:
    for chunk in res.iter_content(8192):
        f.write(chunk)

# 解凍して読む
with gzip.open("sales.tsv.gz", "rt") as f:
    for line in f:
        print(line.strip())

TestFlight ベータテスター一括追加

import requests

token = generate_token()

# ベータテスター作成
res = requests.post(
    "https://api.appstoreconnect.apple.com/v1/betaTesters",
    headers={"Authorization": f"Bearer {token}",
             "Content-Type": "application/json"},
    json={
        "data": {
            "type": "betaTesters",
            "attributes": {
                "email": "tester@example.com",
                "firstName": "Test",
                "lastName": "User",
            },
            "relationships": {
                "betaGroups": {
                    "data": [{"type": "betaGroups", "id": "GROUP_ID"}]
                }
            }
        }
    }
)
print(res.status_code, res.json())

エラー処理

ステータス意味対処
401 UnauthorizedJWT 期限切れ or 署名不正トークン再生成、Key ID/Issuer ID 確認
403 Forbidden権限不足API キーの role を Admin / App Manager に
404 Not FoundID が間違い事前に GET /v1/apps で正しい ID を取得
409 Conflict状態遷移不可例: レビュー中はバージョン削除不可
429 Too Many Requestsレート制限指数バックオフ + キャッシュ

FAQ

Q: 秘密鍵 .p8 を紛失した
A: 再ダウンロード不可。App Store Connect で旧キーを失効させ、新規キー生成。

Q: Apple Developer Enterprise Program でも使える?
A: 使えますが、利用可能なエンドポイントが限定されます (Enterprise 配布固有の API が別にあります)。

Q: Fastlane / spaceship との使い分けは?
A: 単純なアプリ公開・TestFlight は Fastlane で十分。複雑な独自ワークフロー (ダッシュボード、社内ツール) は API 直叩き。

Q: Webhook はある?
A: レビュー状況通知の Webhook はありません。定期 polling + Slack 通知が現実解。