How to Integrate FastAPI for retail banking with LangChain for AI agents

By Cyprian AaronsUpdated 2026-04-21
fastapi-for-retail-bankinglangchainai-agents

Combining FastAPI for retail banking with LangChain gives you a clean way to expose banking capabilities as APIs while letting AI agents orchestrate them safely. The practical win is simple: an agent can answer customer questions, check balances, initiate transfers, or route disputes by calling controlled backend endpoints instead of improvising responses.

Prerequisites

  • Python 3.10+
  • A FastAPI service for your retail banking backend
  • A LangChain installation with tool and model support
  • An API key for your LLM provider if you’re using a hosted model
  • uvicorn for running the FastAPI app
  • httpx or requests for HTTP calls from LangChain tools
  • Basic auth in place for banking endpoints
  • A sandbox or test banking environment, not production accounts

Install the core packages:

pip install fastapi uvicorn httpx langchain langchain-openai pydantic

Integration Steps

1) Build the banking API in FastAPI

Start by exposing the banking operations you want the agent to use. Keep the contract narrow: balance lookup, transaction history, and transfer initiation are enough for most first-pass agent flows.

from fastapi import FastAPI, Header, HTTPException
from pydantic import BaseModel, Field

app = FastAPI(title="Retail Banking API")

class TransferRequest(BaseModel):
    from_account: str = Field(..., examples=["ACC123"])
    to_account: str = Field(..., examples=["ACC456"])
    amount: float = Field(..., gt=0)

@app.get("/accounts/{account_id}/balance")
def get_balance(account_id: str, x_api_key: str = Header(default="")):
    if x_api_key != "test-key":
        raise HTTPException(status_code=401, detail="Unauthorized")
    return {"account_id": account_id, "currency": "USD", "balance": 2450.75}

@app.post("/transfers")
def create_transfer(payload: TransferRequest, x_api_key: str = Header(default="")):
    if x_api_key != "test-key":
        raise HTTPException(status_code=401, detail="Unauthorized")
    return {
        "status": "pending",
        "transfer_id": "TRX-10001",
        "from_account": payload.from_account,
        "to_account": payload.to_account,
        "amount": payload.amount,
    }

Run it:

uvicorn main:app --reload --port 8000

2) Wrap the FastAPI endpoints as LangChain tools

LangChain agents need tools they can call. The simplest production-friendly pattern is to wrap your banking API with functions that make authenticated HTTP requests.

import os
import httpx
from langchain_core.tools import tool

BASE_URL = os.getenv("BANKING_API_URL", "http://localhost:8000")
API_KEY = os.getenv("BANKING_API_KEY", "test-key")

@tool
def get_account_balance(account_id: str) -> str:
    """Fetch the current balance for a retail banking account."""
    headers = {"X-API-Key": API_KEY}
    response = httpx.get(f"{BASE_URL}/accounts/{account_id}/balance", headers=headers, timeout=10)
    response.raise_for_status()
    return response.json().__str__()

@tool
def initiate_transfer(from_account: str, to_account: str, amount: float) -> str:
    """Initiate a transfer between two retail banking accounts."""
    headers = {"X-API-Key": API_KEY}
    payload = {
        "from_account": from_account,
        "to_account": to_account,
        "amount": amount,
    }
    response = httpx.post(f"{BASE_URL}/transfers", json=payload, headers=headers, timeout=10)
    response.raise_for_status()
    return response.json().__str__()

This keeps the agent away from direct database access and forces all actions through your API layer.

3) Create the LangChain agent and register the tools

Now wire those tools into an agent. Use a chat model that supports tool calling so the agent can choose when to hit your banking APIs.

from langchain_openai import ChatOpenAI
from langchain.agents import create_tool_calling_agent, AgentExecutor
from langchain_core.prompts import ChatPromptTemplate

llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)

prompt = ChatPromptTemplate.from_messages([
    ("system", "You are a retail banking assistant. Use tools for any account data or money movement."),
    ("human", "{input}"),
])

tools = [get_account_balance, initiate_transfer]

agent = create_tool_calling_agent(llm=llm, tools=tools, prompt=prompt)
executor = AgentExecutor(agent=agent, tools=tools, verbose=True)

Use explicit system instructions here. In banking flows, “helpful” without constraints becomes a liability.

4) Run an end-to-end agent request

At this point the agent can answer user questions by calling your FastAPI-backed tools.

result = executor.invoke({
    "input": "Check my balance for account ACC123 and move 100 USD to ACC456 if balance is above 500."
})

print(result["output"])

If you want stronger guardrails before transfers execute, add a policy step in front of initiate_transfer. For example:

  • require human approval above a threshold
  • block transfers to untrusted beneficiaries
  • validate KYC/AML flags before tool execution

5) Add structured outputs for safer downstream handling

For production systems, don’t let the agent return free-form strings when you need machine-readable results. Wrap final responses in a schema so your frontend or orchestration layer can handle them reliably.

from pydantic import BaseModel

class BankingDecision(BaseModel):
    action: str
    status: str
    details: str

structured_prompt = ChatPromptTemplate.from_messages([
    ("system", "Return only structured decisions about retail banking actions."),
    ("human", "{input}"),
])

structured_llm = llm.with_structured_output(BankingDecision)
decision = structured_llm.invoke(
    "User wants balance check on ACC123 and transfer 100 USD to ACC456 if eligible."
)

print(decision)

This is useful when you need deterministic routing into audit logs, case management systems, or approval queues.

Testing the Integration

Use a simple script to verify both the API and agent path work together.

import httpx

# Direct API test
resp = httpx.get(
    "http://localhost:8000/accounts/ACC123/balance",
    headers={"X-API-Key": "test-key"},
)
print(resp.json())

# Agent test assumes executor is already initialized
result = executor.invoke({"input": "What is the balance of ACC123?"})
print(result["output"])

Expected output:

{'account_id': 'ACC123', 'currency': 'USD', 'balance': 2450.75}
{'account_id': 'ACC123', 'currency': 'USD', 'balance': 2450.75}

If the direct API call works but the agent doesn’t, check these first:

  • model supports tool calling
  • tool names and docstrings are clear
  • auth headers are being passed correctly
  • endpoint timeouts are not too aggressive

Real-World Use Cases

  • Customer servicing assistant

    • Balance checks
    • Recent transaction summaries
    • Card status inquiries routed through approved endpoints
  • Payments operations copilot

    • Draft transfer requests from natural language
    • Validate beneficiary details before submission
    • Escalate suspicious transfers for review
  • Collections and support workflows

    • Surface overdue account data from FastAPI services
    • Generate customer-specific repayment options with LangChain logic
    • Create follow-up tasks in CRM systems after policy checks

Keep learning

By Cyprian Aarons, AI Consultant at Topiax.

Want the complete 8-step roadmap?

Grab the free AI Agent Starter Kit — architecture templates, compliance checklists, and a 7-email deep-dive course.

Get the Starter Kit

Related Guides