0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Qiita APIで記事が404になる理由|item_idの取り方と確認

0
Posted at

Qiita APIで記事を取得しようとした際「404 Not Found」が発生してデータが取れない原因は、item_idの指定間違いや限定共有記事の認証不備にあります。

  • item_idは記事URL末尾にある20桁の16進数文字列であり、ユーザー名や記事タイトル文字列(スラグ)ではありません
  • 限定共有(プライベート)記事の取得には read_qiita スコープを含むアクセストークンが必要であり、未認証アクセスでは404エラー(または権限エラー)が返されます
  • API実行時はステータスコード(404, 401, 403, 429等)を必ず判定し、レート制限や認証状態に応じた適切なエラー処理を実装する必要があります

Qiitaにおけるitem_idの役割と正しい抽出方法

Qiita API v2を利用して記事を取得・更新・削除する場合、リクエストのパスパラメータとして item_id(記事ID)を指定します。Qiita APIにおける item_id は、Qiita上のすべての記事に個別に割り当てられる一意の識別子です。

item_idのデータ構造と仕様

公式ドキュメントにおける item_id のデータ型およびパターンは以下の通り規定されています。

  • 型: 文字列(string)
  • 文字数: 20文字
  • 形式: 半角の小文字英数字(16進数)
  • 正規表現パターン: ^[0-9a-f]{20}$

例えば、c686397e4a0f4f11683d のような20桁の文字列が正しい item_id です。

Web画面のURLからitem_idを取得する手順

ブラウザでQiitaの記事を開いたとき、アドレスバーに表示されるURLは以下の構造になっています。

https://qiita.com/{user_id}/items/{item_id}

具体例として、URLが https://qiita.com/Qiita/items/c686397e4a0f4f11683d の場合、末尾の c686397e4a0f4f11683d が item_id となります。

開発時に混同しやすい要素として、ユーザーID(user_id)や記事のURL全体があります。以下の対応表を参考に、正しいパラメータのみをAPIに渡すようにしてください。

URLに含まれる要素 例 Qiita APIパスパラメータでの扱い
プロトコル・ホスト https://qiita.com ベースURLとして指定(https://qiita.com/api/v2)
ユーザーID Qiita 記事単体取得のパスには含めない
パス識別子 /items/ APIエンドポイントの固定パス
item_id c686397e4a0f4f11683d :item_id に指定する文字列(20桁)

よくある指定ミスのパターン

APIを呼び出す際に404エラーが発生する場合、まずは指定しているリクエスト文字列が以下のようになっていないか確認してください。

  1. URL全体をパラメータに設定している
    GET /api/v2/items/https://qiita.com/Qiita/items/c686397e4a0f4f11683d のようにURL全体を渡すと、存在しないリソースとして404になります。
  2. ユーザー名を含めてしまっている
    GET /api/v2/items/Qiita/c686397e4a0f4f11683d のようなパスはQiita API v2の仕様に存在しないため404となります。
  3. 文字列内に余分なスペースや改行が含まれている
    スクレイピングや設定ファイルから読み込む際に空白文字が混入すると、20桁の正規表現にマッチせずエラーの原因になります。

Qiita APIで404 Not Foundが発生する主な原因

Qiita API v2において、単一の記事情報を取得するエンドポイントは GET /api/v2/items/:item_id です。このエンドポイントに対してリクエストを送った際にレスポンスステータスコード 404 Not Found が返される理由は、単なる文字列の入力ミスだけにとどまりません。主な原因は以下の5点に集約されます。

1. item_idの指定ミス(存在しないID)

前述の通り、item_id の桁数が不足している場合や、16進数以外の文字(大文字や記号)が含まれている場合、当然ながら該当する記事が存在しないためサーバー側で404が返されます。

2. 記事が既に削除されている

過去に存在していた記事であっても、投稿者によって削除された場合、その item_id に対するアクセスは404レスポンスとなります。APIのレスポンスボディには以下のようなJSON形式のエラーメッセージが返されます。

{
  "message": "Not found",
  "type": "not_found"
}

3. 限定共有記事(プライベート記事)への未認証アクセス

Qiitaには投稿時に「限定共有」として公開範囲を制限した記事が存在します(APIレスポンス上では private: true と表されます)。
限定共有記事は、URLを知っているユーザーや投稿者本人のみが閲覧できます。未認証のリクエスト(アクセストークンを付与しない状態)で限定共有記事の item_id を指定して GET /api/v2/items/:item_id を呼び出すと、セキュリティおよびプライバシーの観点から記事の存在自体を隠蔽するため、403 Forbidden ではなく 404 Not Found が返されます。

4. エンドポイントのパス記述ミス

APIのベースURLやパスの記述が間違っている場合も404になります。
正解のパスは https://qiita.com/api/v2/items/:item_id です。スラッシュの重複(//api/v2/...)や、v1 などの古いバージョン表記を指定していないか確認してください。

5. Qiita Teamの記事に対して標準ドメインを使用している

Qiita Team(組織向けサービス)内の記事を取得する場合は、標準の qiita.com ドメインではなく、チーム専用のサブドメイン(https://{team_name}.qiita.com/api/v2/items/:item_id)にアクセスする必要があります。qiita.com 側に対してチーム記事の item_id を要求しても、該当記事が見つからず404エラーとなります。


限定共有記事の取得とアクセストークンのスコープ設定

限定共有記事の情報をAPI経由で正常に取得するには、認証済みのリクエストを送信する必要があります。ここではアクセストークンの発行とスコープの仕様について解説します。

アクセストークンのスコープ仕様

Qiita API v2では、アクセストークンごとに実行できる操作の権限範囲(スコープ)が定められています。公式ドキュメントに定義されているスコープは以下の4種類です。

  • read_qiita: Qiita上の認証ユーザーに関連するデータの読み取りを許可
  • read_qiita_team: Qiita Team上のデータの読み取りを許可(※Qiita Team専用)
  • write_qiita: Qiita上のデータの書き込み(投稿・編集・削除など)を許可
  • write_qiita_team: Qiita Team上のデータの書き込みを許可(※Qiita Team専用)

自身が投稿した限定共有記事や閲覧権限を持つデータを取得するには、少なくとも read_qiita のスコープが付与されたアクセストークンを使用する必要があります。

HTTPリクエストヘッダーへの設定方法

取得したアクセストークンは、リクエスト送信時に Authorization HTTPヘッダーへ設定します。スキームには Bearer を使用します。

GET /api/v2/items/c686397e4a0f4f11683d HTTP/1.1
Host: qiita.com
Authorization: Bearer 1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcd

トークンをヘッダーに設定せずに限定共有記事へアクセスすると404エラーになり、付与したトークンのスコープに read_qiita が含まれていない場合は権限不足によりエラーとなります。

トークンの安全な管理(環境変数 QIITA_TOKEN)

ソースコード内にアクセストークンを直接ハードコードすることはセキュリティ上大変危険です。Gitなどのバージョン管理システムに誤ってコミットしてしまうと、第三者にアカウントを不正利用されるリスクが生じます。

APIを呼び出すプログラムを作成する際は、必ず環境変数(例: QIITA_TOKEN)からトークンを読み込む構成にしてください。


Qiita APIのステータスコードとエラー判定の仕組み

Qiita API v2から返されるHTTPステータスコードを正しく理解することで、発生した問題が「記事が存在しない(404)」のか、「認証の問題(401/403)」なのか、「アクセス過多(429)」なのかを迅速に切り分けることができます。

主要なステータスコード一覧

Qiita APIで返却される代表的なステータスコードと、その発生理由・対応策は下表の通りです。

ステータスコード 意味 発生原因 対応策
200 OK 成功 GET, PATCH リクエストが成功した 正常に処理を継続
201 Created 成功 POST リクエストでリソースが作成された 新規投稿やコメント作成成功
204 No Content 成功 PUT, DELETE リクエストが成功した(ボディなし) ストック追加や記事削除成功
400 Bad Request クライアントエラー パラメータの形式不備や必須パラメータの欠落 リクエストボディ(JSON)やクエリを確認
401 Unauthorized 認証エラー アクセストークンが無効・期限切れ・未指定 有効なアクセストークンを設定
403 Forbidden 認可エラー アカウントの権限不足、またはスコープ不足 必要なスコープ(read_qiita等)を再確認
404 Not Found リソース未検出 存在しない item_id や限定共有記事への未認証アクセス item_id の確認および認証状態の確認
429 Too Many Requests レート制限超過 1時間あたりのリクエスト上限を超過した 時間経過を待つか、認証リクエストに切り替え
500 Internal Server Error サーバーエラー Qiitaサーバー側の内部障害 しばらく時間をおいてから再試行

エラー発生時のJSONレスポンス形式

4xxや5xxのエラーが発生した際、APIは以下のフォーマットで人間が読めるメッセージと機械判別用のタイプを返します。

{
  "message": "Not found",
  "type": "not_found"
}

プログラム側では、HTTPステータスコードのチェックに加えて、このレスポンスボディの message や type を解析することで詳細なエラーログを出力できます。


レート制限とページネーションパラメータの仕様

API呼び出しで404以外のエラーが発生する代表的なケースが、**レート制限(Rate Limit)**の超過です。連続して大量のリクエストを送信するスクリプトを作成する場合は、レート制限とページネーションの正しい仕様を把握しておく必要があります。

レート制限(Rate Limit)の公式ルール

Qiita API v2では、サービスの安定性を保つためにクライアントごとのリクエスト回数が制限されています。

  • 認証済みリクエスト: 1時間あたり最大 1,000 回(アクセストークンを使用)
  • 未認証リクエスト: 1時間あたり最大 60 回(接続元のIPアドレス単位)

未認証状態では1分間に1回程度のペースでも上限に達してしまうため、自動化スクリプトやバッチ処理を作成する際は、アクセストークンの利用が強く推奨されます。

制限を超過した場合、APIは 429 Too Many Requests ステータスコードを返します。現在の利用状況や残り回数は、APIレスポンスのHTTPヘッダー(RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset など)から確認できます。

ページネーション(Pagination)パラメータの仕様

記事一覧を取得するエンドポイント(例: GET /api/v2/items や GET /api/v2/authenticated_user/items)では、1回のリクエストで全件を取得することはできません。ページネーションを利用して分割取得します。

  • page: ページ番号(指定可能範囲: 1 ~ 100、デフォルト: 1)
  • per_page: 1ページあたりの取得件数(指定可能範囲: 1 ~ 100、デフォルト: 20)

ページネーション対応のエンドポイントでは、レスポンスヘッダーに Link および Total-Count が含まれます。

Total-Count: 60
Link: <https://qiita.com/api/v2/items?page=1&per_page=20>; rel="first",
      <https://qiita.com/api/v2/items?page=1&per_page=20>; rel="prev",
      <https://qiita.com/api/v2/items?page=3&per_page=20>; rel="next",
      <https://qiita.com/api/v2/items?page=3&per_page=20>; rel="last"

範囲外のページ数(例: page=101 以上)を指定するとパラメータ不正としてエラーになるため注意してください。


Pythonによる記事取得とエラー判定の実践実装

ここからは、Pythonの requests ライブラリを使用して、Qiita APIから記事を取得する具体的なコード例を紹介します。環境変数 QIITA_TOKEN を利用し、404エラーをはじめとする各種ステータスコードを適切にハンドリングする構成になっています。

サンプル1:指定したitem_idの記事を取得・検証するスクリプト

特定の item_id を指定して GET /api/v2/items/:item_id を呼び出し、ステータスコードごとに処理を分岐する最小限の実行可能コードです。

import os
import requests

def get_qiita_item(item_id: str):
    # 公式エンドポイントの組み立て
    url = f"https://qiita.com/api/v2/items/{item_id}"
    
    # 環境変数からアクセストークンを取得
    qiita_token = os.environ.get("QIITA_TOKEN")
    
    headers = {}
    if qiita_token:
        headers["Authorization"] = f"Bearer {qiita_token}"
    
    try:
        response = requests.get(url, headers=headers, timeout=10)
        
        # ステータスコードの判定
        if response.status_code == 200:
            item_data = response.json()
            print("=== 記事情報の取得に成功しました ===")
            print(f"タイトル  : {item_data.get('title')}")
            print(f"投稿者    : {item_data.get('user', {}).get('id')}")
            print(f"限定共有  : {item_data.get('private')}")
            print(f"URL       : {item_data.get('url')}")
            return item_data
            
        elif response.status_code == 404:
            print(f"[エラー 404] 指定された item_id ('{item_id}') の記事が見つかりません。")
            print("原因の可能性: IDのタイポ、記事の削除、または未認証による限定共有記事へのアクセス。")
            
        elif response.status_code == 401:
            print("[エラー 401] アクセストークンが無効または有効期限切れです。")
            
        elif response.status_code == 403:
            print("[エラー 403] 閲覧権限がありません。トークンのスコープ(read_qiita)を確認してください。")
            
        elif response.status_code == 429:
            print("[エラー 429] レート制限を超過しました。しばらく時間を置いてから再試行してください。")
            
        else:
            print(f"[エラー {response.status_code}] リクエストに失敗しました: {response.text}")
            
    except requests.exceptions.RequestException as e:
        print(f"通信エラーが発生しました: {e}")
        
    return None

if __name__ == "__main__":
    # テスト実行(確認したいitem_idを指定してください)
    target_item_id = "c686397e4a0f4f11683d"
    get_qiita_item(target_item_id)

サンプル2:自分の記事一覧からitem_idを自動抽出し検証するスクリプト

自分の投稿した記事(限定共有記事を含む)の一覧を取得(GET /api/v2/authenticated_user/items)し、正しい item_id を抽出して表示する実践的なスクリプトです。

import os
import requests

def fetch_authenticated_user_items():
    qiita_token = os.environ.get("QIITA_TOKEN")
    
    if not qiita_token:
        print("エラー: 認証ユーザーの記事を取得するには環境変数 QIITA_TOKEN が必要です。")
        return
    
    url = "https://qiita.com/api/v2/authenticated_user/items"
    headers = {
        "Authorization": f"Bearer {qiita_token}"
    }
    params = {
        "page": 1,
        "per_page": 20
    }
    
    try:
        response = requests.get(url, headers=headers, params=params, timeout=10)
        
        if response.status_code == 200:
            items = response.json()
            print(f"=== 取得成功: {len(items)} 件の記事を取得しました ===")
            
            for index, item in enumerate(items, start=1):
                item_id = item.get("id")
                title = item.get("title")
                is_private = item.get("private")
                status = "限定共有" if is_private else "公開"
                
                print(f"[{index}] ID: {item_id} | 状態: {status} | タイトル: {title}")
                
        elif response.status_code == 401:
            print("[エラー 401] 認証に失敗しました。QIITA_TOKEN の値を確認してください。")
            
        elif response.status_code == 403:
            print("[エラー 403] スコープ権限が不足しています。'read_qiita' スコープを付与してください。")
            
        elif response.status_code == 429:
            print("[エラー 429] 1時間あたりのリクエスト上限(1,000回)に達しました。")
            
        else:
            print(f"[エラー {response.status_code}] 記事一覧の取得に失敗しました: {response.text}")
            
    except requests.exceptions.RequestException as e:
        print(f"通信エラーが発生しました: {e}")

if __name__ == "__main__":
    fetch_authenticated_user_items()

Qiita Team専用機能と廃止済み機能に関する注意点

Qiita APIを利用する際、公式ドキュメントに記載されているエンドポイントが「Qiita Team専用」であるか、あるいは「廃止済みAPI」であるかを区別することが不可欠です。存在しないエンドポイントや権限外のエンドポイントを呼ぶことも404やエラーの原因になります。

Qiita Team専用機能の整理

以下の機能・エンドポイントは Qiita Team(組織向け環境)でのみ利用可能な専用機能です。通常のQiita(qiita.com)に対して使用することはできません。

  • グループ管理機能(GET/POST/PATCH/DELETE /api/v2/groups 等)
  • テンプレート拡張 preview(POST /api/v2/expanded_templates)
  • 絵文字リアクション機能(POST/DELETE /api/v2/items/:item_id/reactions 等)
  • 記事のアクション取得(GET /api/v2/items/reactions)
  • チームアクセストークン発行(POST /api/v2/team_access_tokens)
  • SSO(シングルサインオン)制限(SSOが設定されたチームへのアクセスはチームアクセストークンが必須)

個人用のQiita開発でこれらのエンドポイントを呼び出すと、リソースが存在しないため404エラーや403エラーが返されます。

過去に廃止された機能(現役機能として使用不可)

Qiita API v2の仕様変更に伴い、以下の機能は既に廃止(Abolished)されています。実装時に間違えて呼び出さないよう注意してください。

  1. プロジェクト機能(Project)
    • 廃止日: 2022年10月1日
    • 廃止エンドポイント: GET/POST/PATCH/DELETE /api/v2/projects およびそのコメント・リアクション関連API全般
  2. コメントへのThank機能(Thank / Team Like)
    • 廃止日: 2020年11月4日
    • 廃止エンドポイント: PUT/DELETE /api/v2/comments/:comment_id/thank

なお、標準的な記事に対する「いいね」(PUT/DELETE /api/v2/items/:item_id/like)や「ストック」(PUT/DELETE /api/v2/items/:item_id/stock)は、現在も現役機能として利用可能です。


まとめ

Qiita APIで記事取得時に発生する「404 Not Found」エラーの解決ポイントをまとめます。

  • item_id の仕様を正確に把握する: URL末尾の20桁の16進数文字列(^[0-9a-f]{20}$)のみ抽出し、ユーザー名やURL全体をパスに含めない
  • 限定共有記事には認証を適用する: read_qiita スコープを持つアクセストークンを Authorization: Bearer <TOKEN> ヘッダーに付与してリクエストする
  • ステータスコードによるエラーハンドリングを徹底する: 404(未検出/権限なし)だけでなく、401(未認証)、403(スコープ不足)、429(レート制限オーバー)を区別して処理する
  • レート制限を守る: 未認証(60回/時)と認証済み(1,000回/時)の上限を意識し、ページネーション(page, per_page: 各1?100)を活用する
  • Qiita Team専用・廃止APIに注意する: グループやリアクション機能はTeam専用であり、廃止済みのProject機能などは呼び出さない

開発・運用時には環境変数 QIITA_TOKEN を活用し、安全かつ堅牢なAPI連携を実装しましょう。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?