WebAPIを扱うとき、もっとも頻繁に目にするのがHTTPステータスコードです。
200や404といった数字の意味を正しく理解していないと、原因調査やエラーハンドリングで大きく遠回りしてしまいます。
本記事では代表的なステータスコードの意味を体系的に整理し、Pythonからの扱い方までを図解とサンプルコード付きで丁寧に解説します。
WebAPIステータスコードとは
ステータスコードの基本と役割

WebAPIのステータスコードとは、HTTPレスポンスに含まれる「処理結果を表す3桁の番号」です。
クライアントからサーバーにリクエストを送信すると、サーバーは処理結果とともに応答を返しますが、そのとき「成功したのか」「リダイレクトが必要なのか」「クライアント側のミスなのか」「サーバー側の障害なのか」を機械的に判断できるようにするために、この番号が使われます。
人間にとってはメッセージ本文からでも結果を読み取れますが、プログラムは本文の自然文を理解できません。
そのため機械が即座に意味を判断できる「標準化された番号」としてステータスコードが定義されているのです。
HTTPレスポンスとの関係

HTTPレスポンスは、一般的に次のような構造を持っています。
- ステータスライン
- レスポンスヘッダ
- レスポンスボディ
このうちステータスコードが含まれるのは「ステータスライン」です。
例えば次のようなレスポンスをイメージすると分かりやすいです。
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 123
{"message": "success", "user_id": 1}
ここで200がステータスコード、OKはそのテキスト表現です。
PythonなどのHTTPクライアントはまずステータスコードを確認し、その後ボディ(JSONなど)を解析するという流れで処理を行います。
ステータスコードの分類
1xx情報レスポンスの概要
ステータスコードは先頭の1桁によって大きく5つに分類されます。
1xxは「情報レスポンス」に分類され、処理がまだ継続中であることなどを示します。
代表的なものとして100 Continueや101 Switching Protocolsがありますが、これらは主にブラウザとサーバー間などの低レベルなやり取りで使われることが多く、通常のWebAPIクライアント(Pythonからの利用)ではあまり意識する機会がありません。
2xx成功レスポンスの概要

2xxは「リクエストが正常に処理された」ことを表すステータスコードです。
APIクライアントから見れば「ひとまず成功」と判断できるゾーンですが、必ずしも全て同じ意味ではなく、200と201、204などは用途が違います。
主な2xxは次の通りです。
- 200 OK
- 201 Created
- 202 Accepted
- 204 No Content
それぞれの違いは後述の章で詳しく解説します。
3xxリダイレクトの概要
3xxは「リソースの場所が移動したため、別のURLにアクセスしてほしい」という意味のステータスコードです。
ブラウザは自動的に新しいURLへアクセスし直しますが、APIクライアントでは自動追跡するかどうかを自分で制御することもできます。
代表的な3xxコードとしては301 Moved Permanently、302 Found、304 Not Modifiedなどがあります。
4xxクライアントエラーの概要
4xxは「クライアント側の誤りによるエラー」です。
例えばURLが間違っている、リクエストボディが不正、認証情報が足りないなどが該当します。
WebAPIを利用する上では、特に次のコードを頻繁に目にします。
- 400 Bad Request
- 401 Unauthorized
- 403 Forbidden
- 404 Not Found
- 429 Too Many Requests
4xxエラーは「リクエスト内容を直さない限り、同じ結果が返ってくる」と理解しておくとよいです。
5xxサーバーエラーの概要
5xxは「サーバー側の障害によるエラー」です。
リクエストは正しかったとしても、サーバー内部の例外、バックエンドサービスの障害、過負荷などにより処理できなかった場合に返されます。
主なものとして500 Internal Server Error、502 Bad Gateway、503 Service Unavailable、504 Gateway Timeoutがあります。
5xxはサーバー側の問題なので、「待ってからリトライする」という戦略がよく用いられます。
2xx成功ステータスコードの意味
200 OKの意味と使いどころ

200 OKはもっとも基本的な成功レスポンスで、リクエストが正常に処理され、レスポンスボディに結果データが含まれる場合に使われます。
たとえば次のようなケースで用いられます。
- GET /users/1 でユーザー情報を取得
- GET /articles で記事一覧を取得
- PUT /users/1 で情報更新後、更新結果を返却
API設計では「成功かつボディに何かしらの有用な情報を返すときは200」と覚えておくと分かりやすいです。
201 Createdとリソース作成API

201 Createdは新しいリソースが作成されたことを示す成功コードです。
主にPOSTリクエストに対するレスポンスとして利用されます。
典型的なパターンは次の通りです。
- POST /users に新規ユーザー情報を送信
- サーバー側でID=123のユーザーを作成
- レスポンスとして
- ステータスコード: 201
- Locationヘッダ: /users/123
- ボディ: 作成されたユーザー情報(JSON)
「新規作成が成功した」ことを明確に示したい場合は200ではなく201を返すと、クライアントが挙動を判断しやすくなります。
204 No ContentとDELETEレスポンス

204 No Contentはリクエストが成功したが、返すべきコンテンツが特にない場合に使われます。
典型例がDELETEです。
- DELETE /users/123 に成功した場合
- ステータスコード: 204
- レスポンスボディ: なし
また、PUTやPATCHで更新処理を行った際にも結果の詳細をボディで返さず、「成功した」という事実だけでよい場合に204を使うことがあります。
クライアントは「ボディが無いことを前提に」実装する必要があります。
3xxリダイレクトステータスコードの意味
301 Moved Permanentlyと恒久的リダイレクト

301 Moved Permanentlyはリソースの場所が恒久的に変更されたことを示します。
ブラウザではSEOにも関係するステータスですが、WebAPIにおいてもバージョンアップなどでURLが変わる場合に使われます。
例えば、APIバージョンの切り替えで次のようなケースがあります。
- 旧: GET /api/v1/users
- 新: GET /api/v2/users
- /api/v1/users にアクセスが来た場合、301で/api/v2/users へ誘導
クライアント側は「今後は新しいURLを使うべき」と理解し、可能であれば設定を更新するべきです。
302 Foundと一時的リダイレクト

302 Foundは「一時的に別の場所にリソースがある」ことを示します。
サーバー側の一時的な構成変更や、A/Bテストなどで使われることがあります。
301との違いは「恒久的(301)」か「一時的(302)」かです。
WebAPIにおいては、クライアントがURLを更新すべきかどうかの判断材料となるため、使い分けが重要です。
304 Not Modifiedとキャッシュ制御

304 Not Modifiedはクライアントが持っているキャッシュが最新のものであり、再送する必要がないことを示すコードです。
主にETagやLast-Modifiedヘッダと組み合わせて利用されます。
WebAPIでは、静的な設定情報や大きなリストデータなどをキャッシュするときに役立ちます。
クライアントは次のような動きをします。
- 初回: 通常のGET → 200 OK + 本文 + ETag
- 2回目以降: ETagを
If-None-Matchヘッダで送る - 変更なしならサーバーは304を返し、本文を送らない
これにより帯域の節約やレスポンス高速化が期待できます。
4xxクライアントエラーステータスコードの意味
400 Bad Requestとバリデーションエラー

400 Bad Requestはリクエストの内容が不正で、サーバーが理解・処理できない場合に返されます。
具体的には次のような状況です。
- JSONの形式が壊れている
- 必須パラメータが欠けている
- パラメータの型が違う(文字列なのに数値を想定しているなど)
API設計では、400を返すときにどの項目が、どのように不正だったのかを詳細にボディで返すことが重要です。
これについては後半の「汎用エラーコードと詳細メッセージ」で詳しく触れます。
401 Unauthorizedと認証エラー

401 Unauthorizedは「認証されていない」状態を示すコードです。
トークン未付与、トークン期限切れ、無効な署名などが原因となります。
ポイントは「認証に失敗した」のであって、「権限が足りない」わけではないことです。
後述する403 Forbiddenと使い分けることで、クライアント側の対応が明確になります。
403 Forbiddenと権限エラー

403 Forbiddenは「認証はできているが、その操作を行う権限がない」場合に使われます。
典型的な例としては次のようなものがあります。
- 一般ユーザーが管理者専用APIにアクセス
- 他人のリソースにアクセスしようとした
クライアントにとっては「ログイン情報を変えるだけでは解決しない」ことを示すため、401との区別は非常に重要です。
404 Not FoundとURL間違い

404 Not Foundはリソースが存在しないことを表します。
URLのパスが間違っている、またはID指定されたリソースが削除済みなどのケースで返却されます。
API設計では「存在しないID」も404を返すことが多く、「リソースの有無」を明示する標準的な手段として使われています。
429 Too Many Requestsとレート制限

429 Too Many Requestsはレート制限を超えたことを知らせるステータスコードです。
API提供側が、サーバー資源を守るために「一定時間あたりの最大リクエスト数」を設定している場合によく利用されます。
レスポンスヘッダにRetry-Afterを含めて、何秒後に再試行すべきかを知らせる実装も一般的です。
クライアント側は429を受け取った場合、リトライではなく「時間を置いてから再試行」する戦略を取る必要があります。
5xxサーバーエラーステータスコードの意味
500 Internal Server Errorの原因例

500 Internal Server Errorはサーバー内部で想定外のエラーが発生したことを示します。
原因としては次のようなものが考えられます。
- コード上のバグ(Null参照、ゼロ除算など)
- 依存している外部サービスのエラーを適切にハンドリングしていない
- 予期しない例外がキャッチされずに伝播した
API設計としては、500をクライアントに返す前にサーバー側でログをしっかり出力することが重要です。
クライアントは詳細な原因を把握できないため、最小限の情報のみを返し、内部構造が漏れないようにすることもポイントです。
502 Bad Gatewayとゲートウェイエラー

502 Bad Gatewayはゲートウェイやプロキシが背後の上流サーバーから不正なレスポンスを受け取った場合に返されます。
マイクロサービス構成やAPIゲートウェイを挟んだ構成でよく見られます。
例えば次のようなシナリオです。
- クライアントはAPI Gatewayにリクエスト
- Gatewayは内部のUser Serviceへ転送
- User Serviceがクラッシュ、または異常なレスポンスを返す
- Gatewayはクライアントに502を返却
クライアントから見れば「サーバーの内部構成までは分からないが、とにかく中継先のどこかで問題が起きている」という状態です。
503 Service Unavailableと一時的な停止

503 Service Unavailableはサービスが一時的に利用できないことを表します。
メンテナンス中、過負荷状態、バックエンドの一時障害などが原因です。
しばしばRetry-Afterヘッダと組み合わせて使われ、「何秒後に再試行すべきか」をクライアントに伝えることがあります。
クライアント側では503を受け取った場合、指数バックオフなどを用いたリトライ戦略を組み込むと安定した動作になります。
504 Gateway Timeoutとタイムアウト

504 Gateway Timeoutはゲートウェイやリバースプロキシが、上流サーバーからの応答を待っている間にタイムアウトしたことを示します。
ネットワーク遅延やバックエンドの処理遅延が原因です。
クライアントとしては「リクエストは届いているが、結果が返ってこなかった可能性がある」点に注意が必要です。
特にPOSTやPUTなど副作用を伴う操作では、サーバー側が処理を完了したかどうかを確認するための工夫(冪等な設計など)が不可欠です。
Pythonでステータスコードを扱う基本
requestsライブラリでのステータスコード取得

PythonでHTTPリクエストを扱う場合、最もよく使われるのがrequestsライブラリです。
インストールは次のように行います。
pip install requests
ステータスコードを取得する最小限のサンプルは次のようになります。
import requests
# シンプルなGETリクエストの例
url = "https://httpbin.org/status/200" # 常に指定したステータスコードを返すテスト用サービス
response = requests.get(url)
# レスポンスのステータスコードを表示
print("ステータスコード:", response.status_code)
# ステータスコードのカテゴリ判定(2xx, 4xx, 5xxなど)
if response.ok: # 200~399を「成功」とみなす便利プロパティ
print("OKまたはリダイレクト系のレスポンスです")
else:
print("クライアントエラーまたはサーバーエラーが発生しました")
ステータスコード: 200
OKまたはリダイレクト系のレスポンスです
このように、Pythonではresponse.status_codeを参照するだけで数値としてステータスコードを扱えるため、後述する分岐処理やエラーハンドリングに簡単に組み込むことができます。
レスポンスコードごとの分岐処理

ステータスコードを使った分岐処理の基本形は、次のように書くことができます。
import requests
def fetch_user(user_id: int) -> None:
"""ユーザー情報を取得し、ステータスコードに応じて処理を分ける例"""
url = f"https://httpbin.org/status/200" # 実際はユーザーAPIなどを指定
response = requests.get(url)
status = response.status_code
print("ステータスコード:", status)
# 2xx: 成功
if 200 <= status < 300:
print("成功しました。レスポンスボディを処理します。")
# 実際の処理: JSONパースなど
# data = response.json()
# print(data)
# 3xx: リダイレクト(通常はrequestsが自動追跡)
elif 300 <= status < 400:
print("リダイレクトレスポンスです。必要に応じてLocationヘッダを確認します。")
print("Location:", response.headers.get("Location"))
# 4xx: クライアントエラー(リクエストの見直しが必要)
elif 400 <= status < 500:
print("クライアントエラーが発生しました。リクエスト内容を確認してください。")
print("レスポンス本文:", response.text)
# 5xx: サーバーエラー(リトライなどの対応が必要)
elif 500 <= status < 600:
print("サーバーエラーが発生しました。時間をおいて再試行するなどの対策が必要です。")
print("レスポンス本文:", response.text)
else:
print("想定外のステータスコードです。")
# 関数を呼び出して動作確認
fetch_user(1)
実行環境やURLによって結果は変わりますが、ステータスコードに応じたメッセージが表示されることが確認できます。
ステータスコード: 200
成功しました。レスポンスボディを処理します。
PythonとWebAPIステータスコードの実践例
成功(2xx)時のレスポンス処理例

2xxのときはレスポンスボディに有用な情報が入っている場合が多く、JSONをパースしてドメインオブジェクトに変換するケースが一般的です。
import requests
def get_user_detail(user_id: int) -> dict | None:
"""2xx時のみJSONをパースして返す例"""
url = f"https://jsonplaceholder.typicode.com/users/{user_id}"
response = requests.get(url)
print("ステータスコード:", response.status_code)
if 200 <= response.status_code < 300:
# 成功時のみJSONをパース
user = response.json() # JSON → Pythonのdict/listに変換
print("ユーザー名:", user.get("name"))
return user
else:
# エラー時はログを出すだけにするなど、アプリごとに設計
print("ユーザー取得に失敗しました。")
print("レスポンス本文:", response.text)
return None
# 動作例
user_data = get_user_detail(1)
ステータスコード: 200
ユーザー名: Leanne Graham
このように成功時とそれ以外で処理をはっきり分けておくことで、コードの見通しが良くなります。
リダイレクト(3xx)の自動追跡と制御

requestsはデフォルトでリダイレクトを自動追跡しますが、制御したい場合はallow_redirects引数を利用します。
import requests
def check_redirect(url: str) -> None:
"""リダイレクトの挙動を確認する例"""
# 1. デフォルト(自動でリダイレクトを追跡)
response_auto = requests.get(url)
print("自動追跡有り:")
print("最終URL:", response_auto.url)
print("最終ステータスコード:", response_auto.status_code)
# 2. リダイレクトを追跡しない
response_no_redirect = requests.get(url, allow_redirects=False)
print("\n自動追跡なし:")
print("最初のURL:", response_no_redirect.url)
print("最初のステータスコード:", response_no_redirect.status_code)
print("Locationヘッダ:", response_no_redirect.headers.get("Location"))
# テスト用のURL(リダイレクトを返すエンドポイントの例)
test_url = "http://httpbin.org/redirect-to?url=https://httpbin.org/get"
check_redirect(test_url)
自動追跡有り:
最終URL: https://httpbin.org/get
最終ステータスコード: 200
自動追跡なし:
最初のURL: http://httpbin.org/redirect-to?url=https://httpbin.org/get
最初のステータスコード: 302
Locationヘッダ: https://httpbin.org/get
APIクライアントを実装する場合、重要な更新系リクエストで意図しないリダイレクトが起きていないかを確認するために、あえてallow_redirects=Falseを使う設計が有効な場合もあります。
クライアントエラー(4xx)のエラーハンドリング

4xxエラーはクライアント側の修正が必要なため、エラー内容を分かりやすく呼び出し元へ伝えることが重要です。
ここでは簡単な例として、400系を検出したら例外を投げる実装を示します。
import requests
class ClientError(Exception):
"""4xxエラー用のカスタム例外"""
pass
def call_api_with_error_handling(url: str) -> dict:
"""4xxエラーを例外として扱うサンプル"""
response = requests.get(url)
status = response.status_code
if 200 <= status < 300:
return response.json()
elif 400 <= status < 500:
# 詳細なメッセージを組み立てて例外として投げる
message = f"クライアントエラー({status})が発生しました: {response.text}"
raise ClientError(message)
else:
# 5xxやその他は別の扱いにしてもよい
message = f"予期しないエラー({status})が発生しました: {response.text}"
raise RuntimeError(message)
# 呼び出し側の例
try:
data = call_api_with_error_handling("https://httpbin.org/status/400")
print("成功:", data)
except ClientError as e:
print("入力内容を見直してください。詳細:", e)
except Exception as e:
print("その他のエラー:", e)
入力内容を見直してください。詳細: クライアントエラー(400)が発生しました:
このように4xxを「入力の見直しが必要なエラー」として明確に扱うことで、ユーザー向けのUIやログ出力を改善できます。
サーバーエラー(5xx)のリトライ実装例

5xxエラーはサーバー側の問題であることが多いため、一定回数まで自動リトライする設計がよく用いられます。
簡易的なリトライ実装の例を示します。
import time
import requests
def get_with_retry(url: str, max_retries: int = 3, base_delay: float = 1.0) -> requests.Response:
"""5xxエラー時にリトライする簡易実装(指数バックオフ付き)"""
attempt = 0
while True:
attempt += 1
response = requests.get(url)
status = response.status_code
print(f"{attempt}回目の試行: ステータスコード={status}")
# 成功または4xx(クライアントエラー)なら、そのまま返す
if status < 500:
return response
# 5xxかつ最大リトライ回数に達したらあきらめる
if attempt >= max_retries:
print("最大リトライ回数に達しました。処理を中断します。")
return response
# リトライ待機(指数バックオフ)
delay = base_delay * (2 ** (attempt - 1))
print(f"{delay}秒待機してから再試行します...")
time.sleep(delay)
# 例: 常に500を返すテストエンドポイント
test_url = "https://httpbin.org/status/500"
final_response = get_with_retry(test_url, max_retries=3)
1回目の試行: ステータスコード=500
1.0秒待機してから再試行します...
2回目の試行: ステータスコード=500
2.0秒待機してから再試行します...
3回目の試行: ステータスコード=500
最大リトライ回数に達しました。処理を中断します。
5xxだからといって無限にリトライするのではなく、回数制限と待機時間を設けることが、安定したAPIクライアントの実装には欠かせません。
ステータスコードとAPI設計のポイント
適切なステータスコード設計の考え方

API設計においてステータスコードは、クライアントとサーバーの「約束事(契約)」の一部です。
適切な設計の考え方として、次のようなポイントがあります。
- 成功時のコードを明確にする
- 読み取り専用は原則200
- 作成成功は201、削除成功は204など、意図を表現する
- 4xxと5xxを厳密に分ける
- クライアントの修正で直るものは4xx
- サーバー側の障害は5xx
- 認証エラーは401、権限エラーは403
- ログインしていない、トークン無効 → 401
- ログイン済みだがアクセス禁止 → 403
- 404と403の使い分けに注意
- あえて「存在するかどうかを隠す」ために403ではなく404を返す設計もある
「とりあえず全部200と500にしておく」と、クライアント側が状況を判別できなくなり、コードや運用が急激に複雑になります。
標準的な意味に沿ってステータスコードを設計することが、APIの可読性と保守性を高める近道です。
汎用エラーコードと詳細メッセージの返し方

HTTPステータスコードだけでは、アプリケーション固有のエラーを細かく表現しきれません。
そこでステータスコードとは別に「アプリケーション内エラーコード」を定義し、JSONボディに含めるパターンがよく使われます。
例えば400エラー時のレスポンスを次のように設計します。
{
"error_code": "VALIDATION_ERROR",
"message": "入力値が不正です。",
"details": [
{"field": "email", "message": "メールアドレスの形式が不正です。"},
{"field": "age", "message": "年齢は0以上で指定してください。"}
]
}
このようにすると、クライアントは次の2段階でエラーを判断できます。
- HTTPステータスコード
- 400なのか401なのかなど、大まかな分類を把握
- アプリケーション内エラーコード(error_code)
- VALIDATION_ERROR, DUPLICATE_EMAIL, QUOTA_EXCEEDED など、ビジネスロジックに即した区別
加えてdetailsのような配列でフィールド別メッセージを返すと、フォーム入力画面などでどの項目にどんなエラーが起きているかをユーザーに正確に伝えられるため、UXの向上につながります。
Pythonクライアント側では、こうしたJSONをパースしてerror_codeごとに処理を分ける実装が可能です。
まとめ
WebAPIのステータスコードは、単なる数字ではなくクライアントとサーバーの間で「何が起きたか」を瞬時に共有するための重要な言語です。
200・201・204といった成功コードの違い、301~304のリダイレクト、400・401・403・404・429などのクライアントエラー、500~504のサーバーエラーを正しく理解しておくことで、問題の切り分けやエラーハンドリングが格段にやりやすくなります。
Pythonのrequestsを用いれば、ステータスコードの取得と分岐処理は容易ですので、本記事のサンプルを土台に、わかりやすく堅牢なAPIクライアントとAPI設計へ発展させていってください。
