組織レベルでのGitHub Copilot の利用状況を API で取り込んで自作ダッシュボード on Azure して可視化してみた
どうも、@chips0711 です。
突然ですが・・・
GitHub Copilot を Organization で導入すると、次に気になるのは「で、実際どれくらい使われているの?」というところではないでしょうか。
GitHub Copilot usage metrics 全体は 2026 年 2 月 27 日に GA していて、GitHub.com では Copilot usage dashboard と Code generation dashboardを使えます。
どちらも 28 日ローリングの可視化が中心なので、四半期単位の振り返りや個人別の深掘りは、ダッシュボード単体より NDJSON export や API を併用したほうが向いていました。少なくとも私の確認環境では、Terraform や Bicep のようなロングテールな言語は UI だけで継続的に追いにくかったです。
そこで 本記事では自由度高く自分の可視化したいものを、どこまで可視化できるかを検証するために、Copilot usage metrics API を使い、Python でデータを取り込んで React で描くダッシュボードを自作してみました。
ローカルで自作するだけに飽き足らず、今回はAzure上にて、Azure Functions + App Service に載せて、mock データで end-to-end 確認が通ったところまでやってみました。
(Publicな記事ということもあり諸々考慮して、実データではなくmockデータでの実装となっています。)
開発は GitHub Copilot CLIで進めました。
ちなみにスクラップにて、GitHub Copilot CLIについてもまとめていますので、そもそも何?という方は是非ご覧ください。
詳細は後述しますが、以下のような構成でAzure上で組んでみました。

リポジトリはこちらです。
では、早速本題に入っていきたいと思います。
GitHub のネイティブ分析と、API で自作する理由
GitHub は利用状況の見え方を 3 つのレイヤーに分けています。
- Copilot usage dashboard が DAU・acceptance rate・Agent 利用・言語別 activity など 28 日間のトレンド
- Code generation dashboard が Copilot の生成行数・追加行数・削除行数を user-initiated / agent-initiated で分解
- REST API + NDJSON export が enterprise / organization / user の 3 スコープで日次レコードを返します。
GUI は Copilot usage metrics policy を有効にすれば Insights タブから見えます。28 日ウインドウで概況をつかむには十分ですし、code generation dashboard で Copilot が実際にどれだけコードを吐いているかが見えるのもありがたい。
ただ、運用が進むと付属の GUI だけでは足りないところが出てきます。
そこで今回、以下のようなダッシュボードを自作してみました。(詳細は後述します。)

で、話を戻しますと、足りない点として例えば、
- 四半期や半期のスパンで傾向を追いたいが、28 日ウインドウでは切り出しにくい
- 少なくとも私の確認環境では、UI だけだと Terraform や Bicep のようなロングテールな言語を継続的に追いにくい
- ダッシュボード単体ではユーザー単位の深掘りが弱く、活用が止まっている層を探るには NDJSON export か API 併用が前提になる
- telemetry を無効にしている開発者のアクティビティはそもそも計上されない
などが挙げられます。
ダッシュボード単体では cycle time や PR throughput とのつながりは見えにくいです。ただ、Copilot usage metrics の reference には organization / enterprise 向けの pull_requests.* fields が入っていて、total_merged や median_minutes_to_merge まで追えます。
API を使えば、期間もスコープも自由になります。API で取れるフィールドの詳細は公式リファレンスにまとまっています。
表でまとめると以下となります。
| 観点 | GUI ダッシュボード | Copilot Metrics API |
|---|---|---|
| データ期間 | 直近 28 日の傾向 | 最大 1 年(日単位で指定) |
| 時系列の粒度 | 28 日トレンド中心 | 日単位で自由に取得 |
| 言語の粒度 | 私の確認環境ではロングテールな言語を追いにくい | 全言語を個別集計しやすい |
| ユーザー別 | ダッシュボード単体では弱い。NDJSON export か API 併用 | user-level エンドポイントあり |
| データ更新ラグ | docs 上は最大 3 UTC 日遅れ | docs 上は 2 full days。運用上は 2〜3 UTC 日を見る |
| 対象外データ | GitHub.com Chat / Mobile は含まれない | GitHub.com Chat / Mobile は含まれない |
| CLI の扱い | 2026 年 4 月時点で docs の記述差あり | CLI activity の専用 fields があり、4 月 10 日以降は top-level totals / breakdowns にも統合 |
| 外部連携 | NDJSON export で持ち出せる | BI ツール、自作ダッシュボード等 |
| ダッシュボード閲覧条件 |
Copilot usage metrics policy を有効化した上で閲覧ロール |
- |
| API のトークン要件 | - | Organization は read:org。Enterprise は manage_billing:copilot / read:enterprise
|
今回のリポジトリは、この API 側を使って自前のダッシュボードを作ったものです。
ちなみに Organization レベルの metrics が出るのは 2025 年 12 月 12 日以降です。最大 1 年分とはいっても、Organization スコープではそこが起点になります。
もう 1 つ、organization レベルの数字を読むときに知っておいたほうがいいことがあります。
あるユーザーの利用状況は、「どのリポジトリで使ったか」ではなく「そのユーザーが所属している organization」に紐づきます。enterprise 内で複数 organization に属しているユーザーは、それぞれの organization のダッシュボードに現れます(enterprise レベルでは重複が排除されます)。
自作ダッシュボードで organization レベルの数字を見せるときは、「この org のリポジトリで発生した量」ではなく「この org のメンバーが使った量」という意味である点は、補足しておいたほうが良いです。
ここは少しややこしいです。GitHub.com 上の Copilot Chat と GitHub Mobile の利用は、Copilot usage metrics には入りません。
一方で CLI は 2026 年春に仕様変更が続いていて、reference docs では「Copilot usage dashboard の charts には CLI は含まれない」と書かれている一方、4 月 10 日の changelog では top-level totals と feature breakdowns に CLI が統合されたと案内されています。
なので本記事では、CLI は反映範囲が更新中の領域として扱うこととします。
2026 年 4 月 10 日を境に top-level totals の意味が変わっているので、その前後で totals を単純比較すると誤読します。
時系列で追うなら、この日付を境に分けて見たほうが安全です。
もう 1 つ、IDE の telemetry を切っている開発者のアクティビティは GUI にも API にも出ません。利用者数が想定より少ないときは、まず telemetry の設定を疑うのがいいかなと思います。
余談ですが、API を使ったダッシュボードとしては、GitHub 公式の copilot-metrics-viewer(v3.0 で新 API に移行済み)もあります。チーム比較や CSV export など機能は充実しているので、まずはこちらを試すのも手です。今回は Azure + OTel で自前構成を通したかったので別で作りましたが、用途によってはこちらで十分だと思います。
ここまでが「なぜ API を使うのか」の話です。次は、実際に API を叩いたときに引っかかったレスポンス形式の話です。
API の返し方が旧 API と違う
最初に引っかかったのはレスポンスの形です。旧 API は JSON 配列をそのまま返していました。新しい Copilot usage metrics API はそうではなく、まず download_links を返して、署名付き URL の先にある NDJSON を取りに行く 2 段階構成です。
| 項目 | 旧 API | Copilot usage metrics API |
|---|---|---|
| エンドポイント | /orgs/{org}/copilot/metrics |
/orgs/{org}/copilot/metrics/reports/... |
| 返り値 | JSON 配列 |
download_links を含む JSON |
| 実データ | レスポンスに直接入る | 署名付き URL 先の NDJSON |
| API バージョン | 2022-11-28 |
2026-03-10 |
response.json() で終わるつもりだったので、正直ここは戸惑いました。新 API のエンドポイントやリクエスト例は REST API リファレンスがわかりやすいです。
ただ、NDJSON 自体は polars がそのまま読めます。1 行ずつ json.loads() しなくてよかったのは助かりました。
旧 API からの移行で、もう 1 つ感覚が変わったのはここです。新 API は「集計済み JSON を 1 回取って終わり」ではなく、日次レコードの束を取り込んで、必要な期間や切り口に合わせてこちらで再集計する前提でした。28 / 60 / 100 日の切り替えやタブごとの要約カードも、その発想で作っています。
def download_ndjson(client: httpx.Client, links: list[str]) -> pl.DataFrame:
"""署名付き URL から NDJSON をダウンロードして結合する。"""
frames: list[pl.DataFrame] = []
for link in links:
resp = client.get(link)
resp.raise_for_status()
df = pl.read_ndjson(io.BytesIO(resp.content))
frames.append(df)
return concat_data_frames(frames)
100 日分のデータは organization-1-day?day=YYYY-MM-DD を日付ごとに叩いて束ねています。
以下の公式ドキュメントでは、ある日のデータは day closes 後 2 full UTC days で利用可能とされています。
一方でダッシュボードの画面表示は up to three full UTC days behind と書かれていて、少しずれがあります。
実装判断としては API 側の 2 full days を根拠に、当日と前日を取りに行かず、終端を date.today() - timedelta(days=2) にしています。
ただし IDE telemetry は 3 UTC 日で落ち着くことが多いとも案内されているので、四半期レビューのように数字を確定値として使う場面では、3 UTC 日待ってから比較するほうが安全です。
28 / 60 / 100 日の切り替えは、その日次データをどこまで切るかの違いです。
API の形はわかったので、次はダッシュボードを作る前に先にやったことである、mock データの話をします。
mock を先に作った理由
今回の環境では、会社のポリシーで実 Organization データを取れなさそうでしたのでmockデータで実装してます。
なので、mock の質がそのままダッシュボードの検証精度になりました。
リポジトリ内にありますが、src/generate_mock.py では、10 人のユーザーにペルソナを持たせています。Agent をよく使う人、review に寄る人、週末はほぼ触らない人。各ユーザーで activity の出方やモード比率を変えているので、「みんな同じ使い方」にはなりません。
言語の偏りも入れました。Python と Go が多い人、TypeScript と CSS に寄る人、HCL や Bicep が跳ねる期間がある人。ここが平坦だと、よくある「Other Languages が多すぎて何もわからない」問題を検証できないので。
API と mock が揃ったところで、いよいよダッシュボードの話に入ります。
今回自作したダッシュボードの 3 画面
全部を縦に並べると長すぎるので、Overview / Agent / Diagnostics の 3 タブに分けました。
ここからはスクリーンショットと一緒に、何を見せたかったかを紹介します。
ダッシュボードに表示されているソースデータはすべて mock データとなっていますがけっこう本物に近いと思います。(GitHub Copilot CLIで生成)
Overview タブ

Overview タブ: DAU・承認率・言語別 activity の全体像
上段に平均 DAU、総プロンプト数、承認率、Agent mode share の要約カードを並べて、その下に DAU 推移と 1 人あたり prompt の日次チャート、さらに言語別 activity の積み上げエリアチャートを置いています。
狙いは、全体像 → 日次の揺れ → 言語内訳の順で読めること。数が伸びた日が何の言語で動いたか、そのまま追いやすいようにしています。GUI ダッシュボードの「Other Languages」問題が気になっていたので、ここでは全言語を個別に出しました。
Agent タブ

Agent タブ: Agent 利用浸透度とモード構成比
monthly active agent users や coding agent active users の推移に加えて、IDE 内の Agent mode 比率や Ask / Edit / Plan の構成比を並べています。
個人的には、このタブがいちばん「導入後の変化」を話しやすいです。
単に何回使われたかではなく、モードの偏りまで見えるので。API の chat_panel_edit_mode や chat_panel_agent_mode といったフィールド名はそのままだと伝わりにくいので、UI 上は Edit / Agent のように短くラベルを付けています。
Diagnostics タブ

Diagnostics タブ: ユーザー別の利用濃度と review engagement
コード生成・承認・Agent edit volume の推移、review engagement の day count、ユーザー別サマリーをまとめています。
ここは、総量グラフだけでは「誰が深く使ったか」が見えないので作りました。user_daily_summary.json を期間ごとに再集計して、何人が何日 review に関わったかまで掘れるようにしています。活用が停滞しているチームや、review engagement が薄いユーザー群を見つけるには向いています。
ただ、seat の棚卸しをこれだけでやるのは危ないです。ライセンスや last_activity_at の正本は Copilot user management API 側なので、seat を剥がす判断まで持っていくなら、そちらを併用するのが筋だと思っています。last_activity_at は 2026 年 4 月 14 日時点で public preview で、IDE だけでなく GitHub.com / Mobile / CLI / pull request summary 由来の activity も含みます。更新には最大 24 時間、保持は 90 日という前提もあります。
これでようやくダッシュボードの中身が決まったので、次はこれをAzureにどう載せるかです。
Azure 構成
構成は Azure App Service + Functions + Cosmos DB + Storage + Key Vault を基本にしつつ、新 API の NDJSON パースを Python でやる方針にしました。Bicep や azure.yaml を含めたコード全体は GitHub リポジトリにあります。
冒頭出しましたが、以下のようなAzureアーキテクチャとなっています。

Azure 構成図: GitHub API → Functions → Blob / Cosmos → App Service → Browser
フロントエンドは React + Vite、バックエンドは Python です。API が旧 JSON 形式から NDJSON に変わったので、polars でパースする部分を Python で書いてしまったほうが楽だった、というのが正直な理由です。
データの流れ
全体の動きはこうなっています。
Azure Functions(Python)が GitHub の Copilot usage metrics API を叩いて、download_links → 署名付き URL → NDJSON の順にデータを取得します。取ってきた生の NDJSON は Blob Storage の raw-metrics コンテナにそのまま保存し、polars で集計した JSON(daily_summary.json / user_summary.json など)は dashboard-data コンテナに書き出します。実行のたびに Cosmos DB に run metadata(いつ・何日分・成功/失敗)を書いておくので、あとから ingestion の履歴を追えます。
ダッシュボード側の App Service は Node.js で動いていて、React のビルド済みファイルを配信しつつ、/api/data/* へのリクエストを Functions にプロキシします。ブラウザからは App Service だけを見ていて、Functions には直接触りません。この構成だと、Functions 側に認証をかけたまま App Service からだけアクセスさせられるので、API キーの露出を気にしなくて済みます。
GitHub Token は Key Vault に入れてあり、Functions からは managed identity で取りに行きます。Application Insights には Functions と App Service の両方から OTel トレースを飛ばしていて、そこから Log Analytics に流れる形です。
他に、Azure 側の構成で意識したところをもう少し書いておきます。
managed identity ベースの設計
Storage の Shared Key を無効にして、Function App のホストストレージも含めて全部 MI で接続しています。AzureWebJobsStorage は __accountName 形式で設定すると、global Azure なら blob / queue / table のエンドポイントを推論してくれます。Key Vault にも MI でアクセスして、AzureWebJobsSecretStorageType=keyvault でホストキーを Key Vault 管理にしています。
RBAC は Bicep 側で全部定義していて、Function App の MI には Storage Blob Data Owner、Storage Table Data Contributor、Key Vault Secrets Officer、Cosmos DB Data Contributor を付けています。Dashboard 側の App Service には Blob Data Reader と Cosmos DB Data Reader だけ。ここがずれるとホストが起動しなくなるので、地味ですがかなり見ました。
IaC と azd
Bicep は subscription スコープで書いて、リソースグループの作成から始めて、Log Analytics、Storage、Cosmos DB、Key Vault、App Service Plan、Web App、Function App、RBAC まで一通りモジュール化しています。azd up 一発でプロビジョニングからデプロイまで通ります。
azure.yaml では dashboard(App Service)と ingestion(Function App)の 2 サービスを定義していて、それぞれ prepackage hook で npm run build と uv export を走らせています。deploy は azd が push deploy でやってくれるので、CI/CD を組まなくても手元から azd deploy --all で反映できます。
Application Insights と OpenTelemetry
APPLICATIONINSIGHTS_CONNECTION_STRING があるときだけ OTel を有効にする設計にしています。Functions 側では httpx のリクエストを HTTPXClientInstrumentor でトレースしているので、API 呼び出しや署名付き URL へのダウンロードが Application Insights 上で追えます。Azure Functions が落ちたときも、FunctionAppLogs と合わせて Log Analytics の KQL で探れるのが助かりました。
Azure で引っかかったところ
一番ハマったのは Azure Functions の Python バージョンです。
以下のMicrosoft Learn では Azure Functions の Python 3.13 は GA として案内されていますが、Linux Consumption プランは 3.12 まで、また 3.13+ では runtime package の扱いが変わるなど、環境ごとに注意点があります。
今回の構成では最初 PYTHON|3.13 で設定していたところ、ホストは起動するのに 0 functions found が続きました。
FunctionAppLogs を追うと、ワーカーが python/3.12 のパスで動いていて、少なくとも今回のデプロイでは 3.13 用の構成と噛み合っていませんでした。PYTHON|3.12 に下げたら関数 4 本がすぐインデックスされました。ここはローカルだとまったく見えなかったです。
もう 1 つは App Service Plan の quota です。リージョンと SKU の組み合わせで「そのリージョンでは作れない」に何度か当たりました。Azure って、コード以前に「そのリージョンで作れるか」が先に来ることがあるんですよね。なので要確認項目です。
ここまでが構築とトラブルシュートの話です。結局どこまで動いたのかを整理しておきます。
確認できたこと
この記事を書いている時点で、Azure 上ではこのあたりまで確認が取れています。
- App Service のダッシュボードが 200 を返し、各タブが描画される
- Functions の
GET /api/data/daily_summary.jsonが JSON を返す -
POST /api/ingestion/runが mock ingestion に成功する - Application Insights にトレースが飛んでいる
まだ試せていないのは、実 Organization データでの Azure 上の検証と、保持期間やコストの実運用です。
この UI が組織の実利用傾向に十分かどうかも、実データで回してみないとわかりませんので、参考程度に見てもらえればと思います。
最後に、このダッシュボードを作る過程自体の話をしておきます。
GitHub Copilot CLI で開発した話
今回のコードおよびmockデータ作成は大半を GitHub Copilot CLI で書きました。
ご参考までに、GitHub Copilot CLIとは?といった話は以下の記事で書いておりますので良ければ覗いてみていただければ幸いです。
ターミナルで copilot コマンドを実行すると対話セッションが始まり、ファイルの作成・編集、シェルコマンドの実行、GitHub.com での PR 作成まで自律的にやってくれるエージェントです。以前の gh copilot 拡張は retired になっていて、今はこのスタンドアロンの Copilot CLI が後継です。
効いたのはコード生成そのものより、前提の固定です。
AGENTS.md や .github/instructions/*.instructions.md に API バージョン、NDJSON の読み方、HTTPXClientInstrumentor を使うことなどを書いておくと、古い endpoint や別解を提案される回数が減りました。Copilot CLI はリポジトリ内のカスタムインストラクションを自動で読んでくれるので、この手の前提共有がとても楽です。
特に Azure 周りでは、Bicep の設定や Application Insights の KQL をゼロから書くより、Copilot CLI に叩き台を出させてから詰めるほうが速かったです。
たとえば Bicep の azd-service-name タグの付け忘れを指摘してくれたり、AzureWebJobsStorage__blobServiceUri のような長い設定名を正確に生成してくれたりと、Microsoft 製品の設定値は細かい名前の違いでハマりやすいので、この使い方はかなり効きました。Application Insights 側でも、FunctionAppLogs から障害調査に使える KQL を Copilot CLI に書かせて、そこから入る形が多かったです。
Azure 構成と Copilot CLI の組み合わせは、「コード生成」というより「インフラ設定の加速」という側面で効きました。
たとえば今回のリポジトリでは、Python 向けの instruction に次のような前提を書いていました。
- 非同期が不要なら `httpx.Client()` を使う
- タイムアウトは明示的に設定する(`timeout=30`)
- `response.raise_for_status()` でエラーチェックする
- API バージョンヘッダー: `X-GitHub-Api-Version: 2026-03-10`
- レスポンスは `download_links` を含む JSON → リンク先は NDJSON
- `HTTPXClientInstrumentor().instrument()` で有効化する
検証では途中で方針が変わるのが普通ですが、変わった前提を毎回思い出さなくていいのは大きかったです。
おわりに
GitHub Copilot usage metrics API で、GUI だけでは足りなかった部分を自前で可視化してみました。最大 1 年分の時系列、全言語の内訳、ユーザー単位のドリルダウンまで mock で Azure 実機に通せたので、実データが使える環境になったらそのまま差し替えて試すつもりです。
次にやりたいのは、Cosmos DB に残した run 履歴から Agent 利用の伸びを時系列で追えるようにすることと、Copilot CLI の OpenTelemetry トレースと組み合わせて、開発プロセス全体の observability を一枚にすることです。
再掲となりますが、今回のリポジトリはこちらです。
以上です!お読みいただきありがとうございました!
参考リンク
記事中で紹介したリンクのほか、以下も参考になります。
また、私の他のGitHub Copilot関係記事もご参考までにリンクを置いておきます。
免責事項
本記事は筆者個人の見解であり、所属組織の公式見解ではありません。記事中のコマンドやコード例は、環境によって動作が異なる場合があります。また、記載されたサービスや機能はプレビュー段階のものを含み、仕様は予告なく変更される場合があります。
本記事は情報提供を目的としており、2026年4月14日時点の情報に基づいています。本記事について、内容の正確性・完全性は保証されず、誤りを含む可能性があります。公式ドキュメントで最新情報をご確認ください。記事内のコードサンプルおよび筆者のGitHubリポジトリは自己責任でご利用ください。本記事内容の利用によって生じたいかなる損害(サービスの中断、データ損失、営業損失等を含む)についても、著者は一切の責任を負いません。本記事に掲載されている各社製品・サービスは各社の利用規約に従ってご利用ください。
Discussion