Vitals APIリファレンス
このガイドには、Vitals APIの統合に役立つリファレンス情報が含まれています。
Vitals APIの使用にあたっては、Program Materials License Agreementが適用されます。
前提条件
API呼び出しを行う前に、次の要件を満たしていることを確認してください。
| 要件 | 詳細 |
|---|---|
| Amazon開発者アカウント | 管理者レベルの認証情報が必要 |
| セキュリティプロファイル | 開発者コンソール > [設定] > [APIアクセス] で作成 |
| APIにマッピングされたセキュリティプロファイル | プロファイルをReporting APIに関連付け |
| クライアントIDとクライアントシークレット | セキュリティプロファイルの [ウェブ設定] タブから |
| LWAスコープ | adx_reporting::appstore:marketer |
| パッケージ名 | アプリのパッケージ名(例:com.example.myapp)は開発者のベンダーアカウントが所有している必要があります |
Python 3.6以上とrequestsライブラリ |
ベースURL
Vitals APIのベースURLはhttps://developer.amazon.com/api/appstoreです。
操作
VitalsAPIには、以下の操作とエンドポイントが含まれています。
| 操作 | メソッド | エンドポイント | 説明 |
|---|---|---|---|
| 最新状況の確認 | GET | /vitals/apps/{パッケージ名}/{指標セット名} |
最新の利用可能なデータのタイムスタンプ |
| 指標の照会 | POST | /vitals/apps/{パッケージ名}/{指標セット名}:query |
ディメンション/フィルター付きの時系列指標 |
リクエスト
パラメーターと検証規則の詳細については、次のセクションを参照してください。
リクエストパラメーター
すべてのクエリリクエストには、次のパラメーターを含める必要があります。
| パラメーター | 説明 | 型 | 必須 |
|---|---|---|---|
timelineSpec.aggregationPeriod |
返されるデータの詳細度です。 DAILYまたはHOURLYを指定できます。 |
文字列 | はい |
timelineSpec.startTime |
クエリの対象期間の開始日を、UTCでの日付として{年, 月, 日}の形式で指定します。 |
オブジェクト | はい |
timelineSpec.endTime |
クエリの対象期間の開始日を、UTCでの日付として{year, month, day}の形式で指定します。startTimeより後の日付にする必要があります。 |
オブジェクト | はい |
metrics[] |
指標セットから返す指標名のサブセットを指定します(crashRate、crashCountなど)。省略すると、セットで定義されているすべての指標が返されます。 |
配列 | いいえ |
dimensions[] |
結果をグループ化するディメンションを指定します(versionCode、countryCodeなど)。 |
配列 | いいえ |
filter |
フィルター条件を表すJSONオブジェクトです。各キーはディメンションを示し、その値には許容する値のリストを指定します(フィールド内はOR、フィールド間はANDとして処理されます)。たとえば、{"countryCode": ["US","CA"], "deviceType": ["AMAZON_FIRE_TV"]}のようになります。 |
文字列 | いいえ |
pageSize |
1ページあたりの最大行数です。デフォルトは 1,000です。最大値: 100,000です。 | 整数 | いいえ |
pageToken |
前回のレスポンスのnextPageTokenとして返された不透明なカーソルです。初回のリクエストでは省略します。 |
文字列 | いいえ |
reportType |
issuesMetricSetの場合にのみエラータイプを指定します。CRASHかANRのいずれかです。発生率指標セットには適用されません。 | 文字列 | いいえ |
レスポンス
レスポンスフィールドの詳細については、以下のセクションを参照してください。
レスポンスフィールド
| フィールド | 常に存在する | 説明 |
|---|---|---|
status |
はい | HTTPステータスコード(200) |
requestId |
はい | デバッグ用のUUID、サポートチケットに含める |
freshness |
はい | クエリ対象期間の最新データのタイムスタンプ |
resultCount |
はい | このページの行数 |
rows[] |
はい | 一致するデータがない場合は空になることがあります |
nextPageToken |
さらにページがある場合のみ | 次のページを取得するには、次のリクエストに渡します |
成功時のレスポンス(200)
{
"status": 200,
"requestId": "8a3f2c71-e4b9-4012-a8c1-9d5e7f3b2a10",
"freshness": {
"aggregationPeriod": "DAILY",
"latestDataTimestamp": "2026-04-25T00:00:00Z"
},
"resultCount": 3,
"rows": [
{
"startTime": {"year": 2026, "month": 4, "day": 25},
"aggregationPeriod": "DAILY",
"dimensions": [
{"name": "versionCode", "value": "151"},
{"name": "countryCode", "value": "US"}
],
"metrics": [
{"name": "crashRate", "value": "0.0142"},
{"name": "distinctDevices", "value": "54320"}
]
}
],
"nextPageToken": "MjAyNi0wNC0yNXwxNA=="
}
実装例
Vitals APIを介して行うさまざまなタスクで使用するPythonコードについて、以下に例を示します。
アクセストークンの取得
クライアントIDとクライアントシークレットを使用して認可トークンを取得し、各APIリクエストのヘッダーに追加する必要があります。以下の例では、認可トークンを取得し、このトークンを利用してAuthorizationヘッダーを作成する方法を紹介します。
# 指定する必要がある値
client_id = "<クライアントID>"
client_secret = "<クライアントシークレット>"
package_name = "<アプリのパッケージ名>"
BASE_URL = "https://developer.amazon.com/api/appstore"
scope = "adx_reporting::appstore:marketer"
data = {
"grant_type": "client_credentials",
"client_id": client_id,
"client_secret": client_secret,
"scope": scope
}
auth_response = requests.post("https://api.amazon.com/auth/o2/token", data=data)
auth_token = auth_response.json()["access_token"]
headers = {
"Authorization": f"Bearer {auth_token}",
"Content-Type": "application/json"
}
データの最新状況の確認
次の例は、クエリを実行する前に、利用可能な最新データのタイムスタンプを確認する方法を示しています。
freshness_url = f"{BASE_URL}/vitals/apps/{package_name}/crashMetricSet"
freshness_response = requests.get(freshness_url, headers=headers)
freshness = freshness_response.json()
for info in freshness["freshnessInfo"]:
print(f"{info['aggregationPeriod']}: {info['latestDataTimestamp']}")
最新状況取得のレスポンスの例
{
"name": "crashMetricSet",
"metrics": ["crashRate", "crashRate7dUserWeighted", "crashRate28dUserWeighted", "userPerceivedCrashRate", "userPerceivedCrashRate7dUserWeighted", "userPerceivedCrashRate28dUserWeighted",
"distinctDevices", "crashCount"],
"dimensions": ["versionCode", "countryCode", "deviceModel", "deviceType", "deviceOS", "osVersion"],
"freshnessInfo": [
{"aggregationPeriod": "DAILY", "latestDataTimestamp": "2026-04-25"},
{"aggregationPeriod": "HOURLY", "latestDataTimestamp": "2026-04-25T14"}
]
}
ローカル時刻をUTCに変換する方法
次の例は、ローカル時刻のdatetimeをUTCに変換する方法を示しています。
from datetime import datetime, timezone, timedelta
def utc_date_components(local_dt=None):
"""API向けに、datetimeをUTCのyear/month/dayに変換します。
タイムゾーン情報のないlocal_dtは、システムのローカル時刻として解釈されます。
あいまいさを避けるには、タイムゾーン付きのdatetimeを渡してください。
"""
if local_dt is None:
utc_dt = datetime.now(timezone.utc)
else:
if local_dt.tzinfo is None:
local_dt = local_dt.astimezone() # システムのローカルタイムゾーンを明示的に適用
utc_dt = local_dt.astimezone(timezone.utc)
return {"year": utc_dt.year, "month": utc_dt.month, "day": utc_dt.day}
# 例:UTCでの「昨日」(ローカルタイムゾーンとは無関係)
yesterday_utc = datetime.now(timezone.utc) - timedelta(days=1)
start = utc_date_components(yesterday_utc)
指標の照会
次の例は、発生率指標セット(クラッシュ、ANR、LMK)に対するクエリの完全な構造を示しています。コメントで、フィールドが必須であるか、省略可能であるかを示しています。
metric_set = "crashMetricSet" # or "anrMetricSet", "lmkMetricSet"
query_url = f"{BASE_URL}/vitals/apps/{package_name}/{metric_set}:query"
body = {
# 必須:時間範囲と詳細度
"timelineSpec": {
"aggregationPeriod": "DAILY", # DAILYまたはHOURLY
"startTime": {"year": 2026, "month": 4, "day": 01},
"endTime": {"year": 2026, "month": 4, "day": 01},
},
# 省略可能:クエリ対象の指標セット内の指標
"metrics": ["crashRate", "crashCount", "distinctDevices"],
# 省略可能:ディメンション別の内訳(最大5つ)
"dimensions": ["versionCode", "countryCode"],
# 省略可能: AIP-160仕様のフィルター式
"filter": {"countryCode": ["US", "CA"], "deviceType": ["AMAZON_FIRE_TV"]},
# 省略可能:ページ分割(デフォルトは1000、最大100000)
"pageSize": 1000,
"pageToken": "<前回のレスポンスから取得>",
}
response = requests.post(query_url, headers=headers, json=body)
result = response.json()
結果のページ分割
次の例は、レスポンスにnextPageTokenが含まれている場合に、すべての結果を取得する方法を示しています。
all_rows = []
body.pop("pageToken", None)
while True:
result = requests.post(query_url, headers=headers, json=body).json()
all_rows.extend(result["rows"])
token = result.get("nextPageToken")
if not token:
break
body["pageToken"] = token
上位のクラッシュ問題のクエリ
issuesMetricSetは、頻度順にランク付けされた上位のクラッシュ/ANR/LMKの問題を返します。発生率指標セットとは異なり、時間範囲全体にわたって集計され、スタックトレースと共に一意のクラッシュシグニチャごとに1行が返されます。reportTypeには、共有スタックトレースのタイプとしてCRASHまたはANRを指定します。
issues_url = f"{BASE_URL}/vitals/apps/{package_name}/issuesMetricSet:query"
body = {
"startTime": { "year": 2025, "month": 6, "day": 1 },
"endTime": { "year": 2025, "month": 6, "day": 15 },
"reportType": "CRASH",
"dimensions": ["deviceType", "osVersion"],
"filter": { "countryCode": ["US", "CA"] },
"pageSize": 25,
"pageToken": null
}
response = requests.post(issues_url, headers=headers, json=body)
result = response.json()
# レスポンス構造(発生率指標セットとは異なります):
# {
# "rows": [
# {
# "crashDescriptor": "67f35eb2...",
# "reportText": "java.lang.IllegalStateException: ...",
# "metrics": [
# {"name": "errorEventCount", "value": "78"},
# {"name": "affectedDeviceCount", "value": "45"}
# ]
# }
# ],
# "nextPageToken": "..."
# }
エラー処理
エラーの処理方法については、次のセクションを参照してください。
エラーレスポンス形式
すべてのエラーは構造化されたJSONを返します。
{
"code": "INVALID_ARGUMENT",
"message": 「不明な指標'bogus'です。crashMetricSetの有効な指標:crashRate, crashRate7dUserWeighted, ...",
"status": 400
}
エラーコード
| HTTPステータス | コード | 原因 | 対処法 |
|---|---|---|---|
| 400 | INVALID_ARGUMENT | リクエストパラメーターが正しくありません(指標、ディメンション、日付範囲が無効です) | リクエストを修正してください。再試行しないでください |
| 403 | UNAUTHORIZED | トークンが期限切れまたは無効です | トークンを更新して再試行します |
| 404 | NOT_FOUND | 指標セット名が不明です | 指標セット名のスペルをチェックします |
| 429 | RATE_LIMITED | 3リクエスト/秒の制限を超えました | 待機時間を指数的に伸ばしながら再試行します |
| 500 | INTERNAL | サーバーエラー | 待機時間を伸ばしながら再試行します(最大3回) |
よくある誤りと修正
| 誤り | 表示されるエラー | 修正 |
|---|---|---|
timelineSpecが見つからない |
"timelineSpec is required" | 必要なtimelineSpecオブジェクトを追加します |
28d指標でHOURLYを使用 |
"crashRate28dUserWeighted not available for HOURLY" | DAILYに切り替えるか、28d指標を削除します |
| 日付範囲が広すぎる(時間単位の場合) | "HOURLY queries limited to 15 days" | 15日以内に絞り込む |
| トークンの期限切れ | 403 Forbidden | LWAトークンを更新します |
| リクエストが多すぎる | 429 Too Many Requests | 呼び出しの間にtime.sleep()を追加します |
再試行とベストプラクティス
再試行の戦略とベストプラクティスについては、次のセクションを参照してください。
再試行戦略の例
import time
MAX_RETRIES = 3
RETRIABLE_CODES = {429, 500, 502, 503}
def query_with_retry(client, package, metric_set, body):
for attempt in range(MAX_RETRIES):
resp = client.query(package, metric_set, body)
if resp.status_code == 200:
return resp.json()
if resp.status_code not in RETRIABLE_CODES:
break # 再試行不可のエラー
wait = (2 ** attempt) + 0.5 # 1.5秒、2.5秒、4.5秒
print(f"Retry {attempt + 1}/{MAX_RETRIES} after {wait}s (HTTP {resp.status_code})")
time.sleep(wait)
# 最後の試行は失敗しました
error = resp.json() if resp.headers.get("content-type", "").startswith("application/json") else {}
raise RuntimeError(f"Failed after {MAX_RETRIES} retries: {resp.status_code} {error.get('message', '')}")
本番環境でのベストプラクティス
| ベストプラクティス | 推奨される方法 |
|---|---|
| トークンのキャッシング | トークンをキャッシュに保存し、有効期限が切れる5分前に更新します。API呼び出しごとに新しいトークンをリクエストしないでください。 |
| 最初に鮮度を確認する | 結果が空にならないように、クエリを実行する前に必ず鮮度エンドポイントを呼び出します。 |
| ページ分割を使用する | デフォルトのpageSizeは1000です。ディメンションによる内訳が多い場合は、タイムアウトを避けるためにページ分割を行います。 |
| リクエスト頻度の制限 | リクエストは1秒あたり3回未満にします。連続した呼び出しの間にtime.sleep(0.4)を追加します。 |
| 再試行可能なエラーについてのみ再試行する | エラーが429、500、502、503の場合、再試行します。400の場合は再試行しないでください(代わりにリクエストを修正してください)。 |
| requestIdをログに記録する | すべてのレスポンスにはrequestIdが含まれています。デバッグやサポートチケット用に、これをログに記録します。 |
| 空の結果を処理する | rows: []を返すクエリはエラーではありません。これは、その日付範囲のデータがないことを意味します。 |
| オーバーフェッチを回避する | 必要な指標のみをリクエストします。metrics[]を省略すると、すべてが返されますが、速度は遅くなります。 |
関連トピック
- Vitals APIの概要: 利用可能な指標、ディメンション、FAQ
- アプリ健全性インサイトダッシュボード: 同じ指標の視覚的なダッシュボード
- Reporting API Explorer: 開発者ポータルのインタラクティブなAPIエクスプローラー
Last updated: 2026年6月29日

