Python AI Tutorial Β· Streamlit + Google Gemini Β· 100% Free

Build an AI Web App
with Python
& Free APIs.

A complete, beginner-friendly tutorial to build a multi-tool AI web application in Python using Streamlit and the genuinely-free Google Gemini API tier. Five tools in one app: summarizer, sentiment analyzer, Q&A, content generator, and translator. Full source code, .env setup, error handling, and free deployment on Streamlit Community Cloud β€” no credit card, no trial expiry. If you’re looking for practical Python projects with source code, this one is end-to-end.

πŸ€–5 AI Tools in 1 App
πŸ’Έ$0 Total Cost
⚑2-Hour Build
πŸš€Free Cloud Deploy
app.py
# AI Content Studio β€” Streamlit + Gemini
import streamlit as st
from src.ai_client import summarize_text

text = st.text_area("Paste text")
if st.button("Summarize"):
    out = summarize_text(text, n=5)
    st.markdown(out)
5 Toolssummarize, sentiment, QA…
Gemini 1.5 Flash15 RPM Β· 1500/day free
Free deployStreamlit Community Cloud
1 Β· Introduction

What we’re building
and why it matters.

In this tutorial you’ll build AI Content Studio β€” a single Python web application that bundles five AI tools behind a clean browser UI: a text summarizer, a sentiment analyzer, a question answerer, a content generator, and a translator. The whole app runs on Streamlit (a Python web framework that needs zero HTML/CSS) and calls the Google Gemini API free tier for the actual AI work.

🎯 Why this project?

Most “AI web app” tutorials online either (a) call paid APIs like OpenAI without mentioning the cost, or (b) show pseudocode that doesn’t actually run. This one is different on three counts:

  • Genuinely free end-to-end. The Gemini 1.5 Flash free tier gives you 15 requests/minute and 1,500 requests/day with no credit card. Streamlit Community Cloud hosts the app for $0 from a public GitHub repo. You can build, run, and ship this without spending anything.
  • Real working code, not snippets. Every file β€” app.py, src/ai_client.py, src/prompts.py, requirements.txt, .env.example, .streamlit/config.toml, README.md β€” is provided in full. You can clone the structure, paste the code, and run streamlit run app.py.
  • Production-grade error handling. The wrapper catches the four Gemini errors beginners actually hit β€” rate-limit (429), invalid key (403), invalid argument (400), and generic API failure β€” and surfaces friendly messages instead of stack traces.

If you’ve worked through our broader Python projects with source code guide, this is the natural next step: it’s the first project that puts a real AI model behind a real web UI you can share with non-technical users.

By the end you’ll have a deployed web app at a URL like https://your-username-ai-content-studio.streamlit.app that anyone can open in a browser. The same architecture β€” Streamlit frontend, a thin Python wrapper around an LLM API, and free cloud hosting β€” works for chatbots, code explainers, document Q&A tools, and most other single-user AI apps you’ll want to prototype.

2 Β· Definition

What is an AI-powered
web application?

An AI-powered web application is any web app where the core business logic β€” what happens when the user clicks a button β€” is delegated to a machine-learning model instead of being hand-coded with rules. In a traditional web app, the server runs deterministic code: if user.age > 18: show_premium_page(). In an AI web app, the server forwards the user’s input to a model and renders whatever the model returns.

There are two architectural patterns:

🧠 Two architectures

Self-hosted model
vs. hosted API.

Most beginners should start with the API approach β€” it’s what this tutorial uses.

A

Self-hosted model

You download a model (e.g. Llama 3 8B, ~5 GB) and run it inside your Python process with transformers or llama-cpp-python. Pros: free, private, no rate limits. Cons: needs a GPU or 16+ GB RAM, slow on CPU, complex deployment.

B

Hosted API (this tutorial)

You call a remote model via HTTP. The provider (Google, OpenAI, Anthropic, Hugging Face) runs the model on their GPUs and charges per request β€” but most offer a free tier generous enough for prototyping. Pros: fast, no GPU needed, simple deployment. Cons: rate limits, network latency, data leaves your machine.

If you’ve already built something like our Python sentiment analysis project using a locally-trained model, this tutorial shows the complementary pattern: instead of training the model yourself, you consume one as a service. Both are valid; the API approach is faster to ship and easier to maintain.

3 Β· Features

What the app
actually does.

🎯 5 tools, 1 UI

A sidebar radio
switches between
five AI tools.

All tools share the same layout β€” input on the left, output on the right β€” so users learn the pattern once.

πŸ“

Text Summarizer

Paste any long text (article, meeting notes, transcript) and get a 3–10 bullet-point summary. The number of bullets is adjustable with a slider.

😊

Sentiment Analyzer

Returns overall sentiment (Positive/Negative/Neutral), a confidence score, the key emotions detected, and a one-sentence explanation. Useful for product reviews, social media monitoring, and customer feedback.

❓

Question Answerer

Give the model a context paragraph and a question. It answers only from the context β€” and says “I cannot answer this from the given context” when the answer isn’t there, instead of hallucinating.

✍️

Content Generator

Generate 50–500 words on any topic in five tones: informative, casual, professional, funny, or persuasive. Useful for blog drafts, social posts, and email outlines.

🌐

Language Translator

Translate text between any two languages you type in (e.g. “English” β†’ “Spanish”, “Japanese” β†’ “French”). The model handles 100+ languages without you needing to maintain a list.

πŸ’‘ What makes this a real app, not a demo: Every tool has input validation (refuses empty or too-short input), a loading spinner during the API call, friendly error messages for rate-limit/key issues, and Markdown rendering of the output so bullet lists and bold text display correctly.
4 Β· Tech Stack

Technologies and APIs
we use.

Each choice is justified β€” no arbitrary picks.

πŸ“¦ Tech Stack
LayerChoiceFree-tier factsOfficial docs
LanguagePython 3.10+Free foreverdocs.python.org
Web frameworkStreamlit 1.39Open source, MITdocs.streamlit.io
AI APIGoogle Gemini 1.5 Flash15 RPM Β· 1,500/day Β· 1M TPM, no cardai.google.dev
Python SDKgoogle-generativeai 0.8.3Free PyPI packageGitHub repo
Secretspython-dotenv 1.0.1Free PyPI packagepypi.org
HostingStreamlit Community Cloud1 GB RAM Β· 5 apps Β· sleeps after 12hDeploy docs
Version controlGit + GitHubFree for public reposdocs.github.com
5 Β· Why These Choices

Why Streamlit + Gemini,
not Flask + OpenAI.

🧠 3 design decisions

Each choice
removes friction
for beginners.

If you disagree with any of these, the architecture is modular β€” swap one layer without touching the others.

01

Streamlit over Flask/Django

Flask forces you to write HTML templates, route handlers, and static asset folders β€” three new skills. Streamlit renders a polished UI from pure Python: st.text_area() becomes an HTML textarea, st.button() becomes a button, st.markdown() renders rich text. You write 80% less boilerplate. Trade-off: Streamlit is single-page and Python-only; if you need a public multi-user SaaS with custom auth, switch to Flask later.

02

Gemini over OpenAI

OpenAI’s API requires a credit card on signup and bills per token. There is no permanent free tier β€” only $5 of trial credit that expires in 3 months. Gemini’s free tier is permanent: 15 RPM, 1,500 RPD, no card, no expiry. For a tutorial aimed at students, that’s the difference between “I can build this for free forever” and “I need to set up billing first.”

03

Hosted API over self-hosted model

You could run Llama 3 8B locally with transformers β€” but you’d need 8+ GB VRAM, a GPU, and 15 minutes of model loading per session. The Gemini API returns answers in 1–3 seconds from any laptop, including a Chromebook. Trade-off: your data leaves your machine. Don’t paste trade secrets into any hosted AI API, free or paid.

⚠ Hugging Face note (October 2025): Hugging Face’s serverless Inference API used to be the standard “free AI API” recommendation. As of late 2025, free users must purchase credits to use Inference Providers β€” it is no longer strictly free. The Google Gemini free tier is currently the most generous no-cost option for generative AI. If you want to swap providers later, only src/ai_client.py needs to change.
6 Β· Prerequisites

What you need
before starting.

🎯 Minimum requirements
  • Python 3.10 or newer. Verify with python --version in your terminal. Streamlit and google-generativeai both require 3.9+; 3.10+ is recommended for the modern type-hint syntax used in ai_client.py.
  • A code editor. VS Code, PyCharm, Sublime, or even nano β€” anything that edits text files. We recommend VS Code with the official Python extension.
  • A terminal. On Windows: PowerShell or Git Bash. On macOS/Linux: the default Terminal.
  • A free Google account. Used to get the Gemini API key from Google AI Studio. No credit card.
  • A free GitHub account. Only needed for deployment (Step 17). You can build and run the whole app locally without one.
  • Basic Python. You should know what a function, a list, and an import statement are. If you’ve completed our Python projects guide, you’re ready.

No prior web development experience needed. No prior machine-learning experience needed. No prior API-integration experience needed β€” that’s literally what this tutorial teaches.

7 Β· Folder Structure

The complete project
file tree.

Recreate this exact structure β€” Streamlit expects app.py at the root, and the src/ package keeps the AI logic separate from the UI.

ai-content-studio/ β”œβ”€β”€ app.py # Streamlit entry point β€” the UI β”œβ”€β”€ requirements.txt # pip dependencies (locked versions) β”œβ”€β”€ .env.example # Template β€” copy to .env, paste your key β”œβ”€β”€ .env # Real API key β€” NEVER commit this β”œβ”€β”€ .gitignore # Tells git to skip .env, venv/, __pycache__ β”œβ”€β”€ README.md # Project documentation β”œβ”€β”€ src/ β”‚ β”œβ”€β”€ __init__.py # Empty file β€” marks src/ as a package β”‚ β”œβ”€β”€ ai_client.py # Gemini API wrapper + error handling β”‚ β”œβ”€β”€ prompts.py # Prompt templates for each tool β”‚ └── utils.py # Small Streamlit UI helpers └── .streamlit/ └── config.toml # Streamlit theme + server settings
🎯 Why split into src/?

You could put everything in one big app.py file. We don’t, for three reasons:

  • Testability. You can unit-test ai_client.py without launching Streamlit.
  • Swap-ability. If you later switch from Gemini to OpenAI or a local Llama model, you only edit ai_client.py β€” the UI code in app.py stays untouched.
  • Readability. app.py stays under 120 lines, focused purely on UI. The AI logic lives where it belongs.
8 Β· Environment Setup

Set up the Python
virtual environment.

Always use a virtual environment so this project’s dependencies don’t pollute your system Python.

Step 8.1 β€” Create the project folder and enter it

terminal
mkdir ai-content-studio
cd ai-content-studio

Step 8.2 β€” Create and activate a virtual environment

terminal
# macOS / Linux
python3 -m venv venv
source venv/bin/activate

# Windows (PowerShell)
python -m venv venv
venv\Scripts\Activate.ps1

# Windows (Git Bash)
python -m venv venv
source venv/Scripts/activate

You should now see (venv) at the start of your terminal prompt. If you don’t, the activation failed β€” check that you’re in the right folder and that Python is on your PATH. (If PowerShell blocks the script with an execution-policy error, run Set-ExecutionPolicy -Scope CurrentUser RemoteSigned once and retry.)

Step 8.3 β€” Install the dependencies

terminal
pip install streamlit==1.39.0 google-generativeai==0.8.3 python-dotenv==1.0.1
$~/ai-content-studiopip install streamlit google-generativeai python-dotenv
βœ“Successfully installed streamlit-1.39.0 google-generativeai-0.8.3 python-dotenv-1.0.1 (and 23 transitive dependencies)

Step 8.4 β€” Save the dependencies to requirements.txt

requirements.txt
streamlit==1.39.0
google-generativeai==0.8.3
python-dotenv==1.0.1

Locking the exact versions is what lets Streamlit Community Cloud reproduce your environment reliably. Without == pinning, a future breaking change in Streamlit 1.40+ could silently break your deployed app a month from now.

9 Β· Free API Key

Get your free
Gemini API key.

This takes 90 seconds and requires no credit card. The free tier is permanent β€” it does not expire.

🎯 Step-by-step
  1. Open https://aistudio.google.com/app/apikey in your browser.
  2. Sign in with any Google account (Gmail, Workspace, school Google account β€” all work).
  3. Read and accept the terms of service. (The free tier is for non-production use; that’s fine for a tutorial.)
  4. Click the Create API key button. If prompted, choose “Create API key in new project”.
  5. Copy the string that appears. It will look like AIzaSyA1B2C3D4E5F6G7H8I9J0K_L_M_N_O_P_Q (39 characters, starts with AIza).
  6. Paste it into your .env file (created in Step 10) as GEMINI_API_KEY=AIza....
⚠ Security rules: (1) Never paste the key into app.py or any other committed file. (2) Never commit .env to git. (3) Never share the key in screenshots, Discord, or Slack. (4) If you accidentally leak it, delete it from Google AI Studio and create a new one β€” Google automatically scans public GitHub for leaked keys and revokes them within minutes.

The free tier limits are generous for personal use: 15 requests per minute, 1,500 requests per day, and 1 million tokens per minute. For comparison, a typical summarization request uses ~500 input tokens and ~200 output tokens β€” so you can summarize about 2,000 articles per day before hitting the daily cap. Full pricing details are on the official Gemini pricing page.

11 Β· Complete Source Code

Every file, fully listed.
Copy and run.

Each block has a copy button. Recreate the folder structure from Section 7, then paste each file’s contents.

File 1 β€” .env.example (commit this) + .env (never commit)

Create .env.example with the template below. Then copy it to .env and replace the placeholder with your real key from Section 9.

.env.example
# Copy this file to .env and paste your real key from
# https://aistudio.google.com/app/apikey
GEMINI_API_KEY=your_api_key_here

Then in your terminal:

terminal
cp .env.example .env
# Then open .env in your editor and replace "your_api_key_here"
# with your real key starting with AIza...

File 2 β€” .gitignore

.gitignore
# Virtual environment
venv/
.env

# Python cache
__pycache__/
*.pyc

# Streamlit secrets (used in cloud deploy)
.streamlit/secrets.toml

File 3 β€” src/prompts.py

Prompt engineering is half the work. The five templates below are battle-tested β€” they reliably produce structured output for each tool. Save them in src/prompts.py.

src/prompts.py
SUMMARIZE_PROMPT = """Summarize the following text in {n} bullet points.
Be concise and factual. Do not add information that is not in the source.

TEXT:
{text}

SUMMARY:"""

SENTIMENT_PROMPT = """Analyze the sentiment of the following text.
Return your answer in this exact format:
- Overall sentiment: Positive / Negative / Neutral
- Confidence: 0-100%
- Key emotions: (comma-separated list)
- One-sentence explanation

TEXT:
{text}

ANALYSIS:"""

QA_PROMPT = """Use only the context below to answer the question.
If the answer is not contained in the context, reply exactly:
"I cannot answer this from the given context."

CONTEXT:
{context}

QUESTION:
{question}

ANSWER:"""

GENERATE_PROMPT = """You are a helpful writing assistant.
Write {n} words of {tone} content about: {topic}
Do not add a title or headings unless asked.

CONTENT:"""

TRANSLATE_PROMPT = """Translate the following text from {source_lang} to {target_lang}.
Preserve the original tone and formatting. Return only the translation.

TEXT:
{text}

TRANSLATION:"""

File 4 β€” src/ai_client.py (the AI wrapper)

This is the most important file. It loads the Gemini model once at import time, exposes five functions (one per tool), and catches every common Gemini error with a friendly message.

src/ai_client.py
"""Thin wrapper around the Google Gemini API with friendly error handling."""
import os
import google.generativeai as genai
from google.api_core.exceptions import (
    ResourceExhausted, PermissionDenied, GoogleAPICallError, InvalidArgument,
)
from .prompts import (
    SUMMARIZE_PROMPT, SENTIMENT_PROMPT, QA_PROMPT,
    GENERATE_PROMPT, TRANSLATE_PROMPT,
)

# Load the API key from the environment
API_KEY = os.getenv("GEMINI_API_KEY")
if not API_KEY:
    raise RuntimeError(
        "GEMINI_API_KEY is missing. Copy .env.example to .env "
        "and paste your key from https://aistudio.google.com/app/apikey"
    )

genai.configure(api_key=API_KEY)
_MODEL = genai.GenerativeModel("gemini-1.5-flash")

class AIClientError(Exception):
    """Raised when the Gemini API call fails. The message is user-facing."""

def _safe_generate(prompt: str) -> str:
    try:
        response = _MODEL.generate_content(prompt)
        return response.text.strip()
    except ResourceExhausted:
        raise AIClientError(
            "Rate limit reached. The free tier allows 15 requests/minute "
            "and 1,500/day. Wait 60 seconds and try again."
        )
    except PermissionDenied:
        raise AIClientError(
            "API key rejected. Check that GEMINI_API_KEY in your .env "
            "matches a key from https://aistudio.google.com/app/apikey"
        )
    except InvalidArgument as e:
        raise AIClientError(f"Invalid request: {e}")
    except GoogleAPICallError as e:
        raise AIClientError(f"Gemini API error: {e}")

def summarize_text(text: str, n: int = 5) -> str:
    if len(text.strip()) < 30:
        raise AIClientError("Please paste at least 30 characters to summarize.")
    return _safe_generate(SUMMARIZE_PROMPT.format(n=n, text=text))

def analyze_sentiment(text: str) -> str:
    if len(text.strip()) < 10:
        raise AIClientError("Please paste at least 10 characters to analyze.")
    return _safe_generate(SENTIMENT_PROMPT.format(text=text))

def answer_question(context: str, question: str) -> str:
    if len(context.strip()) < 30:
        raise AIClientError("Context must be at least 30 characters.")
    if len(question.strip()) < 3:
        raise AIClientError("Question must be at least 3 characters.")
    return _safe_generate(QA_PROMPT.format(context=context, question=question))

def generate_content(topic: str, tone: str = "informative", n: int = 200) -> str:
    if len(topic.strip()) < 3:
        raise AIClientError("Please enter a topic of at least 3 characters.")
    return _safe_generate(GENERATE_PROMPT.format(topic=topic, tone=tone, n=n))

def translate_text(text: str, source_lang: str, target_lang: str) -> str:
    if len(text.strip()) < 5:
        raise AIClientError("Please enter at least 5 characters to translate.")
    return _safe_generate(
        TRANSLATE_PROMPT.format(
            text=text, source_lang=source_lang, target_lang=target_lang
        )
    )
πŸ” Code walkthrough
os.getenv("GEMINI_API_KEY")
Reads the key from the environment (set by python-dotenv before this module imports). If missing, raises a clear RuntimeError instead of letting the SDK fail later with a cryptic 403.
_MODEL = genai.GenerativeModel("gemini-1.5-flash")
Loads the model once at import time. Re-loading per request is wasteful β€” the SDK caches it, but explicit is better.
_safe_generate(prompt)
Private helper that catches the four common errors (rate-limit, permission, invalid-argument, generic API failure) and converts each to a user-friendly AIClientError.
summarize_text(text, n=5)
Public function β€” validates input length, formats the prompt template, calls _safe_generate, returns the model’s text.

File 5 β€” src/utils.py

Three small Streamlit helpers that keep app.py readable.

src/utils.py
"""Small helpers shared across the Streamlit UI."""
import streamlit as st

def show_error(message: str) -> None:
    st.error(f"⚠ {message}")

def show_success(message: str) -> None:
    st.success(f"βœ… {message}")

def render_markdown(content: str) -> None:
    st.markdown(content)

File 6 β€” app.py (the Streamlit UI)

This is the file you launch with streamlit run app.py. It defines the sidebar tool selector, the five tool layouts, and the footer. Each tool calls the corresponding function from src/ai_client.py and renders the result with st.markdown.

app.py
"""AI Content Studio β€” a Streamlit web app powered by Google Gemini."""
import os
from dotenv import load_dotenv
import streamlit as st

# Load .env before importing ai_client (which reads the key at import time)
load_dotenv()

from src.ai_client import (
    AIClientError, summarize_text, analyze_sentiment,
    answer_question, generate_content, translate_text,
)
from src.utils import show_error, show_success, render_markdown

st.set_page_config(
    page_title="AI Content Studio",
    page_icon="πŸ€–",
    layout="wide",
    initial_sidebar_state="expanded",
)

# ---------- Header ----------
st.title("πŸ€– AI Content Studio")
st.caption("Powered by Google Gemini 1.5 Flash Β· Free tier Β· No credit card required")

# ---------- Sidebar ----------
with st.sidebar:
    st.header("Choose a tool")
    tool = st.radio(
        "Available tools",
        ["πŸ“ Summarize", "😊 Sentiment", "❓ Q&A", "✍️ Generate", "🌐 Translate"],
        label_visibility="collapsed",
    )
    st.divider()
    st.markdown(
        "**Free tier limits**\n"
        "- 15 requests / minute\n"
        "- 1,500 requests / day\n"
        "- 1 million tokens / minute\n\n"
        "_No credit card required. Get a key at "
        "[Google AI Studio](https://aistudio.google.com/app/apikey)._\""
    )

# ---------- Tool layouts ----------
left, right = st.columns([1, 1.2])

if tool == "πŸ“ Summarize":
    with left:
        st.subheader("Paste your text")
        text = st.text_area("Text to summarize", height=300, key="sum_text")
        n = st.slider("Number of bullet points", 3, 10, 5)
        if st.button("Summarize", type="primary"):
            try:
                with st.spinner("Summarizing..."):
                    out = summarize_text(text, n=n)
                show_success("Done!")
                render_markdown(out)
            except AIClientError as e:
                show_error(str(e))

elif tool == "😊 Sentiment":
    with left:
        st.subheader("Paste text to analyze")
        text = st.text_area("Text", height=300, key="sent_text")
        if st.button("Analyze", type="primary"):
            try:
                with st.spinner("Analyzing..."):
                    out = analyze_sentiment(text)
                show_success("Done!")
                render_markdown(out)
            except AIClientError as e:
                show_error(str(e))

elif tool == "❓ Q&A":
    with left:
        st.subheader("Provide context and a question")
        context = st.text_area("Context (the source text)", height=220, key="qa_ctx")
        question = st.text_input("Your question", key="qa_q")
        if st.button("Answer", type="primary"):
            try:
                with st.spinner("Answering..."):
                    out = answer_question(context, question)
                show_success("Done!")
                render_markdown(out)
            except AIClientError as e:
                show_error(str(e))

elif tool == "✍️ Generate":
    with left:
        st.subheader("Generate new content")
        topic = st.text_input("Topic", key="gen_topic")
        tone = st.selectbox("Tone", ["informative", "casual", "professional", "funny", "persuasive"])
        n = st.slider("Word count", 50, 500, 200, step=50)
        if st.button("Generate", type="primary"):
            try:
                with st.spinner("Generating..."):
                    out = generate_content(topic, tone=tone, n=n)
                show_success("Done!")
                render_markdown(out)
            except AIClientError as e:
                show_error(str(e))

elif tool == "🌐 Translate":
    with left:
        st.subheader("Translate text")
        text = st.text_area("Text to translate", height=220, key="tr_text")
        col1, col2 = st.columns(2)
        with col1:
            source_lang = st.text_input("From", value="English")
        with col2:
            target_lang = st.text_input("To", value="Spanish")
        if st.button("Translate", type="primary"):
            try:
                with st.spinner("Translating..."):
                    out = translate_text(text, source_lang, target_lang)
                show_success("Done!")
                render_markdown(out)
            except AIClientError as e:
                show_error(str(e))

with right:
    st.subheader("Result")
    st.info("Your AI output will appear on the left after you click the action button.")

# ---------- Footer ----------
st.divider()
st.caption(
    "Built with [Streamlit](https://streamlit.io) + "
    "[Google Gemini](https://ai.google.dev). "
    "Source: [Darekdari.com tutorial](https://darekdari.com/build-ai-web-app-python-free-apis/)"
)

File 7 β€” .streamlit/config.toml (optional, dark theme)

Create the .streamlit/ folder inside your project root, then create config.toml inside it. This makes Streamlit use a dark theme matching the Darekdari aesthetic and limits uploads to 5 MB.

.streamlit/config.toml
[server]
maxUploadSize = 5

[theme]
base = "dark"
primaryColor = "#22d3ee"
backgroundColor = "#04060d"
secondaryBackgroundColor = "#0a1128"
textColor = "#f5f8ff"
βœ… Don’t forget src/__init__.py β€” create an empty file at src/__init__.py. Without it, Python won’t recognize src/ as a package and your from src.ai_client import ... will fail with ModuleNotFoundError.
12 Β· The Web Interface

How the Streamlit UI
actually works.

Streamlit’s mental model is unique: every time the user interacts with a widget (clicks a button, types in a text area, moves a slider), Streamlit re-runs your entire app.py script from top to bottom. State is preserved only inside special widgets via the key parameter. This sounds inefficient, but it’s what lets you write a stateful UI without ever touching React or JavaScript.

πŸ” The 6 Streamlit primitives used in app.py
st.set_page_config(...)
Sets browser tab title, favicon, and layout. Must be the first Streamlit call β€” before any st.write or st.title.
st.sidebar.radio(...)
Renders the 5-tool selector in the sidebar. Returns the currently-selected string, which we store in tool and use to switch the main layout.
st.columns([1, 1.2])
Creates a 2-column layout β€” input on the left (narrower), output on the right (wider). The ratio [1, 1.2] gives the result panel slightly more space.
st.text_area(..., key="...")
Multi-line text input for the Summarize/Sentiment/QA/Translate tools. The key keeps the text preserved across re-runs.
st.button("...", type="primary")
Action button. Returns True only on the run triggered by clicking it β€” so the if st.button(...) block executes the AI call exactly once per click.
st.spinner("...")
Shows a loading animation while the Gemini API call is in flight (1-3 seconds typically). Without it, the user sees a frozen screen and assumes the app crashed.

If you’ve built a Python social media bot before, you’re used to writing CLI scripts that run once and exit. Streamlit is different: your script runs continuously, re-executing on every widget interaction. Once you internalize that, the framework becomes very intuitive.

13 Β· Connecting the AI API

How ai_client.py
talks to Gemini.

The flow for every tool is identical: UI collects input β†’ app.py validates β†’ ai_client.py formats the prompt β†’ google-generativeai SDK sends HTTP POST to Gemini β†’ response.text is returned β†’ app.py renders it. The whole round-trip takes 1–3 seconds.

Sequence diagram (text form)
User clicks "Summarize"
        |
        v
app.py (line 30)        ai_client.py           Gemini API          app.py (line 32)
+-----------------+   +-------------------+   +---------------+   +-----------------+
| st.button(       |--->| summarize_text(  |--->| HTTPS POST    |--->| st.markdown(     |
|  "Summarize") |   |  text, n=5)     |   | /v1beta/models/  |   |  out)            |
+-----------------+   +-------------------+   +---------------+   +-----------------+
                            |   | gemini-1.5-flash|   |
                            <---| response.text    |<---|
                            |   +---------------+   |

Two architectural choices matter here:

  • The model is loaded once at import time (_MODEL = genai.GenerativeModel("gemini-1.5-flash")). Loading per request would add ~200ms of overhead each call.
  • The _safe_generate() wrapper catches every common error and converts it to an AIClientError with a friendly message. Without this, a rate-limit hit would crash the app with a 50-line stack trace.
14 Β· Run & Test Locally

Launch the app
on your machine.

Step 14.1 β€” Verify your folder structure

From inside ai-content-studio/, run ls -la (macOS/Linux) or dir (Windows). You should see all 7 files from Section 7 plus the venv/ folder. If anything is missing, go back to Section 11.

Step 14.2 β€” Activate the virtual environment

terminal
# macOS / Linux
source venv/bin/activate

# Windows (PowerShell)
venv\Scripts\Activate.ps1

Step 14.3 β€” Launch Streamlit

terminal
streamlit run app.py
$~/ai-content-studiostreamlit run app.py
βœ“You can now view your Streamlit app in your browser. Local URL: http://localhost:8501 Network URL: http://192.168.1.42:8501

Your browser should auto-open at http://localhost:8501. If it doesn’t, open it manually. You should see the πŸ€– AI Content Studio title and the 5-tool sidebar.

Step 14.4 β€” Test each tool

🎯 5-minute smoke test
  1. Summarize: Paste any 200+ word paragraph (e.g. from a Wikipedia article). Click Summarize. You should get a 5-bullet summary in 2-3 seconds.
  2. Sentiment: Paste a strong product review (“This product is amazing! Best purchase I’ve made all year.”). Click Analyze. You should get “Overall sentiment: Positive, Confidence: 95%”.
  3. Q&A: Paste a paragraph about any topic, ask a question whose answer is in the text. Click Answer.
  4. Generate: Type “the future of remote work” as topic, pick “informative” tone, 200 words. Click Generate.
  5. Translate: Paste “Hello, how are you today?” β€” From: English, To: Spanish. Click Translate. You should get “Hola, ΒΏcΓ³mo estΓ‘s hoy?”.

Step 14.5 β€” Test error handling (important)

🎯 Verify error handling works
  1. Empty input: Click Summarize without typing anything. You should see “⚠ Please paste at least 30 characters to summarize.” β€” not a stack trace.
  2. Invalid API key: Temporarily rename your .env file to .env.bak, restart Streamlit, and try any tool. You should see the “API key rejected” error. Rename it back when done.
  3. Rate limit (optional): Click Summarize 16 times within 60 seconds. On the 16th, you should see “⚠ Rate limit reached. The free tier allows 15 requests/minute…” instead of a crash.
15 Β· Example I/O

Sample inputs
and outputs.

Real outputs from running the app on the Gemini 1.5 Flash free tier.

πŸ“ Summarize β€” example

Input β†’ Output
// INPUT (paste into Summarize tool)
"Python is a high-level, general-purpose programming language. Its design philosophy
emphasizes code readability with the use of significant indentation. Python is
dynamically typed and garbage-collected. It supports multiple programming paradigms,
including structured, object-oriented and functional programming. First released in
1991 by Guido van Rossum, Python has become one of the most popular programming
languages in the world, especially for data science, machine learning, and web
development. The Python Software Foundation maintains the language and curates
PyPI, a repository of over 500,000 third-party packages."

// OUTPUT (5 bullets)
- Python is a high-level, general-purpose language emphasizing code readability.
- It is dynamically typed, garbage-collected, and supports multiple paradigms.
- Created by Guido van Rossum in 1991, it's now among the world's most popular languages.
- Python dominates data science, machine learning, and web development.
- The Python Software Foundation maintains the language and PyPI (500k+ packages).

😊 Sentiment β€” example

Input β†’ Output
// INPUT
"I've been using this laptop for three months now and I'm extremely disappointed.
The battery dies after 2 hours, the screen flickers, and customer support keeps
transferring me between departments. Save your money."

// OUTPUT
- Overall sentiment: Negative
- Confidence: 96%
- Key emotions: frustration, disappointment, regret, anger
- Explanation: The user describes multiple product failures and unhelpful support,
            ending with an explicit warning to others β€” strong negative sentiment.

🌐 Translate β€” example

Input β†’ Output
// INPUT (From: English β†’ To: Spanish)
"The quick brown fox jumps over the lazy dog."

// OUTPUT
"El rΓ‘pido zorro marrΓ³n salta sobre el perro perezoso."
16 Β· Common Errors

The 5 errors you’ll
actually hit.

Each one has a 1-line fix.

Error 1 β€” ModuleNotFoundError: No module named 'src'

Cause: You forgot to create src/__init__.py, or you ran streamlit run app.py from inside the src/ folder instead of the project root.

Fix: Create an empty file at src/__init__.py. Then cd back to ai-content-studio/ (where app.py lives) and run streamlit run app.py again.

Error 2 β€” RuntimeError: GEMINI_API_KEY is missing

Cause: The .env file doesn’t exist, or you forgot the load_dotenv() call before importing ai_client, or the key value has a typo.

Fix: Run cp .env.example .env, open .env in your editor, paste your real key (starts with AIza), save. Confirm app.py line 6 calls load_dotenv() before the from src.ai_client import line.

Error 3 β€” 429 Resource has been exhausted

Cause: You exceeded the free-tier rate limit (15 requests/minute or 1,500/day).

Fix: Wait 60 seconds (for the per-minute limit) or until midnight Pacific time (for the daily limit). The _safe_generate wrapper converts this into a friendly message β€” you should never see the raw 429 unless you’re calling the SDK directly.

Error 4 β€” 403 PermissionDenied

Cause: Wrong API key, expired key, or Google revoked the key (often because it was committed to a public GitHub repo).

Fix: Visit Google AI Studio, delete the old key, create a new one, paste it into .env. If you committed the old key to GitHub, force-push to remove it from history (git filter-branch or BFG Repo-Cleaner) β€” Google scans public repos.

Error 5 β€” StreamlitAPIException: files need a unique key

Cause: Two Streamlit widgets share the same key= parameter. Streamlit uses keys to track widget state across re-runs; duplicates break it.

Fix: Open app.py and verify every st.text_area and st.text_input has a unique key value. The 5 tools use: sum_text, sent_text, qa_ctx, qa_q, gen_topic, tr_text.

17 Β· Free Deployment

Deploy to Streamlit
Community Cloud.

3-minute deploy from a public GitHub repo. The hosting is genuinely free for public apps.

🎯 Step-by-step deploy
  1. Push to GitHub. Create a public repo (e.g. ai-content-studio) and push your project. Double-check .env is in .gitignore β€” verify with git status that .env does NOT appear as a tracked file.
  2. Open share.streamlit.io. Sign in with the same GitHub account.
  3. Click “New app”. Pick your repo, the main branch, and the file path app.py.
  4. Open Advanced Settings β†’ Secrets. Paste this TOML:
    GEMINI_API_KEY = "AIza...your-real-key-here"
    This is how Streamlit Community Cloud injects your API key without you committing it to git.
  5. Click Deploy. The first build takes 1-2 minutes (Streamlit installs your requirements.txt in a fresh container). When done, you’ll get a URL like https://your-username-ai-content-studio.streamlit.app.
  6. Verify the deployed app by opening the URL in an incognito window and running the same smoke test from Section 14.
⚠ Streamlit Community Cloud limits (October 2025): Free tier includes 1 GB RAM per app, 5 apps per account, and apps sleep after 12 hours of inactivity (a 5-10 second cold-start when a user revisits). No custom domain. For higher traffic, upgrade to Streamlit in Snowflake or self-host on Render/Fly.io β€” but for a personal portfolio project, the free tier is plenty.

Every git push to your main branch triggers an automatic redeploy within ~60 seconds. There’s no CI/CD to configure β€” it just works. This is the fastest iteration loop of any free Python hosting option we’ve found.

18 Β· Future Enhancements

Where to take this
from here.

πŸš€ 5 next steps

Pick one,
implement it,
add to your portfolio.

Each enhancement below is a 2-4 hour project on top of the existing codebase.

01

Add chat memory

Swap the one-shot Q&A tool for a conversational chat using genai.GenerativeModel.start_chat(). The Gemini SDK handles message history automatically β€” you just append user turns and render the response.

02

Switch to a stronger model

Change "gemini-1.5-flash" to "gemini-1.5-pro" in ai_client.py. The Pro model is smarter but has a tighter free tier (2 RPM, 50 RPD). Use it for the Q&A tool only.

03

Add file upload + PDF parsing

Use st.file_uploader + pypdf2 to let users upload a PDF and summarize or Q&A it. The text-extraction logic is ~15 lines.

04

Add usage analytics

Log every API call to a SQLite database with timestamp, tool name, and token count. Render a weekly chart with st.bar_chart. Useful for tracking your own usage against the 1,500/day limit.

05

Multi-language UI

Add a language dropdown in the sidebar and translate the UI labels. The Gemini API can translate the UI strings on first launch β€” store the result in st.session_state.

For more Python project ideas that pair well with this one β€” including a full Python activation and license validation control system if you ever want to commercialize an AI app β€” browse our Computer Engineering project archive.

19 Β· What You Learned

Skills gained from
this project.

πŸŽ“ 6 transferable skills

Each skill
transfers to your
next AI project.

01

Streamlit fundamentals

Widgets, layout, sidebar, columns, session state, page config. Enough to build any data app you can imagine.

02

LLM API integration

Loading a model, formatting prompts, parsing responses, handling errors. The same pattern works for OpenAI, Anthropic, Cohere, and Hugging Face.

03

Secrets management

Using python-dotenv locally and Streamlit Secrets in production. Never committing .env to git.

04

Prompt engineering

How to write templates that produce structured output (bullet lists, sentiment reports). The skill that separates good AI apps from broken ones.

05

Python packaging

The src/ package pattern, __init__.py, relative imports (from .prompts import ...).

06

Free cloud deployment

Git β†’ GitHub β†’ Streamlit Cloud. The same workflow applies to Render, Fly.io, and Hugging Face Spaces.

20 Β· Conclusion

You built an AI web app.
Now ship more.

You now have a deployed AI web application at a public URL, built entirely with free tools, with production-grade error handling and a clean modular codebase. That’s more than most “AI tutorials” deliver β€” most stop at “here’s how to call the API” and skip deployment, secrets, and error handling entirely.

The architecture you just learned β€” Streamlit UI + thin Python wrapper + hosted LLM API + free cloud deploy β€” is the same pattern behind most single-user AI tools you see on Product Hunt, indie hacker portfolios, and university final-year projects. Once you internalize it, building the next idea (chatbot, code explainer, document Q&A, content rewriter) takes hours, not weeks.

If you want to keep building, two natural next projects: (1) our Python sentiment analysis project shows you how to train your own sentiment model locally β€” useful when you don’t want to depend on a paid API; (2) our Python social media bot project shows you how to schedule automated posts β€” combine it with the content generator from this tutorial and you have an AI content pipeline.

21 Β· FAQ

Frequently asked
questions.

Can I really build an AI web app for free with Python? +

Yes. This tutorial uses two genuinely free services: the Google Gemini API free tier (Gemini 1.5 Flash at 15 requests/minute and 1,500 requests/day, no credit card required) and Streamlit Community Cloud (free hosting for public apps from public GitHub repos, 1 GB RAM, 5-app limit). You can build, run, and deploy the entire application for $0.

Is the Google Gemini API really free, or is it just a free trial? +

The Gemini API free tier is a permanent free tier, not a trial. It requires a Google account and an API key from Google AI Studio, but no credit card. The free tier of Gemini 1.5 Flash allows 15 requests per minute, 1,500 requests per day, and 1 million tokens per minute. If you exceed these limits, the API returns a 429 error and you simply wait β€” there are no charges.

Why use Streamlit instead of Flask or Django for an AI web app? +

Streamlit is purpose-built for data and AI apps: it handles HTML/CSS/JS automatically, so you write pure Python and get a polished, responsive web UI in 50 lines. Flask requires you to write templates, route handlers, and static assets separately. For a beginner AI project where the focus is on the AI logic, Streamlit cuts boilerplate by 80%. Flask is better for production APIs; Streamlit is better for interactive demos and internal tools. See the official Streamlit docs for more.

How do I securely store the Gemini API key in a Python web app? +

Store the API key in a .env file at the project root, load it with the python-dotenv library, and never commit .env to git. Add .env to your .gitignore. In Streamlit Community Cloud, paste the key into the Secrets manager (Settings β†’ Secrets) instead of using a .env file. Never hard-code API keys directly in app.py β€” if you push them to a public GitHub repo, Google will detect the leak and revoke the key automatically.

What happens when the Gemini free-tier rate limit is exceeded? +

The API returns HTTP 429 Too Many Requests. Our ai_client.py wraps every call in a try/except that catches google.api_core.exceptions.ResourceExhausted and returns a friendly error message to the user. The user can wait 60 seconds (for the 15-RPM limit) or until midnight Pacific time (for the 1,500-RPD limit) and retry.

Can I use Hugging Face Inference API instead of Gemini for this project? +

You can, but as of late 2025 Hugging Face requires free users to purchase credits to use serverless Inference Providers β€” it is no longer strictly free. The Google Gemini free tier is currently the most generous no-cost option for generative AI. If you want to swap providers, replace the ai_client.py module with a Hugging Face InferenceClient wrapper; the rest of the Streamlit app stays the same.

How long does it take to deploy a Streamlit app to Streamlit Community Cloud? +

About 3 minutes from a clean GitHub repo. You log in with GitHub at share.streamlit.io, pick the repo/branch/file, paste your GEMINI_API_KEY into the Secrets manager, and click Deploy. The first build takes 1-2 minutes; subsequent pushes redeploy automatically. The free tier sleeps the app after 12 hours of inactivity, with a 5-10 second cold start when a user revisits.

Build & deploy
your AI app today.

Get a free Gemini API key, clone the source code, and ship your AI Content Studio to a public URL in under 2 hours. Or click “Copy Full Project Code” at the top of this page to grab every file in one click.

5 AI tools$0 total cost2-hour buildfree cloud deploy
Share

Share this Tutorial

If this tutorial helped you build your first AI web app, share it with fellow students and developers.

Scroll to Top