[Step 1-A-1/4] Build an AI PDF Summarizer — Environment Setup & Your First API Call (Developer Edition)

📌Expert Workflows · Step 1: AI PDF Summarizer · Edition A (Developer) · Part 1 of 4

Review the Expert Workflows roadmap for the currently published prerequisites and project path.

🛠️ Gather These First

  • Python 3.9+ andpip
  • An API key from a provider that currently documents a free or trial allowance and supports an OpenAI-compatible endpoint. Free access, rate limits, supported models and billing requirements can change, so check the provider’s official documentation before choosing one.
  • A terminal and a code editor
  • One PDF to test with — start with a text-based one, not a scan

How to Use This Troubleshooting Tutorial

  • 🔴 BEFORE shows an intentionally incomplete or flawed example so readers can reproduce and understand a representative problem.

  • 🧠 CAUSE explains why the problem occurs.

  • 🟢 AFTER presents the corrected implementation for this lesson.

  • ✅ VERIFICATION explains how to confirm that the correction works.

📝 Step 1: Project Structure and a Virtual Environment

Start with a structure you won’t have to undo later:

mkdir pdf-summarizer && cd pdf-summarizer
python -m venv .venv

# macOS/Linux
source .venv/bin/activate
# Windows (PowerShell)
.venv\Scripts\Activate.ps1

pip install openai pypdf python-dotenv

Three dependencies, each with one job:openaiis the client SDK (it speaks theOpenAI-compatible chat-completions protocolthat some providers implement),pypdfreads PDFs, andpython-dotenvloads your secrets from a file instead of hard-coding them. The reason this matters for portability: because so many providers implement the same protocol, some compatible providers may require only configuration changes, while authentication, endpoints, request fields, and supported features can still differ in.env— you are not locking yourself into a single vendor, which is exactly the kind of decision that saves a rewrite six months later.

Now create the files:

pdf-summarizer/
├── .env          # secrets — never committed
├── .gitignore
├── summarize.py
└── sample.pdf

.env:

AI_API_KEY=your_key_here
AI_BASE_URL=https://your-provider.example.com/api/v1
AI_MODEL=your-model-name

.gitignore— write thisbeforeyour first commit, not after:

.venv/
.env
__pycache__/
*.pdf

That.envline is the single most important line in this file. Committing an API key to a public repository can expose it to automated scanning and misuse quickly. Treat any committed key as compromised, revoke or rotate it immediately, and remove it from the repository history where appropriate.

📝 Step 2: The Connection Test

Before touching PDFs, prove you can reach the model. Createsummarize.py:

import os
from dotenv import load_dotenv
from openai import OpenAI

load_dotenv() API_KEY = os.getenv("AI_API_KEY") BASE_URL = os.getenv("AI_BASE_URL") MODEL = os.getenv("AI_MODEL") missing = [name for name, value in { "AI_API_KEY": API_KEY, "AI_BASE_URL": BASE_URL, "AI_MODEL": MODEL, }.items() if not value] if missing: raise SystemExit("Missing required setting(s): " + ", ".join(missing))
client = OpenAI(api_key=API_KEY, base_url=BASE_URL, timeout=30.0)
response = client.chat.completions.create( model=MODEL, messages=[{"role": "user", "content": "Reply with exactly: connection ok"}], temperature=0, ) print(response.choices[0].message.content)

Run it:

python summarize.py

If the environment variables load correctly, the expected output is:

connection ok

If you do not see this, stop here. The error scenario below is intentional: it shows how the script responds when the `.env` file is unavailable to the running program. The failure is part of the lesson, not a sign that the walkthrough has gone wrong.

That explicit if not API_KEY guard is not defensive padding—it converts a confusing downstream failure into a clear message. The controlled example below shows the kind of configuration error that can otherwise waste an hour.


📋 Provider Access and Compatibility Checklist

Before you commit to a provider, sanity-check it against the four things that actually determine whether a provider plan is usable for this build. Keep this table — it’s the checklist that saves you from picking a provider you’ll abandon in part 3:

What to check Why it matters here Where to find it
Current quota and billing requirements A 40-page PDF becomes several chunked requests (part 3)       Provider pricing page
Context window (tokens) Determines how big a chunk you can send Model card / docs
OpenAI-compatible endpoint      Lets you reuse this exact code Provider API docs
Base URL path The #1 cause of the 404 above (/v1vs/api/v1) Provider quickstart

The one that catches people later is the daily request limit: a single summary of a long document is not one API call, it’s one callper chunk, so a provider that looks generous for chat can run out mid-summary. Checking this now, before you build, is a five-minute decision that prevents a part-3 surprise.

⚠️ Troubleshooting — The API key that “was definitely there”

Understand the symptom

The following failure is intentional. It is included so you can observe what happens when the program cannot load the `.env` file.

1. Keep the original code exactly as shown in Step 2.
2. Temporarily rename `.env` to `.env.bak`.
3. Run the script again:

python summarize.py

4. Read the error message.
5. Rename `.env.bak` back to `.env`.
6. Apply the corrected code in the AFTER section.
7. Run the script again and confirm:

connection ok

This is not a failure of the tutorial. The failure is the lesson.

🔴 BEFORE


AI_API_KEY is empty. Check that .env sits in the project root
and that load_dotenv() runs before this line.

🤔 Cause

In this controlled test, the failure is simple: `.env` has been renamed to `.env.bak`, so the program cannot find a file with the expected name. The key may still exist inside `.env.bak`, but that does not help because the program is looking for `.env`.

In a real project, the same symptom can come from a misplaced or misnamed file, a typo in the variable name, or loading the file after `os.getenv()` runs. The important rule is that the environment file must be loaded before `API_KEY`, `BASE_URL`, and `MODEL` are read. The corrected code below makes the intended `.env` location explicit by anchoring it to the folder containing `summarize.py`.

A second version of this same trap: puttingload_dotenv()aftertheos.getenv()calls. Python executes top to bottom; the variables are read before the file is loaded, so you getNonewith a perfectly correct.env.

🟢 AFTER

First restore `.env` by renaming `.env.bak` back to `.env`.

Now update the original `summarize.py` file. Do not replace the entire file. Make only these two changes:

1. Add `from pathlib import Path` to the import section.
2. Replace the original `load_dotenv()` line with the path-anchored version below.

The new loading code must appear before the `os.getenv()` lines.

import os
from pathlib import Path
from dotenv import load_dotenv
from openai import OpenAI

ENV_PATH = Path(__file__).resolve().parent / ".env"
load_dotenv(dotenv_path=ENV_PATH)

Path(__file__).resolve().parentis the folder containing the script, regardless of your terminal’s location. Add a one-line diagnostic while you’re debugging:

print("env loaded from:", ENV_PATH, "| exists:", ENV_PATH.exists())

💡 Teaching note: This is the same failure pattern represented by the example user’s composite scenario. It is included to make the debugging lesson concrete; it is not presented as a personal incident by the author. The path-anchored loader makes the intended `.env` location explicit and easier to verify.


📝 Step 3: Reading the Two Failures That Look Identical

Once the key loads, the next wall is authentication and routing errors — and beginners routinely misdiagnose them because both arrive as ugly tracebacks. Learn the difference now and you’ll save hours across every remaining project in this course:

Error What it usually means First thing to check
401 Unauthorized The key is wrong, expired, or not yet activated Regenerate the key; check for a copied space
404 Not Found Themodel nameorbase URL pathis wrong Compare both against the provider’s docs, exactly
429 Too Many Requests Rate or quota limit hit Check current quota and billing status; wait or adjust the plan if appropriate
Connection error Network, proxy, or SSL problem Try a plaincurlto the same URL

The 404 case is the sneaky one. Every provider publishes a slightly different base path (/v1,/api/v1,/api/paas/v4), and the SDK appends its own route to whatever you give it. If a trailing slash or a missing segment leaves you pointing at a URL that doesn’t exist, the SDK reports a 404 thatlookslike “the model doesn’t exist” — and you’ll waste twenty minutes checking model names that were fine all along.

Quick isolation trick:before debugging in Python, hit the endpoint directly.

curl -s -H "Authorization: Bearer $AI_API_KEY" "$AI_BASE_URL/models" | head

Ifcurlreturns a model list, your key and URL are correct and the problem is in your code. If it doesn’t, the problem was never your code. Isolating layers like this is the highest-value debugging habit in this entire course.

⚠️ The Part People Skip (Don’t)

When an unverified API connection, PDF extraction step, and prompt are combined, one failure creates several possible causes. Verify one layer at a time with the smallest test that can prove it works.

Teaching takeaway: Verify one layer at a time and keep the smallest diagnostic script that proves each layer works. A successful connection test is evidence for that request only; it does not guarantee future quota, model availability, or identical output.

✅ What You Have Now

A clean project with an isolated virtual environment, secrets stored outside your code and excluded from Git, a path-anchored.envloader that works no matter where you run the script from, and a small connection-check script ready for a live test with a provider you configure. That’s the whole foundation — and every remaining part builds directly on this file.

💡 Go One Level Deeper

Add a--checkflag tosummarize.pyso the connection test stays available as a subcommand rather than something you delete. Also worth reading before part 3:how models actually process your text— tokens and context windows stop being trivia the moment you feed a 40-page PDF into a request.

❓ Try This Yourself

Getconnection okprinting on your machine, then deliberately break it: rename.envto.env.bakand run the script again. Confirm you get your own clear error message instead of a cryptic SDK traceback. Knowing what your failure looks like is what makes part 2 fast.

🔗 Next in This Series

[Step 1-A-2/4] Extracting Text From Any PDF — Including the Ones That Fight Back, where we pull text out withpypdf, handle the scanned PDFs that return nothing at all, and fix the encoding bug that turns non-English text into question marks.


Written and edited by G. Troy. See the editorial standards for sourcing and AI-assistance practices.

Last updated: August 9, 2026

Security, cost, and compatibility note

Use a restricted project and a non-sensitive test prompt. Never print or commit credentials. Confirm current provider pricing, rate limits, data use, endpoint compatibility, and SDK documentation. Revoke any exposed key and treat outputs as unverified drafts.


Verified Setup Evidence

Tested locally on August 30, 2026. The local setup package was checked offline without a real API credential or provider request. Eight structural and safety checks passed.

Check Result
Python syntax PASS
Path-anchored dotenv loading PASS
Required-setting guard PASS
Secret exclusion via .gitignore PASS
Credential-pattern scan PASS
Placeholder provider boundary PASS
Deterministic connection prompt PASS
Finite client timeout PASS

SHA-256: AACC1B6AA7DEB19B6790D56627E47BEDAC9314001E3EC642D1E1BC0E12FC7E31

Verification boundary: these checks prove the package parses, loads configuration in the intended order, excludes a real .env, contains no detected live credential pattern, and builds one deterministic connection request. They do not prove that a provider is free, compatible, available, or configured for the reader. A live round trip still requires the reader’s chosen provider, valid account, supported model, endpoint, quota, billing state, and current data-use settings.