Backlog API で課題一覧を取得し CSV 出力する手順|Python の実装例

  • URLをコピーしました!
目次

はじめに

Backlog はプロジェクト管理ツールですが、毎日の進捗確認や未消化課題のリストアップなど、画面を操作して情報を集める作業に手間を感じる場面があります。

Web ブラウザで行う操作の多くは、Backlog API を使うことでプログラムから自動化できます。本記事では、Python を使って Backlog の課題情報を取得し、レポート(CSV)として出力するまでの手順を解説します。100 件を超える課題の全件取得や、プロジェクトごとに異なるステータスへの対応など、実プロジェクトで動かすための実装まで扱います。

この記事でわかること
  • Backlog API を使うための事前準備(API キー、プロジェクト ID の確認方法)
  • Python で Backlog から課題データを取得する基本コード
  • 100 件を超える課題を全件取得するページネーションの方法
  • 未完了かつ高優先度の課題を抽出し、CSV ファイルに出力する方法

Backlog API は、API キーとプロジェクト ID があれば requests 数行から課題データを取得できます。本記事では、まず基本の取得を確認し、続いて全件取得と条件絞り込み、CSV 出力へと段階的に進めます。API キーの扱いやレート制限など、実務で押さえておきたい注意点もあわせて解説します。

事前準備: API キーとプロジェクト ID の確認

Python から Backlog を操作するには、認証情報の API キーと、操作対象を指定するプロジェクト ID の 2 つが必要です。

API キーの取得

API キーは、個人のアカウントに紐付く認証情報です。第三者に渡るとアカウントの操作が可能になるため、パスワードと同様に扱います。

  1. Backlog にログインし、画面右上のアイコンから「個人設定」を開きます。
  2. 左メニューの「API」をクリックします。
  3. メモ欄(例: python_script など)を入力し、「登録」ボタンをクリックします。
  4. 発行された文字列が API キーです。後でコードから参照するため、控えておきます。

次の URL の xxxx を自分のスペース ID(ドメイン)に置き換えると、設定画面へ直接アクセスできます。

https://xxxx.backlog.com/EditApiSettings.action

なお、Backlog のスペースのドメインは契約によって .backlog.com.backlog.jp.backlogtool.com のいずれかになります。自分のスペースの URL を確認し、コード内のドメインを合わせてください。

API キーの扱いに関する注意

本記事のコードでは、API キーをソースコードに直接書き込まず、環境変数から読み込む形にします。Backlog API では API キーをリクエストの URL(クエリ文字列)に付与するため、URL がアクセスログやブラウザ履歴、共有時のコピーなどに残ると、キーが漏れる可能性があります。ソースに直書きしない、URL をそのまま共有しない、漏れた可能性がある場合は設定画面でキーを削除・再発行する、といった運用をおすすめします。

シェルでの環境変数の設定例は次のとおりです。

export BACKLOG_API_KEY="あなたのAPIキー"
export BACKLOG_SPACE_ID="xxxxx"

参考: Backlog API Overview(Nulab Developer)
“You can access the Backlog API in two ways: (1) API key or (2) OAuth 2.0.”
(Backlog API には API キーと OAuth 2.0 の 2 つのアクセス方法があります。)
https://developer.nulab.com/docs/backlog/

プロジェクト ID の確認

次に、操作対象となるプロジェクトの ID を確認します。

  1. 対象のプロジェクトを開き、左メニューの「プロジェクト設定」をクリックします。
  2. その画面を開いた状態で、ブラウザのアドレスバー(URL)を確認します。
  3. URL の末尾にある project.id=XXXXXX の数字部分がプロジェクト ID です。

API によってはプロジェクトキー(例: PROJ)でも動作しますが、本記事では確実なプロジェクト ID(数値)を使用します。

課題一覧データを取得する(基本編)

ここから実際のコードに入ります。API との通信には、Python の標準的な HTTP ライブラリである requests を使用します。インストールしていない場合は、次のコマンドで追加します。

pip install requests

基本の取得コード

まずは、指定したプロジェクトの課題一覧を取得して表示するスクリプトです。前項で設定した環境変数(BACKLOG_API_KEYBACKLOG_SPACE_ID)から認証情報を読み込みます。スペースのドメインは契約により異なるため、.backlog.com 以外を利用している場合は DOMAIN を変更します。

import os
import pprint
import requests

# 環境変数から認証情報を読み込む
API_KEY = os.environ["BACKLOG_API_KEY"]
SPACE_ID = os.environ["BACKLOG_SPACE_ID"]   # 例: xxxxx(https://xxxxx.backlog.com の xxxxx 部分)
DOMAIN = "backlog.com"                       # .backlog.jp / .backlogtool.com の場合は変更する
PROJECT_ID = 12345                           # 確認したプロジェクト ID(数値)

def main():
    url = f"https://{SPACE_ID}.{DOMAIN}/api/v2/issues"
    params = {
        "apiKey": API_KEY,
        "projectId[]": [PROJECT_ID],   # 配列で指定する
        "count": 20,                   # 取得件数(既定 20、最大 100)
    }

    try:
        response = requests.get(url, params=params)
        response.raise_for_status()    # エラーがあれば例外を発生させる
        issues = response.json()
        pprint.pprint(issues)
    except requests.exceptions.RequestException as e:
        print(f"エラーが発生しました: {e}")

if __name__ == "__main__":
    main()

実行すると、課題ごとの情報を含む JSON(辞書のリスト)が返ります。1 件には issueKey(課題キー)、summary(件名)、status(ステータス)、priority(優先度)、assignee(担当者)、dueDate(期限日)などが含まれます。これが Backlog の課題の生データです。

100 件を超える課題を全件取得する(ページネーション)

課題一覧 API は、1 回のリクエストで最大 100 件までしか返しません。課題が 100 件を超えるプロジェクトでは、offset(取得開始位置)をずらしながら複数回リクエストして、全件を集める必要があります。返却件数が count 未満になったら、最後のページに達したと判断できます。

ここで、レート制限への配慮も加えます。Backlog API は上限を超えると 429 を返すため、リクエストを直列で行い、各リクエストの間に短い待機を入れます。

import os
import time
import requests

API_KEY = os.environ["BACKLOG_API_KEY"]
SPACE_ID = os.environ["BACKLOG_SPACE_ID"]
DOMAIN = "backlog.com"
PROJECT_ID = 12345

def fetch_all_issues(extra_params=None):
    """課題を全件取得して 1 つのリストにまとめて返す"""
    url = f"https://{SPACE_ID}.{DOMAIN}/api/v2/issues"
    count = 100          # 1 回の最大取得件数
    offset = 0
    all_issues = []

    while True:
        params = {
            "apiKey": API_KEY,
            "projectId[]": [PROJECT_ID],
            "count": count,
            "offset": offset,
            "sort": "dueDate",
            "order": "asc",
        }
        # 絞り込み条件などを追加で渡せるようにする
        if extra_params:
            params.update(extra_params)

        response = requests.get(url, params=params)
        response.raise_for_status()
        issues = response.json()

        all_issues.extend(issues)

        # 取得件数が count 未満なら最後のページ
        if len(issues) < count:
            break

        offset += count
        time.sleep(1)    # レート制限に配慮して間隔を空ける

    return all_issues

if __name__ == "__main__":
    issues = fetch_all_issues()
    print(f"取得した課題数: {len(issues)} 件")

fetch_all_issuesoffset を 100 ずつ進めながら、返却件数が 100 未満になるまで取得を繰り返します。extra_params を用意しておくと、後述の絞り込み条件(ステータスや優先度)を渡して再利用できます。

参考: Rate Limit(Nulab Developer)
“Make requests for a single user serially. Do not make requests for a single user concurrently.”
(単一ユーザーのリクエストは直列で行い、並列にしないでください。)
https://developer.nulab.com/docs/backlog/rate-limit/

条件を絞り込んで CSV 出力する(応用編)

全件取得ができたら、次は必要なデータだけを抽出します。実務では「未完了のまま放置されている高優先度の課題」をリストアップしたい場面が多いためです。

カスタムステータスに対応した絞り込み

Backlog API は、リクエスト時にパラメータで条件を渡すと、サーバー側で課題を絞り込んで返します。ここで注意したいのが、ステータスの指定方法です。

Backlog の初期ステータスは未対応(1)・処理中(2)・処理済み(3)・完了(4)の 4 つですが、プロジェクトごとにカスタムステータスを追加できます。初期の 4 つに加えて最大 8 つまで追加できるため、ステータス ID は 1〜4 の範囲に収まるとは限りません。 そのため「完了以外」を statusId[]=[1, 2, 3] と決め打ちすると、カスタムステータスの課題を取りこぼします。

堅牢にするには、プロジェクトのステータス一覧を取得し、完了(ID 4)を除いた ID を動的に組み立てます。ステータス一覧は、プロジェクト単位の API(/api/v2/projects/:projectIdOrKey/statuses)で取得します。なお、スペース全体のステータスを返す旧 API は非推奨となり、このプロジェクト単位の API への置き換えが案内されています。

def fetch_open_status_ids():
    """プロジェクトのステータスから「完了」以外の ID を返す(カスタムステータス対応)"""
    url = f"https://{SPACE_ID}.{DOMAIN}/api/v2/projects/{PROJECT_ID}/statuses"
    params = {"apiKey": API_KEY}

    response = requests.get(url, params=params)
    response.raise_for_status()
    statuses = response.json()

    CLOSED_STATUS_ID = 4   # 初期ステータスの「完了」
    # 完了(4)以外を未完了として扱う(カスタムステータスも自動的に含まれる)
    return [s["id"] for s in statuses if s["id"] != CLOSED_STATUS_ID]

fetch_open_status_ids は、完了(ID 4)を除いたすべてのステータス ID を返します。カスタムステータスは ID 4 と重複しないため、これだけで未完了側に自動的に含められます。

参考: Get Status List of Project(Nulab Developer)
“Returns list of status in the project.”
(プロジェクト内のステータス一覧を返します。)
https://developer.nulab.com/docs/backlog/api/2/get-status-list-of-project/

CSV 出力のコード

取得した課題を、Excel でそのまま開ける CSV(BOM 付き UTF-8)として保存します。前項の fetch_all_issues に絞り込み条件を渡して全件取得し、必要な列だけを書き出します。

import csv

def export_high_priority_issues(output_file="high_priority_issues.csv"):
    # 完了以外のステータス ID を動的に取得する
    open_status_ids = fetch_open_status_ids()

    extra_params = {
        "statusId[]": open_status_ids,   # 完了以外(カスタムステータス含む)
        "priorityId[]": [2],             # 優先度: 高
    }

    issues = fetch_all_issues(extra_params)

    if not issues:
        print("条件に一致する課題はありませんでした。")
        return

    # encoding="utf_8_sig" で Excel の文字化けを防ぐ
    with open(output_file, "w", encoding="utf_8_sig", newline="") as f:
        fieldnames = ["課題キー", "件名", "担当者", "ステータス", "優先度", "期限日", "詳細URL"]
        writer = csv.DictWriter(f, fieldnames=fieldnames)
        writer.writeheader()

        for issue in issues:
            # 担当者が未設定の場合に備える
            assignee = issue["assignee"]["name"] if issue.get("assignee") else "(未設定)"

            writer.writerow({
                "課題キー": issue["issueKey"],
                "件名": issue["summary"],
                "担当者": assignee,
                "ステータス": issue["status"]["name"],
                "優先度": issue["priority"]["name"],
                "期限日": issue["dueDate"] if issue.get("dueDate") else "(指定なし)",
                "詳細URL": f"https://{SPACE_ID}.{DOMAIN}/view/{issue['issueKey']}",
            })

    print(f"'{output_file}' に {len(issues)} 件の課題を出力しました。")

if __name__ == "__main__":
    export_high_priority_issues()

ポイントは 2 点です。statusId[]priorityId[] をリストで渡すと、複数条件を組み合わせて検索できます。また encoding="utf_8_sig"(BOM 付き UTF-8)を指定すると、Windows の Excel で開いたときの文字化けを回避でき、Shift-JIS への変換は不要です。

参考: プロジェクト一覧を CSV 出力する

同じ要領で、参加しているプロジェクトの一覧も CSV 化できます。プロジェクト ID やキーを一覧で確認したいときに便利です。

import csv

def export_projects(output_file="projects.csv"):
    url = f"https://{SPACE_ID}.{DOMAIN}/api/v2/projects"
    params = {"apiKey": API_KEY}

    response = requests.get(url, params=params)
    response.raise_for_status()
    projects = response.json()

    with open(output_file, "w", encoding="utf_8_sig", newline="") as f:
        fieldnames = ["プロジェクトID", "プロジェクトキー", "プロジェクト名"]
        writer = csv.DictWriter(f, fieldnames=fieldnames)
        writer.writeheader()

        for p in projects:
            writer.writerow({
                "プロジェクトID": p["id"],
                "プロジェクトキー": p["projectKey"],
                "プロジェクト名": p["name"],
            })

    print(f"'{output_file}' に {len(projects)} 件のプロジェクトを出力しました。")

取得した CSV は、表計算ソフトでの集計や、別の可視化スクリプトへの入力として活用できます。CSV を読み込んでグラフ化する方法は、関連記事『Python で相関分析を行う手順』や『Plotly でガントチャートを作成する手順』も参考になります。

まとめ

本記事では、Python で Backlog API を操作し、課題を取得して CSV 化する方法を解説しました。あわせて、全件取得やカスタムステータスへの対応、API キーの扱いなど、実務で押さえておきたい点も整理しました。

  • API キーは環境変数で管理し、URL への露出に注意
  • プロジェクト ID は設定画面の URL から確認
  • 課題一覧 API は 1 回最大 100 件、offset で全件取得
  • ステータスはプロジェクト単位で取得し完了以外を動的指定
  • カスタムステータスは初期 4 つに加えて最大 8 つ追加可能
  • レート制限に配慮し、直列かつ間隔を空けて実行
  • CSV は utf_8_sig で Excel の文字化けを回避

以上、最後までお読みいただきありがとうございました。

よかったらシェアしてね!
  • URLをコピーしました!

この記事を書いた人

関西を拠点に活動する、現役インフラエンジニア。経験20年超。

大手通信キャリアにて、中〜大規模インフラ(ネットワーク・サーバ・クラウド・セキュリティ)の設計・構築およびプロジェクトマネジメントに従事。現場で直面した技術課題への対処や、最新の脆弱性情報への実務対応を、一次情報として発信しています。

保有資格
CCIE Lifetime Emeritus(取得から20年以上)/ VCAP-DCA / Azure Solutions Architect Expert

▶ 運営者プロフィール(詳細)

目次