◉GEMINI LABJP
●ARGON — gemini-4-argon is generally available as of 10/13, aimed at complex coding, agents, math and science●DEEP RES — deep-research-pro-preview-12-2025 shuts down 10/23, 5 days left. Move to deep-research-preview-04-2026●NANO — gemini-nano-banana-2.1 is GA (10/06). gemini-3.1-flash-image is deprecated with no shutdown date yet●MCP — One write-up cut an 85,000-token tool result to 310 tokens by wrapping it in an ADK FunctionTool●FLASH — gemini-3.7-flash and 3.5-flash now route automatically to newer Flash models (10/08)●NEW — Stop Re-Reading Every Crash Report: Gre…●ARGON — gemini-4-argon is generally available as of 10/13, aimed at complex coding, agents, math and science●DEEP RES — deep-research-pro-preview-12-2025 shuts down 10/23, 5 days left. Move to deep-research-preview-04-2026●NANO — gemini-nano-banana-2.1 is GA (10/06). gemini-3.1-flash-image is deprecated with no shutdown date yet●MCP — One write-up cut an 85,000-token tool result to 310 tokens by wrapping it in an ADK FunctionTool●FLASH — gemini-3.7-flash and 3.5-flash now route automatically to newer Flash models (10/08)●NEW — Stop Re-Reading Every Crash Report: Gre…
Articles/API / SDK
◈ API / SDK/2026-03-30Beginner

Text Classification and Sentiment Analysis with Gemini API and Python — A

Learn how to build text classification and sentiment analysis pipelines using the Gemini API and Python. Leverage Structured Output for reliable labeling of customer reviews, support tickets, and social media posts.

gemini-api286python108text-classificationsentiment-analysisstructured-output28nlp

When You Need Text Classification and Sentiment Analysis

E-commerce reviews, brand mentions on social media, incoming support emails — the need to automatically categorize text and gauge sentiment keeps growing. Traditionally, you'd need to train a dedicated NLP model with hundreds of labeled examples. With the Gemini API's Structured Output, you can build a high-accuracy text classification and sentiment analysis pipeline in Python without any training data.

Setting Up Your Environment

Getting Your API Key

  1. Visit Google AI Studio
  2. Click "Get API key" in the left menu
  3. Create a new project or select an existing one to generate your key

Installing the SDK

pip install google-genai

Testing the Connection

from google import genai
 
client = genai.Client(api_key="YOUR_GEMINI_API_KEY")
 
response = client.models.generate_content(
    model="gemini-2.5-flash",
    contents="Hello, Gemini! Please send a test response."
)
print(response.text)
# Expected output: Hello! Here's your test response. How can I help you today?

If your API key is configured correctly, you'll see a response from Gemini.

Building a Text Classifier with Structured Output

Gemini API's Structured Output lets you receive classification labels in a strict JSON format. Instead of parsing free text, you get structured data that flows directly into your downstream processing.

Defining the Classification Schema

from google import genai
from pydantic import BaseModel, Field
from enum import Enum
from typing import Optional
 
class Category(str, Enum):
    PRODUCT_QUALITY = "product_quality"
    SHIPPING = "shipping"
    CUSTOMER_SERVICE = "customer_service"
    PRICING = "pricing"
    OTHER = "other"
 
class Sentiment(str, Enum):
    POSITIVE = "positive"
    NEUTRAL = "neutral"
    NEGATIVE = "negative"
 
class TextClassification(BaseModel):
    category: Category = Field(description="The primary category of the text")
    sentiment: Sentiment = Field(description="The sentiment polarity of the text")
    confidence: float = Field(description="Classification confidence score (0.0–1.0)", ge=0.0, le=1.0)
    keywords: list[str] = Field(description="Key terms extracted from the text (up to 5)")
    summary: str = Field(description="One-line summary of the text")

By defining Category and Sentiment as Enums, you ensure that the returned labels never contain unexpected values. Pydantic handles validation automatically.

Classifying a Single Text

client = genai.Client(api_key="YOUR_GEMINI_API_KEY")
 
review = "Arrived in just two days. The packaging was excellent and the product quality met my expectations. The only downside is the manual was only available in Japanese."
 
response = client.models.generate_content(
    model="gemini-2.5-flash",
    contents=f"""Analyze the following customer review.
 
Review: {review}
 
Return the category, sentiment, confidence, keywords, and summary.""",
    config={
        "response_mime_type": "application/json",
        "response_schema": TextClassification,
    }
)
 
result = TextClassification.model_validate_json(response.text)
print(f"Category: {result.category.value}")
print(f"Sentiment: {result.sentiment.value}")
print(f"Confidence: {result.confidence}")
print(f"Keywords: {result.keywords}")
print(f"Summary: {result.summary}")
 
# Expected output:
# Category: product_quality
# Sentiment: positive
# Confidence: 0.82
# Keywords: ['packaging', 'quality', 'delivery', 'manual', 'Japanese']
# Summary: Good product quality and fast delivery, but manual lacks English translation

Just pass the Pydantic model to response_schema, and Gemini returns a response matching your exact JSON structure. No parsing headaches.

Batch Processing for Large Datasets

In production, you'll often need to process hundreds or thousands of reviews at once. Here's a batch implementation with retry logic.

import time
from google import genai
from google.genai.errors import ClientError
 
client = genai.Client(api_key="YOUR_GEMINI_API_KEY")
 
reviews = [
    "The product itself is great, but shipping took over a week.",
    "Customer support was incredibly helpful and responsive.",
    "At this price point, the quality is genuinely surprising. Best value I've found.",
    "The item looks nothing like the photos. The return process is frustrating too.",
    "It works fine. Nothing special to report.",
]
 
def classify_review(review_text: str, max_retries: int = 3) -> Optional[TextClassification]:
    """Classify a review with retry logic."""
    for attempt in range(max_retries):
        try:
            response = client.models.generate_content(
                model="gemini-2.5-flash",
                contents=f"Analyze the following customer review.\n\nReview: {review_text}",
                config={
                    "response_mime_type": "application/json",
                    "response_schema": TextClassification,
                }
            )
            return TextClassification.model_validate_json(response.text)
        except ClientError as e:
            if "429" in str(e):
                wait = 2 ** attempt
                print(f"Rate limited. Retrying in {wait} seconds...")
                time.sleep(wait)
            else:
                print(f"Error: {e}")
                return None
    return None
 
# Run batch classification
results = []
for i, review in enumerate(reviews):
    print(f"Processing: {i + 1}/{len(reviews)}")
    result = classify_review(review)
    if result:
        results.append({"review": review, "classification": result})
    time.sleep(0.5)  # Respect rate limits
 
# Aggregate results
from collections import Counter
 
sentiment_counts = Counter(r["classification"].sentiment.value for r in results)
category_counts = Counter(r["classification"].category.value for r in results)
 
print(f"\n--- Sentiment Distribution ---")
for sentiment, count in sentiment_counts.most_common():
    print(f"  {sentiment}: {count}")
 
print(f"\n--- Category Distribution ---")
for category, count in category_counts.most_common():
    print(f"  {category}: {count}")
 
# Expected output:
# --- Sentiment Distribution ---
#   positive: 2
#   negative: 2
#   neutral: 1
# --- Category Distribution ---
#   shipping: 1
#   customer_service: 1
#   pricing: 1
#   product_quality: 2

The key points are adding time.sleep(0.5) between requests and using exponential backoff for 429 errors. For large-scale processing, consider using the Batch API approach covered in Gemini API Production Pipeline Architecture.

Multilingual Sentiment Analysis

Gemini handles multiple languages natively, so you can process Japanese, English, Chinese, and other texts through the same pipeline.

class MultilingualAnalysis(BaseModel):
    detected_language: str = Field(description="Detected language code (ja, en, zh, etc.)")
    sentiment: Sentiment
    confidence: float = Field(ge=0.0, le=1.0)
    translated_summary: str = Field(description="One-line summary in English")
 
multilingual_texts = [
    "This product exceeded my expectations! Highly recommended.",
    "配送が遅すぎます。二度と利用しません。",
    "产品质量一般,但价格很合理。",
]
 
for text in multilingual_texts:
    response = client.models.generate_content(
        model="gemini-2.5-flash",
        contents=f"Analyze the following text. Detect the language and determine the sentiment.\n\nText: {text}",
        config={
            "response_mime_type": "application/json",
            "response_schema": MultilingualAnalysis,
        }
    )
    result = MultilingualAnalysis.model_validate_json(response.text)
    print(f"Language: {result.detected_language} | Sentiment: {result.sentiment.value} | Summary: {result.translated_summary}")
 
# Expected output:
# Language: en | Sentiment: positive | Summary: Product exceeded expectations, strongly recommended
# Language: ja | Sentiment: negative | Summary: Severe dissatisfaction with slow delivery
# Language: zh | Sentiment: neutral | Summary: Average quality but reasonable pricing

No need to switch models for different languages — one prompt handles everything.

Custom Categories for Your Business

You can customize the classification categories to match your specific business needs. Here's an example for automatic support ticket routing.

class TicketPriority(str, Enum):
    URGENT = "urgent"
    HIGH = "high"
    MEDIUM = "medium"
    LOW = "low"
 
class TicketCategory(str, Enum):
    BUG_REPORT = "bug_report"
    FEATURE_REQUEST = "feature_request"
    BILLING = "billing"
    ACCOUNT = "account"
    GENERAL = "general"
 
class SupportTicket(BaseModel):
    category: TicketCategory
    priority: TicketPriority
    sentiment: Sentiment
    requires_human: bool = Field(description="Whether the ticket needs human escalation")
    suggested_response: str = Field(description="Suggested first response (under 50 words)")
 
ticket_text = "I can't log in to my account. The password reset email isn't arriving either. I need this resolved urgently."
 
response = client.models.generate_content(
    model="gemini-2.5-flash",
    contents=f"""Analyze the following support ticket and determine the category, priority, sentiment, escalation need, and suggested response.
 
Ticket: {ticket_text}""",
    config={
        "response_mime_type": "application/json",
        "response_schema": SupportTicket,
    }
)
 
ticket = SupportTicket.model_validate_json(response.text)
print(f"Category: {ticket.category.value}")
print(f"Priority: {ticket.priority.value}")
print(f"Sentiment: {ticket.sentiment.value}")
print(f"Escalation: {'Required' if ticket.requires_human else 'Not needed'}")
print(f"Suggested Response: {ticket.suggested_response}")
 
# Expected output:
# Category: account
# Priority: urgent
# Sentiment: negative
# Escalation: Required
# Suggested Response: We're looking into your login issue right away. A team member will follow up shortly.

Adding a requires_human field lets you automatically separate tickets that can be handled by bots from those that need human attention.

Prompt Design Tips for Better Accuracy

Classification accuracy depends heavily on how you write your prompts. Here are techniques that make a real difference.

Using System Instructions

response = client.models.generate_content(
    model="gemini-2.5-flash",
    contents=review_text,
    config={
        "system_instruction": """You are a text classification expert.
Follow these rules:
- When text fits multiple categories, choose the single most prominent one
- Set confidence conservatively (only use 0.9+ for clear-cut cases)
- Watch for sarcasm and irony (e.g., "Oh great, another delay" is negative)
- Consider emojis and emoticons as sentiment signals""",
        "response_mime_type": "application/json",
        "response_schema": TextClassification,
    }
)

Adding Few-Shot Examples

few_shot_prompt = """Classify the following review.
 
## Classification examples
Review: "Best product ever!" → category: product_quality, sentiment: positive, confidence: 0.95
Review: "It's okay I guess" → category: product_quality, sentiment: neutral, confidence: 0.70
Review: "I want a refund" → category: billing, sentiment: negative, confidence: 0.90
 
## Text to classify
Review: {review_text}"""

Adding just 2–3 few-shot examples significantly improves accuracy on borderline cases like mildly positive-neutral texts. For more details on configuring Structured Output, check out the Gemini API JSON Mode and Structured Output Guide.

Looking back

By combining the Gemini API's Structured Output with Pydantic models, you can build a text classification and sentiment analysis pipeline quickly — no training data required. Defining categories and sentiment labels as Enums ensures consistent, reliable results that plug directly into your data workflows.

The patterns shown here apply well beyond customer reviews: support ticket routing, social media monitoring, survey analysis, and more. Start with the Gemini API Quickstart Guide to get your API key, and try classifying a small dataset first.

Share

Thank You for Reading

Gemini Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

◈ API / SDK2026-05-16
Automating Firebase Crashlytics Analysis with Gemini API — A Real-World Implementation from an Indie App
A real-world implementation record of automating Firebase Crashlytics log analysis with Gemini API, validated during development of an indie wallpaper app (v2.1.0). Includes Before/After code for a RecyclerView crash fix and a production cost breakdown.
◈ API / SDK2026-10-16
The Day /models Returns 200 but generateContent Returns 403 — Decide Your Triage Order Before It Happens
A triage order for the case where listing models succeeds with 200 but generation fails with 403 permission_denied. Includes a small diagnostic script that checks key, project, and billing in turn, why you should not retry a 403, and how to assemble what support needs.
◈ API / SDK2026-09-04
Screen Loop Seam Clicks With Numbers Before You Hand the Audio to Gemini
An ambient loop that clicks only at the wrap point. Here is the numeric prescreen I run before sending anything to audio understanding, why an absolute threshold fails, and how AAC encoding quietly rebuilt the seam I had just repaired.