# PythonでPDFを要約する — PDF要約APIチュートリアル

数行のコードで、Pythonスクリプト、ノートブック、またはバックエンドにPDF要約機能を追加します。このチュートリアルでは、認証、最初の1件のリクエスト、レスポンス形式、ストリーミング、フォルダ一括処理、適切なエラー処理について解説します。

## Key facts

- エンドポイント: POST https://pdf-summarize.com/api/summarize.php（multipart/form-data、フィールド名 file）。
- 認証: ヘッダー Authorization: Bearer sk_... — サインアップ時に無料キーを取得可能、クレジットカード不要。
- オプション: length（short/medium/long/extensive）、format（paragraph/bullets/mixed）、outputLang（autoまたは言語コード）、includeKeywords。
- レスポンス: summary、keywords、page_count、processing_time、requests_remaining を含むJSON。
- ?stream=1 を追加すると、Server-Sent Eventsとして要約を受信可能。

## 前提条件

- Python 3.8以上と `requests` ライブラリ（`pip install requests`）。
- APIキー: [無料アカウントを作成](/signup)し、ダッシュボードからキーをコピーします。コードに直接記述せず、環境変数に保存してください：

```bash
export PDF_SUMMARIZE_KEY="sk_live_..."
```

## Pythonでの初めてのPDF要約

1. **requestsのインストール** — pip install requests を実行します。
2. **APIキーの設定** — 環境変数 PDF_SUMMARIZE_KEY に設定します。
3. **PDFの送信** — フィールド名を file とした multipart/form-data として送信します。
4. **JSONの読み込み** — 要約は summary に格納されます。success とHTTPステータスを確認してください。

```python
import os
import requests

API_URL = "https://pdf-summarize.com/api/summarize.php"
API_KEY = os.environ["PDF_SUMMARIZE_KEY"]

def summarize_pdf(path, length="medium", fmt="bullets", lang="auto"):
    with open(path, "rb") as f:
        resp = requests.post(
            API_URL,
            headers={"Authorization": f"Bearer {API_KEY}"},
            files={"file": (os.path.basename(path), f, "application/pdf")},
            data={"length": length, "format": fmt, "outputLang": lang, "includeKeywords": "1"},
            timeout=180,
        )
    data = resp.json()
    if resp.status_code != 200 or not data.get("success"):
        raise RuntimeError(f"{resp.status_code}: {data.get('error', data)}")
    return data

result = summarize_pdf("annual-report.pdf")
print(result["summary"])
print("ページ数:", result["page_count"], "| キーワード:", result.get("keywords"))
```

## レスポンス

```json
{
  "success": true,
  "summary": "- 売上高は6%増の...",
  "keywords": "revenue, margin, guidance, ...",
  "page_count": 48,
  "original_length": 182340,
  "summary_length": 1210,
  "processing_time": 6.214,
  "requests_remaining": 4987
}
```

`requests_remaining` は、その月の残りの利用枠です（`-1` は無制限を意味します）。

## 要約のストリーミング

長文の文書やインタラクティブなアプリでは、要約が生成されるにつれてストリーミング形式で受信します：

```python
import json

def stream_summary(path):
    with open(path, "rb") as f:
        with requests.post(
            API_URL + "?stream=1",
            headers={"Authorization": f"Bearer {API_KEY}"},
            files={"file": (os.path.basename(path), f, "application/pdf")},
            data={"length": "long", "format": "mixed"},
            stream=True, timeout=300,
        ) as resp:
            resp.raise_for_status()
            for line in resp.iter_lines(decode_unicode=True):
                if not line or not line.startswith("data: "):
                    continue
                payload = line[6:]
                if payload == "[DONE]":
                    break
                event = json.loads(payload)
                if "error" in event:
                    raise RuntimeError(event["error"])
                if event.get("type") == "summary":
                    print(event["content"], end="", flush=True)

stream_summary("research-paper.pdf")
```

## PDFフォルダの一括処理

```python
import pathlib, time

out_dir = pathlib.Path("summaries")
out_dir.mkdir(exist_ok=True)

for pdf in sorted(pathlib.Path("pdfs").glob("*.pdf")):
    target = out_dir / (pdf.stem + ".md")
    if target.exists():
        continue                      # 中断からの再開に対応
    for attempt in range(3):
        try:
            data = summarize_pdf(str(pdf), length="long", fmt="mixed")
            target.write_text(data["summary"], encoding="utf-8")
            print("成功", pdf.name, data["page_count"], "ページ")
            break
        except RuntimeError as e:
            if str(e).startswith("429"):
                time.sleep(30 * (attempt + 1))   # レート制限: バックオフ（待機）
            else:
                print("失敗", pdf.name, e)
                break
```

## エラーハンドリング

| ステータス | 意味 | 対処法 |
|---|---|---|
| 400 | ファイルの欠落、不正な形式、ページ数制限オーバー | ファイルとプランのページ制限を確認する |
| 401 | APIキーの欠落または無効 | `Authorization` ヘッダーを確認する |
| 403 | アカウントが停止されている | サポートに問い合わせる |
| 413 | ファイルサイズが大きすぎる | PDFを圧縮するか分割する |
| 422 | 読み取り可能なテキストがない（空、保護されている、またはスキャンが不鮮明） | パスワードを解除するか、スキャン品質を改善する |
| 429 | レート制限または月間クォータに到達 | バックオフして再試行するか、プランをアップグレードする |
| 500 | 要約サービスが一時的に利用不可 | バックオフして再試行する |

> **コードを書かずにまずは試してみたいですか？** — ブラウザで同じエンジンを無料でお試しください。

## ヒント

- 長いPDFの場合は、タイムアウト時間を余裕を持たせて（2〜3分に）設定してください。
- 同じ文書に対して二重に料金が発生しないよう、ファイルハッシュによる**結果のキャッシュ**を実装してください。
- **キーはサーバー側で厳重に管理**し、フロントエンドやモバイルアプリに同梱しないでください。
- パラメータの完全なリファレンスについては、[APIドキュメント](/docs)を参照してください。

## FAQ

### 無料のAPIプランはありますか？

はい。無料プランには、クレジットカード不要で使える毎月のリクエスト枠が含まれています。料金プランをご覧ください。

### ファイルの代わりにPDFのURLを送信できますか？

APIはファイルそのものを受け付けます。コード内でPDFをダウンロードし（requestsなどを使用）、そのバイトデータを送信してください。

### 要約の言語は何に対応していますか？

文書の言語をそのまま維持するには outputLang=auto を使用します。また、ja、en、fr、es、de などの言語コードを指定することも可能です。

### スキャンされたPDFはどのようにAPIで要約しますか？

他のPDFと同様に送信してください。利用可能なテキストレイヤーがない場合、抽出パイプラインが自動的にOCRにフォールバックします。

---
Source: https://pdf-summarize.com/ja/guides/pdf-summary-api-python — PDF Summarizer (free PDF summarizer, Q&A with page citations, OCR, REST API). Site overview: https://pdf-summarize.com/llms.txt
