Your Python Type Hints Are Actually Your API Now
4 mins read

Your Python Type Hints Are Actually Your API Now

I spent years treating Python type hints like a bureaucratic tax. We’d write our logic, watch the linter complain about a missing return type, sigh heavily, and slap -> None on the function just to get the CI pipeline to pass.

I miss those days. Python used to be executable pseudocode.

But we don’t live in that world anymore. If you’re still treating type annotations as optional metadata for your IDE, you are fundamentally misunderstanding how modern Python applications actually run.

Your type hints aren’t just for mypy anymore. They are the literal execution engine for your application’s interface.

The silent shift from static to runtime

API development - API Development: Best Practices and Strategies | Astera
API development – API Development: Best Practices and Strategies | Astera

This didn’t happen overnight. Pydantic started the trend years ago by proving that type signatures could be used for runtime data validation. FastAPI took that and built an entire web framework around it.

But the real breaking point hit when we all started building agentic workflows and LLM wrappers. Suddenly, we had these non-deterministic text engines that needed to interact with our deterministic code. And the ecosystem adapted.

Now, almost every tool-calling framework relies on auto-generating that schema directly from your function signatures and docstrings.

# The old way: The LLM has no idea how to use this
def fetch_customer_data(customer_id, include_history=False):
    """Gets the customer."""
    return db.query(customer_id, include_history)

If you pass that function to an agent, it’s going to guess. It might pass a string for the ID. It might pass “yes” instead of a boolean for the history flag. It will fail, and you’ll spend three hours debugging your prompt.

Here is what that exact same function looks like when you actually want it to work in production.

from typing import Annotated
from pydantic import Field

@tool
def fetch_customer_data(
    customer_id: Annotated[int, Field(description="The unique 6-digit integer ID of the customer")],
    include_history: Annotated[bool, Field(description="Set to True to include the last 30 days of purchase history")] = False
) -> dict[str, str | int]:
    """
    Retrieves a customer's profile from the primary database.
    Only use this when you have a specific, known customer_id.
    """
    return db.query(customer_id, include_history)

Annotated is the MVP

API development - API Development Guide: Build, Test, Deploy like a Pro
API development – API Development Guide: Build, Test, Deploy like a Pro

The Annotated type is the bridge between your code and the LLM. The model reads the description. It sees the strict integer requirement. It formats its output perfectly.

I ran into a massive headache with this on a project last Tuesday. We were using standard dict typing for a nested JSON payload we expected the agent to generate. The agent kept hallucinating the keys. I finally ripped out the generic dict and replaced it with a strictly typed TypedDict with Annotated descriptions. The hallucination rate dropped from about 14% to exactly zero.

The performance tax nobody talks about

But doing all this introspection at runtime isn’t free. Benchmarking the startup overhead on my M3 Max MacBook, booting up an agent with 400 heavily annotated tools took 1.2 seconds of pure CPU time just for schema generation.

API development - API Development Services At Sat Sai Infocom
API development – API Development Services At Sat Sai Infocom

My current workflow fixes this by caching the generated schemas during the CI build step. Cut our cold start time down to 45 milliseconds.

Stop fighting it

Writing highly specific type annotations still feels a bit verbose. But the trade-off is undeniable. We traded a few extra keystrokes for the ability to bind complex, non-deterministic AI models directly to our backend logic without writing manual translation layers.

If you’re building anything that touches an LLM right now, your docstrings and type hints are the most important code you write. Just use the types. It’ll save you a weekend of debugging.

Leave a Reply

Your email address will not be published. Required fields are marked *