← Blog

Build AI Apps with Python: A Practical Stack Guide

2026-09-20 · 5 min read · SubToAPI Team

Building AI apps with Python means wiring together a language model API, a web framework to expose your logic, and enough structure to handle streaming, errors, and state without the whole thing turning into a pile of scripts. Python is the default choice here because every major LLM provider ships a Python SDK first, the async ecosystem (FastAPI, httpx, asyncio) handles streaming responses well, and the data/ML tooling you'll eventually need — pandas, numpy, pydantic — is already native to the language.

This guide walks through the actual stack: what to install, how to structure a minimal AI app, and where things typically break (streaming, rate limits, tool calling, cost tracking) so you don't have to discover them the hard way.

The minimal stack

You don't need much to get a working AI app. The core pieces are:

A bare-bones request loop looks like this:

import os
import httpx

API_KEY = os.environ["SUBTOAPI_KEY"]

async def ask(prompt: str) -> str:
    async with httpx.AsyncClient() as client:
        resp = await client.post(
            "https://api.subtoapi.app/v1/messages",
            headers={"Authorization": f"Bearer {API_KEY}"},
            json={
                "model": "claude-sonnet-4",
                "max_tokens": 1024,
                "messages": [{"role": "user", "content": prompt}],
            },
        )
        resp.raise_for_status()
        data = resp.json()
        return data["content"][0]["text"]

That's a complete, working call. Everything else you add — streaming, tools, retries, caching — is layered on top of this base pattern.

Structuring the app, not just the API call

Once you go past a single script, the useful separation is: a thin API layer, a service layer that talks to the model, and a persistence layer if you need conversation history or usage tracking.

app/
  api/          # FastAPI routes
  services/     # LLM calls, tool execution, business logic
  models/       # pydantic schemas
  storage/      # database or file-based history

FastAPI routes should stay small — parse the request, call a service function, return the response. Put retry logic, prompt construction, and error handling in the service layer so it's testable without spinning up a server.

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class ChatRequest(BaseModel):
    message: str

@app.post("/chat")
async def chat(req: ChatRequest):
    reply = await ask(req.message)
    return {"reply": reply}

This is enough to deploy behind a load balancer, add auth middleware, or expose to a frontend.

Streaming responses without blocking

If your app has any kind of chat interface, streaming matters — users wait less and perceive the app as faster. In Python this means using Server-Sent Events or a chunked response, consumed with an async generator.

import json
import httpx

async def stream_reply(prompt: str):
    async with httpx.AsyncClient() as client:
        async with client.stream(
            "POST",
            "https://api.subtoapi.app/v1/messages",
            headers={"Authorization": f"Bearer {API_KEY}"},
            json={
                "model": "claude-sonnet-4",
                "max_tokens": 1024,
                "stream": True,
                "messages": [{"role": "user", "content": prompt}],
            },
        ) as resp:
            async for line in resp.aiter_lines():
                if line.startswith("data:"):
                    yield line[5:].strip()

FastAPI can wrap this in a StreamingResponse, and on the frontend you consume it with EventSource or fetch with a readable stream. Details on the event format are in the streaming docs if you're integrating this against SubToAPI directly.

Adding tool use

Most real AI apps eventually need the model to call functions — look up a record, hit an internal API, run a calculation. The pattern is: define tool schemas, send them with the request, and when the model returns a tool call, execute it in your own code and send the result back.

tools = [
    {
        "name": "get_order_status",
        "description": "Look up the status of an order by ID",
        "input_schema": {
            "type": "object",
            "properties": {"order_id": {"type": "string"}},
            "required": ["order_id"],
        },
    }
]

The model decides when to call the tool; your Python code stays in control of what actually executes, which matters for anything touching a database or external system. See /docs/tools for the exact request/response shape.

Where SubToAPI fits

If you already have Claude access through a subscription rather than a pay-per-token API account, wiring that into a Python app directly is awkward — there's no clean HTTPS API meant for application traffic. SubToAPI turns that subscription into a proper API: you get sub_live_... application keys, standard REST endpoints for messages, streaming and tool use, and usage metadata per key so you can see what each app or teammate is consuming. It's a drop-in replacement for the httpx calls above — same JSON shape, same streaming format — which means you can build the app once and not rewrite it if you change how you're billed for model access.

Setup is a signup and one API key: check /docs/quickstart for the five-minute version, or /docs/messages for the full request reference. Plans start at Solo (€9), with Team (€19/seat) and Scale (€49/seat) for multi-key setups — see /pricing. There's a free trial at /signup if you want to test the stack above against it before committing.

Testing and deploying

Keep model calls behind an interface you can mock in tests — don't hit a live API in your test suite. pytest with respx (for mocking httpx) covers this well. For deployment, a FastAPI app behind uvicorn/gunicorn on any container platform works fine; there's nothing AI-specific about the deployment story once the API layer is built correctly.

questions

Do I need a GPU to build AI apps with Python? No. If you're calling a hosted model API (Claude, or a SubToAPI-backed endpoint), all inference happens on the provider's infrastructure. You only need a GPU if you're running or fine-tuning open-weight models locally.

Which Python framework should I use: FastAPI or Flask? FastAPI is the better default for AI apps because of native async support and built-in request validation via pydantic, which matters when you're streaming responses or defining tool schemas.

How do I avoid hardcoding API keys in a Python AI app? Load them from environment variables with python-dotenv locally and your platform's secrets manager in production. Never commit keys to source control, and rotate them if you're granting per-teammate access.

Turn your Claude access into an HTTPS API

SubToAPI gives you application API keys, streaming, tool use and usage insights on top of your existing Claude access — set up in minutes.

Start free  Read the quickstart →