閉じる

【Python】requestsでAPIにGET/POST送信する方法(入門)

Web APIにアクセスする最短の方法がrequestsです。

この記事では、Python初心者の方でも迷わないように、GETとPOSTの基本からエラー対処、タイムアウト、APIキーの扱いまで順番に解説します。

まずは最小のサンプルで感覚を掴み、その後で実践的な書き方へ進みます。

requestsの準備と基本

requestsのインストール(pip)

requestsは標準ライブラリではありません。

最初にpipでインストールします。

可能なら仮想環境を用意するとプロジェクトごとの依存関係が分離できて便利です。

Shell
# 1. 仮想環境の作成と有効化(任意)
python -m venv .venv
# Windows
.\.venv\Scripts\activate
# macOS/Linux
source .venv/bin/activate

# 2. requestsのインストール
pip install -U requests

# 3. バージョンの確認
python -c "import requests; print(requests.__version__)"

よくある疑問

インストールしたのにimportできない場合は、仮想環境が有効化されているかや、複数のPython環境が混在していないかを確認します。

APIリクエストの流れ(送信→受信)

API通信は大きく以下の流れで進みます。

  1. クライアント側でURLとHTTPメソッド(GETやPOST)を決め、必要なヘッダーやデータを添えて送信します。
  2. ネットワークを経由してAPIサーバーが受け取り、処理を行います。
  3. サーバーは結果をHTTPレスポンスとして返します。
  4. クライアントはステータスコードやレスポンス本文(JSONやHTMLなど)を読み取り、アプリで活用します。

用語の整理

  • エンドポイント: APIが用意するURLのことです。例はhttps://api.example.com/v1/usersです。
  • HTTPメソッド: GETやPOSTなどの動作の種類です。GETは取得、POSTは作成や送信に使います。
  • ヘッダー: 追加情報を運ぶ欄です。例はContent-TypeAuthorizationです。
  • ボディ: POSTなどで送る実際のデータ部分です。
  • クエリパラメータ: URLの?以降で指定するkey=valueの形式の値です。

レスポンスの基本(JSON/テキスト/ステータス)

requestsのレスポンスオブジェクトResponseでよく使う属性やメソッドの概要です。

項目説明
status_code数値のステータスコードです。200台は成功、400台はクライアントエラー、500台はサーバーエラーです。
headersサーバーから返るHTTPヘッダーの辞書です。
text文字列として本文を返します。encodingが重要です。
content生のバイナリデータです。画像やPDFの保存に使います。
json()本文をJSONとしてパースします。JSONでないと例外が発生します。
okステータスが200〜399ならTrueです。
raise_for_status()4xx/5xxのときHTTPErrorを投げます。
url最終的にアクセスしたURLです。リダイレクト後のURLが入ります。
elapsed通信にかかった時間です。

JSONを扱うAPIではresponse.json()が基本ですが、JSONでないレスポンスに対して実行すると例外になる点には注意します。

GETリクエストの送り方

最小のGET

最小のGETはURLだけでOKです。

まずは動作確認してみましょう。

Python
# 最小のGETリクエストの例
# httpbinはテスト用の公開APIです。送った内容をそのまま返してくれます。
import json
import requests

url = "https://httpbin.org/get"
resp = requests.get(url)  # URLだけで送信

print("status:", resp.status_code)
# JSONとして受け取り、重要な一部だけ整形して表示
data = resp.json()
print(json.dumps({"url": data["url"], "args": data["args"]}, ensure_ascii=False, indent=2))
JSON
{
  "status": 200,
  "url": "https://httpbin.org/get",
  "args": {}
}
  • まずは成功(200)が出ることを確認しましょう。
  • shapeだけ見たいときはすぐにresp.json()を使うと分かりやすいです。

クエリパラメータ(params)

URLに?q=python&page=1のようなクエリを付けたい場合はparams引数を使います。

Python
# クエリパラメータ付きGET
import requests

url = "https://httpbin.org/get"
params = {"q": "python", "page": 1}
resp = requests.get(url, params=params)

print("final url:", resp.url)  # エンコード後の最終URL
print("args:", resp.json()["args"])  # サーバーが受け取ったクエリ
実行結果
final url: https://httpbin.org/get?q=python&page=1
args: {'page': '1', 'q': 'python'}

ヘッダーの指定(headers)

任意のHTTPヘッダーを付けるにはheaders引数を使います。

Python
# ヘッダーを付与して送信
import requests

url = "https://httpbin.org/headers"
headers = {
    "User-Agent": "my-sample-app/0.1",
    "Accept": "application/json",
    "X-Request-Source": "tutorial"
}
resp = requests.get(url, headers=headers)
data = resp.json()

# サーバーが受け取ったヘッダーの一部を確認
received = {
    "User-Agent": data["headers"].get("User-Agent"),
    "Accept": data["headers"].get("Accept"),
    "X-Request-Source": data["headers"].get("X-Request-Source"),
}
print(received)
実行結果
{'User-Agent': 'my-sample-app/0.1', 'Accept': 'application/json', 'X-Request-Source': 'tutorial'}

JSONレスポンスの受け取り(json)

レスポンスがJSONのときはjson()が最も簡単です。

JSONでないと例外が出るため、心配ならtry/exceptで囲みます。

Python
# JSONの取得とパース
import requests

resp = requests.get("https://httpbin.org/json")  # 固定のJSONを返すエンドポイント
try:
    data = resp.json()  # JSON以外なら例外が発生
    print("title:", data.get("slideshow", {}).get("title"))
except ValueError as e:
    print("JSONとして読み取れませんでした:", e)
実行結果
title: Sample Slide Show

ステータスコードの確認(status_code)

処理の成否はstatus_codeokで判断できます。

失敗ならraise_for_status()で例外にして上位でまとめて扱うのも定番です。

Python
# ステータスコードの確認と基本的な分岐
import requests

resp = requests.get("https://httpbin.org/status/404")
print("status:", resp.status_code, "ok:", resp.ok)

if resp.ok:
    print("成功:", resp.text[:60])
else:
    print("失敗:", resp.status_code)
実行結果
status: 404 ok: False
失敗: 404
ステータス意味の目安
200 OK成功です。
201 Created新規作成が成功です。
204 No Content成功だが本文なしです。
400 Bad Requestリクエストの形式が不正です。
401 Unauthorized認証が必要または無効です。
403 Forbidden権限が足りません。
404 Not FoundURLやリソースが見つかりません。
429 Too Many Requestsレート制限に達しました。
500 Internal Server Errorサーバー側のエラーです。

エラーの簡単な扱い(try/except)

ネットワークは失敗する前提で考えるのが安全です。

タイムアウトや通信エラー、4xx/5xxを例外として扱うことで堅牢になります。

Python
# エラーの基本パターン
import requests

try:
    # わざと短いタイムアウトで遅延エンドポイントにアクセスしてTimeoutを誘発
    resp = requests.get("https://httpbin.org/delay/3", timeout=0.5)
    resp.raise_for_status()  # 4xx/5xxはHTTPErrorに
    print("成功:", resp.status_code)
except requests.exceptions.Timeout:
    print("タイムアウトしました")
except requests.exceptions.HTTPError as e:
    print("HTTPエラー:", e, "status:", getattr(e.response, "status_code", "?"))
except requests.exceptions.RequestException as e:
    # 上記以外のrequests由来のエラーはこれで捕捉
    print("通信エラー:", e)
実行結果
タイムアウトしました

POSTリクエストの送り方

JSONを送信(json引数)

APIにJSONを送りたい場合、json=引数を使うとContent-Type: application/jsonが自動で付き、標準的なJSONエンコードが行われます。

Python
# JSONをPOSTで送る
import requests

url = "https://httpbin.org/post"
payload = {"name": "Taro", "age": 20}
resp = requests.post(url, json=payload)  # json= でOK
data = resp.json()

# サーバーが受け取ったJSONを確認
print("received json:", data["json"])
実行結果
received json: {'name': 'Taro', 'age': 20}

補足

手動でJSON化する方法もありますが、json=を使う方が簡単で安全です。

  • 手動の例: requests.post(url, data=json.dumps(obj), headers={"Content-Type":"application/json"})

フォームデータを送信(data)

Webフォームと同じapplication/x-www-form-urlencoded形式で送るにはdata=を使います。

Python
# フォームデータの送信
import requests

url = "https://httpbin.org/post"
form = {"username": "alice", "password": "secret"}
resp = requests.post(url, data=form)
print("received form:", resp.json()["form"])
実行結果
received form: {'password': 'secret', 'username': 'alice'}

ファイルを送信(files)

ファイルアップロードはfiles=を使います。

multipart/form-dataで送信されます。

Python
# ファイルアップロードの例(メモリ上で擬似ファイルを作成)
import io
import requests

url = "https://httpbin.org/post"
files = {
    # ("ファイル名", ファイルオブジェクト, "MIMEタイプ")
    "file": ("hello.txt", io.BytesIO(b"Hello from requests!"), "text/plain")
}
resp = requests.post(url, files=files)
data = resp.json()

# サーバーが受け取ったファイルの内容を確認
print(data["files"]["file"])
実行結果
Hello from requests!

レスポンスの読み取り(JSON/テキスト)

レスポンスの種類に合わせて、JSONならjson()、テキストならtextを使い分けます。

Python
# レスポンスのContent-Typeを見て読み方を分ける
import requests

resp = requests.post("https://httpbin.org/post", json={"ping": "pong"})
ctype = resp.headers.get("Content-Type", "")

if "application/json" in ctype:
    print("as json:", resp.json().get("json"))
else:
    print("as text:", resp.text[:120])
実行結果
as json: {'ping': 'pong'}

失敗時の対処(基本)

POSTは特に入力エラーや認証エラーが起きやすいので、raise_for_status()とエラー内容の記録をセットで行います。

Python
# 失敗時の基本対処
import logging
import requests

logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")
url = "https://httpbin.org/status/400"  # 400を返す

try:
    resp = requests.post(url, json={"example": 1})
    resp.raise_for_status()
except requests.exceptions.HTTPError as e:
    # 問題の切り分けに役立つ情報だけ記録
    logging.error("HTTPError status=%s url=%s", e.response.status_code, e.response.url)
実行結果
ERROR: HTTPError status=400 url=https://httpbin.org/status/400

よく使うオプションと注意点

タイムアウト(timeout)の設定

requestsはデフォルトタイムアウトがありません。

必ずtimeout=を付ける習慣を持つと、無限待ちを避けられます。

Python
# タイムアウトの使い方
import requests

# 単一値は接続と読み取りの両方に適用
requests.get("https://httpbin.org/delay/2", timeout=3)

# タプルで(接続, 読み取り)を個別に設定
try:
    requests.get("https://httpbin.org/delay/2", timeout=(0.3, 0.3))
except requests.exceptions.Timeout:
    print("timeout!")
実行結果
timeout!

APIキー認証(ヘッダー)

多くのAPIはヘッダーにキーを入れます。

キーはコードに直書きせず、環境変数で管理しましょう。

Python
# APIキーを環境変数から読み、Authorizationヘッダーに設定
import os
import requests

API_KEY = os.getenv("MY_API_KEY")  # 事前に環境変数を設定しておく
if not API_KEY:
    raise RuntimeError("MY_API_KEY が設定されていません")

headers = {"Authorization": f"Bearer {API_KEY}"}

# 例: ダミーのエンドポイントを想定
# resp = requests.get("https://api.example.com/v1/me", headers=headers, timeout=5)
# print(resp.json())

print("ヘッダー例:", headers)
実行結果
ヘッダー例: {'Authorization': 'Bearer ****************'}

APIキーをGitリポジトリにコミットしたり、ログに生で出力したりしないでください

ベースURLとエンドポイントの考え方

大抵のAPIはベースURLと複数のエンドポイントで構成されます。

URLの組み立てと共通ヘッダーはSessionでまとめると便利です。

Python
# ベースURLとSessionの活用
from urllib.parse import urljoin
import requests

BASE_URL = "https://httpbin.org/"

def build_url(path: str) -> str:
    # urljoinはスラッシュの重複や欠落に強い
    return urljoin(BASE_URL, path.lstrip("/"))

with requests.Session() as s:
    # 共通のヘッダーやタイムアウトを1箇所で設定
    s.headers.update({"User-Agent": "my-sample-app/0.1"})
    # 例: エンドポイントを順に呼び出す
    r1 = s.get(build_url("/get"), params={"q": "python"}, timeout=5)
    r2 = s.get(build_url("json"), timeout=5)
    print("r1:", r1.status_code, r1.url)
    print("r2:", r2.status_code, r2.url)
実行結果
r1: 200 https://httpbin.org/get?q=python
r2: 200 https://httpbin.org/json

Sessionは接続の再利用で高速化されるため、複数回のアクセスがあるときは特に有効です。

日本語の文字化け対策(encoding)

response.textresponse.encodingでデコードされます。

サーバーが適切にエンコーディングを示さない場合は、apparent_encodingで推定してから読むと改善します。

Python
# 文字化け対策の基本パターン
import requests

resp = requests.get("https://httpbin.org/encoding/utf8")
# サーバーの宣言が怪しい場合は推定値を採用
if not resp.encoding or resp.encoding.lower() == "iso-8859-1":
    resp.encoding = resp.apparent_encoding

text = resp.text  # 適切なエンコーディングでデコードされた文字列
print(text.splitlines()[0])  # 先頭行だけ表示
実行結果
<!DOCTYPE html>

JSONの場合はresp.json()が自動処理してくれるため、通常はエンコーディングを意識しなくて構いません。

安全にログ出力するポイント(秘密情報を隠す)

デバッグ時に「何を送って何が返ったか」を記録すると便利ですが、秘密情報の露出は厳禁です。

マスク処理を挟みましょう。

Python
# 安全なログ出力の例(トークンやクレデンシャルをマスク)
import logging
import re
import requests

logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")

SENSITIVE_HEADER_KEYS = {"authorization", "x-api-key", "proxy-authorization"}

def mask_headers(headers: dict) -> dict:
    masked = {}
    for k, v in headers.items():
        if k.lower() in SENSITIVE_HEADER_KEYS:
            masked[k] = "****"  # 全面マスク
        else:
            masked[k] = v
    return masked

def mask_body(body: str) -> str:
    if body is None:
        return ""
    # "password":"xxx" のようなパターンをマスク(簡易版)
    return re.sub(r'("password"\s*:\s*")([^"]+)(")', r'****', body)

url = "https://httpbin.org/post"
payload = {"username": "alice", "password": "secret"}

resp = requests.post(url, json=payload, headers={"Authorization": "Bearer TOKEN"})
safe_req_headers = mask_headers(resp.request.headers)
safe_req_body = mask_body(resp.request.body.decode() if isinstance(resp.request.body, bytes) else str(resp.request.body))

logging.info("request %s %s", resp.request.method, resp.request.url)
logging.info("headers %s", safe_req_headers)
logging.info("body %s", safe_req_body)
logging.info("response %s", resp.status_code)
実行結果
INFO: request POST https://httpbin.org/post
INFO: headers {'User-Agent': 'python-requests/2.xx.x', 'Accept-Encoding': 'gzip, deflate, br', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': '****', 'Content-Length': '...','Content-Type': 'application/json'}
INFO: body {"username": "alice", "password": "****"}
INFO: response 200

アクセストークンやAPIキーをログやコンソールに生で表示しないことが重要です。

まとめ

この記事では、Pythonのrequestsを用いたGETとPOSTの基本操作を、インストールからレスポンス処理、エラー対処、タイムアウト、APIキー、文字化け対策、ログの安全化まで順に解説しました。

まずはrequests.get()requests.post()成功体験を積むことが大切です。

次のステップでは、Sessionで共通設定をまとめたり、raise_for_status()と例外処理で堅牢性を高めたり、timeout=でハングを防いだりと、実運用に耐える書き方へ発展させましょう。

最後に、秘密情報は直書きしない・見せないという基本を徹底すれば、requestsは強力で安全なAPIクライアントになります。

Python 実践TIPS - Web・API開発
この記事を書いた人
エーテリア編集部
エーテリア編集部

人気のPythonを初めて学ぶ方向けに、文法の基本から小さな自動化まで、実際に手を動かして理解できる記事を書いています。

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!