AWS Credits APIでクレジット残高をSlackに自動通知する仕組みを作った

今年のAWS Summit Tokyoは2年振りにガッツリと参加することができまして、とても勉強になりました。特にフィジカルAIが面白かったです。

みなさんはAWSのクレジット残高、どうやって確認していますか?私はこれまでBilling and Cost Managementコンソールを毎回手動で開いて確認していたのですが、2026年6月に発表された 「AWS Credits Detail API」のパブリックAPIを使うことで、クレジット残高や適用履歴をプログラマティックに取得できるようになりました。 本投稿では、このAPIを活用してクレジット残高・利用状況を定期的にSlackへ自動通知するサーバーレスシステムを構築した際の設計と、実装時に見えてきた実践的な注意点を共有します。

最初に結論から

AWS Credits Detail APIのGetCredits(残高一覧)とGetCreditAllocationHistory(月次適用履歴)をBoto3経由で呼び出すことで、 従来コンソールでしか確認できなかったクレジット情報をプログラマティックに取得できます。 今回は3つのLambda関数(月次レポート・日次閾値チェック・日次期限切れチェック)とEventBridge Schedulerを組み合わせ、AWS CDK(TypeScript)でIaC管理するサーバーレスシステムを構築しました。実際に動かしてみると、API仕様書だけでは気づきにくいフィールド名や日付フォーマットの癖がいくつかあり、そのあたりが実装のハマりポイントでした。運用コストはLambda・EventBridge Schedulerの無料枠内に収まっています。

実際の月次AWSクレジットレポートの例

なぜクレジット監視の自動化が必要か

AWSのクレジット(プロモーションクレジットや契約クレジットなど)は、気づかないうちに枯渇していたり、期限切れになっていたりすることがあります。特に複数アカウントを運用している場合、コンソールを都度開いて確認するのは手間ですし、確認を忘れて「気づいたら残高が尽きていた」「気づいたら期限切れになっていた」という事態も起こりえます。

💡AWS Credits Detail APIとは?

2026年6月にAWSが発表した機能で、Billing and Cost Managementコンソール上でクレジットを一元管理できるほか、パブリックAPIとAmazon Q連携が提供されています。従来は月次更新だったクレジット残高が、24時間ごとのリフレッシュで確認できるようになりました。 aws.amazon.com

こうした背景から、クレジットの状態を定期的に自動チェックし、変化があればSlackに通知する仕組みを構築することにしました。

AWS Credits Detail APIの概要

AWS Credits Detail APIはAWS Billingサービス(サービスプレフィックス: billing)に属し、エンドポイントはus-east-1リージョン固定です。主要なAPIアクションは以下の3つです。

APIアクション 説明
GetCredits アカウントに紐づくクレジットの一覧を取得(残高・見込み残高など)
GetCreditAllocationHistory 最大24ヶ月分の月次クレジット適用履歴を取得
RedeemCredits プロモーションコードの引換

GetCreditsはページネーションなしで全件を一括返却しますが、GetCreditAllocationHistorynextToken/maxResultsによるページネーションに対応しています(2026年8月時点)。認証はIAM/SigV4の標準方式で、既存のAWSワークロードに自然に統合できます。

アーキテクチャ設計

AWSアーキテクチャ構成図

今回構築したシステムの構成は以下のとおりです。

通知種別 スケジュール 説明
月次レポート 毎月1日 9:00 UTC 確定残高・推定残高・推定使用額・適用履歴を送信
閾値アラート 毎日 0:00 UTC 残高が閾値(デフォルト$20)を下回ったら送信
期限切れ
アラート
毎日 01:00 UTC 30日以内に期限切れのクレジットを3段階(後述)で通知

期限切れアラートの3段階は、期限までの残日数で以下のように分類しています。

レベル 残日数 説明
🔴 CRITICAL 0日(当日期限切れ) 今日中に失効するクレジット
🟡 WARNING 1〜7日 1週間以内に失効するクレジット
🔵 INFO 8〜30日 1ヶ月以内に失効するクレジット

構成要素は下表のとおりです。

構成要素 役割
Lambda ランタイムはPython 3.12。月次レポート/日次閾値チェック/日次期限切れチェックの各ロジック
EventBridge Scheduler 各Lambdaをcron式で起動。Scheduler専用の実行ロールを分離し、対象Lambda関数のARNにのみlambda:InvokeFunction権限を付与
SSM Parameter Store Slack Bot Token・Channel IDを管理
SQS DLQ Lambda失敗時のイベント退避先
AWS CDK (TypeScript) 全インフラをIaCで管理

シークレット管理には、Secrets ManagerではなくSSM Parameter Store(Standardパラメータ)を採用しました。Secrets Managerはシークレットあたり月額$0.40の固定費が発生しますが、SSM Parameter Storeの標準パラメータは無料のため(いずれも2026年8月時点の料金)、コストを抑えられる点を重視した選択です。

Slackへの送信は、チャンネル固定のIncoming Webhookではなくchat.postMessage API(Bot Token方式)を採用しました。ボットが参加している任意のチャンネルへ動的に送信できる点と、スコープベースの認可(chat:write)で細かい粒度に制御できる点を重視した選択です。

def post_message(channel_id: str, blocks: list, text: str = "") -> None:
    # SSM Parameter StoreからBot Tokenを取得する
    token = _get_token()
    payload = {"channel": channel_id, "blocks": blocks, "text": text}
    headers = {
        "Authorization": f"Bearer {token}",
        "Content-Type": "application/json",
    }
    response = requests.post(SLACK_API_URL, headers=headers, json=payload, timeout=10)
    # Rate Limit(429)時はRetry-Afterヘッダーの秒数だけ待機してリトライ
    ...

Lambda実行ロールは3関数共用の最小権限ロールとし、billing:GetCreditsbilling:GetCreditAllocationHistoryのRead権限と、参照対象となるSSM Parameter Storeリソースのみに限定しています。EventBridge Scheduler用のロールはLambda実行ロールとは別に作成し、対象Lambda関数のARNにのみlambda:InvokeFunctionを許可することで、Schedulerが他リソースにアクセスするリスクを排除しています。

実装時の注意点・つまずきポイント

API仕様書やドキュメントだけでは気づきにくかった、実装時に判明した癖をまとめます。

API呼び出しの作法:ページネーションとリトライは自前実装が必要

エンドポイントはus-east-1固定で、他リージョンで呼び出すと失敗します。ここは単純なので実装時にすぐ気づけますが、厄介なのはGetCreditsGetCreditAllocationHistoryでページネーションの扱いが異なる点です。GetCreditsはページネーションなしで全件を一括返却する一方、 GetCreditAllocationHistorynextTokenによるページネーションに対応していますが、Boto3の自動ページネーターは提供されていません(2026年8月時点)。 つまり、以下のようにnextTokenが返ってこなくなるまで自分でループを書く必要があります。

def get_credit_allocation_history(account_id: str, ...) -> tuple[list, bool, list]:
    all_records: list = []
    next_token: Optional[str] = None

    # nextTokenが返ってこなくなるまでページを取り続ける
    while True:
        params = {"accountId": account_id, "startDate": ..., "endDate": ..., "maxResults": 1000}
        if next_token:
            params["nextToken"] = next_token

        # ThrottlingException対策の指数バックオフ付きで呼び出す
        response = _retry_with_backoff(lambda p=params: _get_client().get_credit_allocation_history(**p))
        all_records.extend(response.get("creditAllocationHistoryList", []))

        # 次ページがなければループを終了する
        next_token = response.get("nextToken")
        if not next_token:
            break

    return all_records, ...

同様に、SDKの自動リトライもThrottlingExceptionには対応していないため(2026年8月時点)、指数バックオフを自前で実装しました。今回は初回1秒・上限32秒・最大6回でリトライする方式にしています。地味な実装ですが、月次バッチのような低頻度呼び出しでもスロットリングは普通に発生するので、省略すると後で痛い目を見るポイントです。

レスポンスのフィールド名・データ型の癖

もう一つ厄介なのが、レスポンスのフィールド名や値の表記が、直感とややズレている点です。いずれも2026年8月時点で確認した仕様であり、今後のAPI改善で変更される可能性はあります。

  • creditStatus"ENABLED"/"DISABLED"で返ります。 他のAWS課金系リソースのステータス表記から"ACTIVE"を想定していると、判定条件を誤って書いてしまいそうな項目です
  • 残高・見込み額はamount/unitではなく、currencyAmount/currencyCodeという名前で返ってきます。 似たような他のAWS課金系APIのフィールド名と混同しやすいので注意が必要です
  • 日付フィールドは"2027-11-30 23:59:59+00:00"のようにスペース区切りで返ってきます。 T区切りのISO-8601形式を期待してPythonのdatetime.fromisoformat()にそのまま渡すとValueErrorになるため、パース前にスペースをTに変換する正規化処理を挟んでいます。
def parse_date(value) -> datetime:
    # 文字列で返ってきた日付を正規化してパースする
    if isinstance(value, str) and value:
        # スペース区切りとZ末尾を正規化してからパースする
        normalized = value.replace(" ", "T").replace("Z", "+00:00")
        try:
            return datetime.fromisoformat(normalized)
        except ValueError:
            pass
    # パースできない場合はdatetime.minを返す
    return datetime.min.replace(tzinfo=timezone.utc)

いずれもAPIリファレンスの型定義だけを読んでいると気づきにくく、実際にレスポンスをダンプして初めて発覚した類のものです。

payerAccountFlag(支払い元アカウント集約フラグ)とデータ欠損チェックを忘れずに

複数アカウントを管理アカウント配下で運用している場合、payerAccountFlag(支払い元アカウント集約フラグ)Trueに指定しないと、Consolidated Billing(一括支払い/統合請求)配下のクレジットが取得できません。

payerAccountFlagは、呼び出し元がConsolidated Billingの管理アカウント(支払いを一括して行うpayer account)である場合に、そのアカウント自身のクレジットだけでなく配下の全メンバーアカウントのクレジットまで集約して返すかどうかを指定するフラグです。デフォルト(false)では、管理アカウントかどうかにかかわらず、リクエストで指定したaccountId自身が保有するクレジットしか見えないため、「なぜか一部のクレジットが表示されない」という状態に陥りがちです。

また、GetCreditAllocationHistoryのレスポンスに含まれるpartialResults(一部の請求月のデータ取得に失敗したかどうかを示すフラグ)とfailedMonths(取得に失敗した請求月のリスト)の確認も欠かせません。一部の月のデータ取得に失敗していてもAPI呼び出し自体は成功扱いになるため、この2つのフィールドをチェックしないと、欠損データに気づかないまま「今月は適用履歴なし」と誤った通知を送ってしまう恐れがあります。

コストについて

月次コストは約 $0.00 です。Lambda・EventBridge Schedulerは無料枠内に収まり、SSM Parameter Store(Standardパラメータ)も無料のため、実質的にランニングコストはかかりません。

実際の使用感

実際に使用してみると、月次レポートで「確定残高・推定残高・推定使用額・適用履歴」がひと目でわかるようになり、コンソールを開く手間がなくなりました。閾値アラートと期限切れアラート(30日前からの3段階通知)を日次で走らせることで、「気づいたら枯渇していた」「気づいたら期限切れだった」という事態を未然に防げるようになりました。

クレジット閾値アラートの例。検証のために閾値を$1,000にしています。月々の消費ペースを考慮して何ヶ月分の余裕を持ってアラートを出すかで閾値を決めます

期限切れアラートの例。検証のために2年以内に期限が切れるクレジットを検知させています。これで消費していないもったいないクレジットを意識できます。

発展的な活用

今回は残高・履歴・期限切れの監視にとどめましたが、AWS Credits Detail APIのcostCategoryArnフィールドやCost Explorer APIのGroupBy: COST_CATEGORYRECORD_TYPE=Creditフィルタを組み合わせれば、部門別のクレジット消費レポートをSlack通知に統合することも可能です。単なる残高通知から一歩進めて、FinOps的なクレジット配分の意思決定支援ツールへ発展させる余地もありそうです。このあたりは今後試してみたいと思っています。

また、現状は閾値アラートの金額と期限切れアラートの日数区切りはそれぞれCDKスタック、Lambdaコード内に直接ハードコードされています。閾値アラートは「月々の消費ペース×確保したいリードタイム」で決めるのが実用的ですが、消費ペースは時期によって変動するため、両方を環境変数化して運用しながら柔軟に調整できるようにするのも改善ポイントの一つです。

ソースコードを公開しています

今回構築したシステムのソースコード(Lambda・CDKスタック一式)はGitHubで公開しています。IAMポリシーの詳細やテストコードも含めて確認できますので、同じ仕組みを構築する際の参考にしていただければと思います。

github.com

最後に

本投稿では、AWS Credits Detail APIを活用したクレジット残高のSlack自動通知システムについて、設計から実装時のつまずきポイントまでを共有しました。API自体は2026年6月に登場したばかりで情報も少ないため、実装時に発見した仕様の癖(フィールド名や日付フォーマットなど)が同じようにこのAPIを使う方の参考になれば幸いです。

参考情報