Setup and context — Why Build a Summarizer with Gemini API
In an age of information overload, extracting key insights from lengthy documents is a constant challenge for professionals and developers alike. With Gemini API's powerful natural language understanding, you can build a web application that accurately summarizes long documents and PDF reports in seconds.
In this tutorial, we'll build a fully functional AI document summarizer from scratch using Python Flask and the Gemini API. The app handles both plain text input and PDF file uploads, making it practical for real-world use.
What you'll learn:
- Setting up and calling the Gemini API with the Google AI Python SDK
- Building a web application with Python Flask
- Prompt engineering techniques for high-quality summaries
- Integrating PDF text extraction into a summarization pipeline
If you're new to the Gemini API, the [Gemini API Quickstart Guide]((/articles/gemini-api/gemini-api-quickstart) is a great place to start before diving in.
Prerequisites and Setup
Requirements
- Python 3.10 or later
- A Gemini API key from Google AI Studio (free tier available)
- pip (Python package manager)
Project Initialization
Create a project directory and install the required packages:
# Run in your terminal
# Create project directory
mkdir gemini-summarizer && cd gemini-summarizer
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
# Install required packages
pip install flask google-genai pypdf2The google-genai package is Google's official Python SDK for accessing the Gemini API. pypdf2 handles PDF text extraction.
API Key Configuration
For security, manage your API key as an environment variable:
# Create a .env file (add to .gitignore)
GEMINI_API_KEY=YOUR_API_KEYApplication Architecture
The summarizer application consists of three components:
1. Frontend (HTML templates): Provides a text input form, PDF upload functionality, and displays the summary results.
2. Flask backend: Handles request routing, file processing, and Gemini API orchestration.
3. Gemini API module: The core component responsible for prompt construction and summary generation.
This clean separation keeps the code maintainable and easy to extend.
Building the Gemini API Module
Let's start with the core module that communicates with the Gemini API:
# summarizer.py — Gemini API summarization module
import os
from google import genai
# Initialize the client
client = genai.Client(api_key=os.environ.get("GEMINI_API_KEY"))
def summarize_text(text: str, style: str = "standard") -> dict:
"""
Summarize text using the Gemini API
Args:
text: The text to summarize
style: Summary style ("standard", "bullet", "executive")
Returns:
dict: {"summary": str, "original_length": int, "key_points": list}
"""
# Style-specific prompt instructions
style_instructions = {
"standard": "Write a concise, readable summary in paragraph form.",
"bullet": "Organize the key points as a bulleted list.",
"executive": "Create an executive summary with clear conclusions and recommended actions."
}
instruction = style_instructions.get(style, style_instructions["standard"])
prompt = f"""Summarize the following text.
【Summarization Rules】
1. Include all major arguments from the original text
2. {instruction}
3. Preserve technical terms and add brief explanations where needed
4. Target approximately 20-30% of the original text length
5. End with 3-5 "Key Takeaways" as a bulleted list
【Source Text】
{text}
【Output Format】
## Summary
(Summary text here)
## Key Takeaways
- Takeaway 1
- Takeaway 2
- Takeaway 3
"""
response = client.models.generate_content(
model="gemini-2.5-flash",
contents=prompt
)
# Parse the response
result_text = response.text
# Extract key takeaways
key_points = []
if "## Key Takeaways" in result_text:
kp_section = result_text.split("## Key Takeaways")[1]
key_points = [
line.strip().lstrip("- ")
for line in kp_section.strip().split("\n")
if line.strip() and line.strip().startswith("-")
]
return {
"summary": result_text,
"original_length": len(text),
"key_points": key_points
}
# Expected output example:
# {
# "summary": "## Summary\nThe Gemini API...\n\n## Key Takeaways\n- Point 1\n- Point 2",
# "original_length": 5000,
# "key_points": ["Point 1", "Point 2", "Point 3"]
# }Notice that we're using the gemini-2.5-flash model. For summarization tasks where response speed matters, the Flash model is the ideal choice. If you need higher accuracy for complex documents, you can switch to gemini-2.5-pro.
Implementing PDF Text Extraction
Add a helper function to extract text from uploaded PDF files:
# pdf_extractor.py — PDF text extraction module
from PyPDF2 import PdfReader
import io
def extract_text_from_pdf(file_stream) -> str:
"""
Extract text content from a PDF file
Args:
file_stream: Binary stream of the PDF file
Returns:
str: Extracted text content
"""
reader = PdfReader(io.BytesIO(file_stream))
text_parts = []
for page_num, page in enumerate(reader.pages, 1):
page_text = page.extract_text()
if page_text:
text_parts.append(f"--- Page {page_num} ---\n{page_text}")
full_text = "\n\n".join(text_parts)
if not full_text.strip():
raise ValueError("Could not extract text from the PDF. Scanned image PDFs require OCR processing.")
return full_text
# Usage example:
# with open("report.pdf", "rb") as f:
# text = extract_text_from_pdf(f.read())
# print(text[:500]) # Display first 500 charactersAs of March 2026, the Gemini API increased its file size limit from 20MB to 100MB. While you can send large PDFs directly to the API, pre-extracting text helps optimize token consumption and reduce costs.
Building the Flask Web Application
Now let's bring everything together in the Flask application:
# app.py — Flask main application
import os
from flask import Flask, render_template, request, jsonify
from summarizer import summarize_text
from pdf_extractor import extract_text_from_pdf
app = Flask(__name__)
app.config["MAX_CONTENT_LENGTH"] = 16 * 1024 * 1024 # 16MB limit
@app.route("/")
def index():
"""Render the home page"""
return render_template("index.html")
@app.route("/summarize", methods=["POST"])
def summarize():
"""Summarization API endpoint"""
try:
style = request.form.get("style", "standard")
# Handle PDF file upload
if "pdf_file" in request.files:
pdf_file = request.files["pdf_file"]
if pdf_file.filename and pdf_file.filename.endswith(".pdf"):
file_data = pdf_file.read()
text = extract_text_from_pdf(file_data)
else:
return jsonify({"error": "Please select a valid PDF file"}), 400
else:
# Handle text input
text = request.form.get("text", "").strip()
if not text:
return jsonify({"error": "Please enter text to summarize"}), 400
if len(text) < 100:
return jsonify({"error": "Text is too short (minimum 100 characters)"}), 400
# Run summarization via Gemini API
result = summarize_text(text, style=style)
return jsonify({
"success": True,
"summary": result["summary"],
"original_length": result["original_length"],
"key_points": result["key_points"]
})
except ValueError as e:
return jsonify({"error": str(e)}), 400
except Exception as e:
return jsonify({"error": f"Summarization failed: {str(e)}"}), 500
if __name__ == "__main__":
app.run(debug=True, port=5000)The MAX_CONTENT_LENGTH is set to 16MB to accommodate large PDF uploads. In production, adjust this value along with rate limiting based on your requirements.
Prompt Engineering for Better Summaries
The quality of your summaries depends heavily on prompt design. Here are techniques that significantly improve output quality with the Gemini API.
Leveraging the Context Window
Gemini 2.5 Flash supports a 1-million-token context window, allowing you to process extremely long documents in a single request. However, since token usage affects billing, it's worth trimming unnecessary content like headers, footers, and tables of contents before sending them to the API.
Controlling Output with Style Parameters
We defined three summary styles earlier, but you can customize further based on your use case:
# Extended summary style examples
CUSTOM_STYLES = {
"technical": (
"Focus on implementation details, architecture decisions, and technical specifics. "
"Pay special attention to code examples and API specifications."
),
"marketing": (
"Focus on market impact, competitive analysis, and business implications. "
"Always include numerical data when available."
),
"one_line": (
"Convey only the single most important point in one sentence."
)
}By parameterizing summary styles, you can generate optimized summaries for different audiences from the same source document. For more advanced output control techniques, check out the [Gemini Structured Output Production Guide]((/articles/gemini-advanced/gemini-structured-output-production-guide).
Error Handling and Retry Strategy
In production environments, implementing retry logic for transient API failures and rate limiting is essential:
# retry_handler.py — API call with retry logic
import time
import logging
logger = logging.getLogger(__name__)
def call_with_retry(func, max_retries=3, base_delay=1.0):
"""
Execute an API call with exponential backoff retry
Args:
func: The function to execute
max_retries: Maximum number of retry attempts
base_delay: Initial delay in seconds before first retry
Returns:
The function's return value
"""
for attempt in range(max_retries + 1):
try:
return func()
except Exception as e:
error_msg = str(e)
# Rate limit (429) — wait longer
if "429" in error_msg or "RESOURCE_EXHAUSTED" in error_msg:
delay = base_delay * (2 ** attempt) * 2
logger.warning(f"Rate limit hit. Retrying in {delay}s ({attempt + 1}/{max_retries})")
elif attempt < max_retries:
delay = base_delay * (2 ** attempt)
logger.warning(f"API call failed. Retrying in {delay}s ({attempt + 1}/{max_retries}): {error_msg}")
else:
logger.error(f"Max retries reached: {error_msg}")
raise
time.sleep(delay)
# Usage:
# result = call_with_retry(lambda: summarize_text(text, style="standard"))For a deeper dive into error handling best practices, see the [Gemini API Error Handling and Retry Patterns Guide]((/articles/gemini-api/gemini-api-error-handling-retry-patterns).
Deployment Tips
Local Testing
# Set environment variable and launch
export GEMINI_API_KEY=YOUR_API_KEY
python app.py
# Open http://localhost:5000 in your browser
# Or test with curl:
# curl -X POST http://localhost:5000/summarize \
# -F "text=Enter your text to summarize here..." \
# -F "style=bullet"Production Deployment
For production, pair Flask with a WSGI server like Gunicorn:
# Launch with Gunicorn
pip install gunicorn
gunicorn app:app --workers 4 --bind 0.0.0.0:8000Google Cloud Run is an excellent hosting choice for this type of application, as it minimizes latency when communicating with the Gemini API. With a Dockerfile in place, deployment is a single gcloud run deploy command away.
Summary
By combining the Gemini API with Python Flask, you can efficiently build a web application that automatically summarizes text and PDF documents. The summarization module we built includes style parameterization and retry handling, making it production-ready out of the box.
Gemini API's 1-million-token context window is a major advantage for processing lengthy documents in a single pass. Start by running the code from this tutorial, then experiment with prompts tailored to your specific use case. If you're interested in building more advanced automation pipelines, the [Gemini API Python Automation Recipes]((/articles/gemini-api/gemini-api-python-automation-recipes) guide is an excellent next step.