How I Used Sarvam AI’s Free API to Clone My Friend
Photo by Naveed Ahmed on Unsplash

This morning, I was staring at a Kubernetes deployment where the helm kept reconciling (brb, crying)

Meanwhile, my friend cum colleague was blowing up my phone with texts about how “HSR Layout” traffic is personally attacking him.

He has a very specific way of speaking. It’s 60% tech jargon, 30% complaining about infrastructure (both digital and physical), and 10% “Arre yaar.”

So, while the helm kept reconciling, I realized I didn’t need to talk to real him. I could build one (I hope bro doesn’t read this)

Now,

Why Sarvam AI?

Basically, the Local Advantage

I could have used GPT-4. It’s smart, polite, and sanitized.

But if I asked GPT-4 to pretend to be a stressed-out Bengaluru tech bro, it would sound like a cringe American actor trying to do an Indian accent.

It wouldn’t understand the nuance of a perfectly timed “adjust maadi.”

Enter Sarvam AI.

Sarvam is building foundational models specifically for the Indian context.

Their models are trained on Indic languages, Hinglish, and the cultural chaos that makes up our daily communication.

Plus, right now (Feb 2026), their API access for developers is free during the beta phase.

I didn’t need a 1-trillion parameter god-model. I needed a scrappy, localized model that understood why someone would be angry about the Silk Board junction at 11 PM ;)

The Recipe: Prompt Engineering a Persona

The secret isn’t the code, it’s the System Prompt You have to distill a human being into a set of instructions that a machine can follow.

I grabbed a coffee and listed Rahul’s core traits:

  1. The Vibe: Permanently slightly stressed backend engineer.
  2. The Language: Hinglish. Uses “bro,” “yaar,” and “scene kya hai” liberally.
  3. The Obsession: Thinks every problem can be solved with more microservices.
  4. The Enemy: Bengaluru traffic and product managers.

Lmao.

There were lot more, I can’t tell you all, of course.

KISS [Keep It Simple Stupid]

I didn’t want to overengineer this.

I got my baby Claude.

It fired up a simple Python server script.

from fastapi import FastAPI, HTTPException, Request
from fastapi.responses import HTMLResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from pydantic import BaseModel
from typing import List, Dict
import os
import logging
from fastapi.middleware.cors import CORSMiddleware
from sarvam_wrapper import AsyncSarvamRateLimited
from dotenv import load_dotenv
from persona import SYSTEM_PROMPT

# Setup logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)

load_dotenv()

app = FastAPI(title="Sarvam AI Chatbot")

# Add CORS middleware
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)

# Custom request logger middleware
@app.middleware("http")
async def log_requests(request: Request, call_next):
logger.info(f"Incoming: {request.method} {request.url}")
try:
response = await call_next(request)
logger.info(f"Completed: {request.method} {request.url} - Status: {response.status_code}")
return response
except Exception as e:
logger.error(f"Request failed: {request.method} {request.url} - Error: {e}", exc_info=True)
raise

# Mount static files and templates
os.makedirs("static", exist_ok=True)
os.makedirs("templates", exist_ok=True)
app.mount("/static", StaticFiles(directory="static"), name="static")
templates = Jinja2Templates(directory="templates")

# Initialize the rate-limited wrapper
sarvam_client = AsyncSarvamRateLimited(rpm_limit=60)

class ChatMessage(BaseModel):
role: str
content: str

class ChatRequest(BaseModel):
messages: List[ChatMessage]

@app.get("/", response_class=HTMLResponse)
async def get_index(request: Request):
return templates.TemplateResponse("index.html", {"request": request})

@app.post("/chat")
async def chat_endpoint(chat_request: ChatRequest):
try:
logger.info(f"Received chat request: {chat_request}")

# Use prompt from persona.py
system_prompt = {
"role": "system",
"content": SYSTEM_PROMPT
}

# Convert pydantic models to dictionaries and prepend system prompt
messages = [system_prompt] + [{"role": m.role, "content": m.content} for m in chat_request.messages]

# Call the rate-limited wrapper
logger.info("Calling Sarvam AI with persona...")
response = await sarvam_client.chat_completion(
messages=messages
)
logger.info(f"Sarvam AI response: {response}")

# Extract the content from the response
content = response.choices[0].message.content
logger.info(f"Sending response back: {content}")
return {"response": content}

except Exception as e:
logger.error(f"Error in chat endpoint: {e}", exc_info=True)
raise HTTPException(status_code=500, detail=str(e))

if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)

So, a Fast-Api server

and a Sarvam wrapper as below

import os
import time
import asyncio
from typing import List, Dict, Any, Optional
from sarvamai import SarvamAI
from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type
from sarvamai.core.api_error import ApiError
from dotenv import load_dotenv

load_dotenv()

class SarvamRateLimited:
def __init__(self, rpm_limit: int = 60):
api_key = os.getenv("SARVAM_API_KEY")
if not api_key:
raise ValueError("SARVAM_API_KEY not found in environment variables")

self.client = SarvamAI(api_subscription_key=api_key)
self.rpm_limit = rpm_limit
self.interval = 60.0 / rpm_limit
self.last_request_time = 0.0
self._lock = asyncio.Lock()

async def _wait_for_rate_limit(self):
async with self._lock:
current_time = time.time()
elapsed = current_time - self.last_request_time
if elapsed < self.interval:
wait_time = self.interval - elapsed
await asyncio.sleep(wait_time)
self.last_request_time = time.time()

@retry(
stop=stop_after_attempt(5),
wait=wait_exponential(multiplier=1, min=2, max=10),
retry=retry_if_exception_type(ApiError),
reraise=True
)

async def chat_completion(self, messages: List[Dict[str, str]], **kwargs) -> Any:
"""
Sends a chat completion request to Sarvam AI with rate limiting and retries.
"""

await self._wait_for_rate_limit()

try:

response = self.client.chat.completions(
messages=messages,
**kwargs
)
return response
except ApiError as e:
if e.status_code == 429:
print(f"Rate limit hit (429). Retrying...")
raise e

class AsyncSarvamRateLimited:
def __init__(self, rpm_limit: int = 60):
from sarvamai import AsyncSarvamAI
api_key = os.getenv("SARVAM_API_KEY")
if not api_key:
raise ValueError("SARVAM_API_KEY not found in environment variables")

self.client = AsyncSarvamAI(api_subscription_key=api_key)
self.rpm_limit = rpm_limit
self.interval = 60.0 / rpm_limit
self.last_request_time = 0.0
self._lock = asyncio.Lock()

async def _wait_for_rate_limit(self):
async with self._lock:
current_time = time.time()
elapsed = current_time - self.last_request_time
if elapsed < self.interval:
wait_time = self.interval - elapsed
await asyncio.sleep(wait_time)
self.last_request_time = time.time()

@retry(
stop=stop_after_attempt(5),
wait=wait_exponential(multiplier=1, min=2, max=10),
retry=retry_if_exception_type(ApiError),
reraise=True
)

async def chat_completion(self, messages: List[Dict[str, str]], **kwargs) -> Any:
await self._wait_for_rate_limit()
try:
response = await self.client.chat.completions(
messages=messages,
**kwargs
)
return response
except ApiError as e:
if e.status_code == 429:
print(f"Rate limit hit (429). Retrying...")
raise e

Now, I dropped the bot into our private Slack channel without warning.

It took about four minutes before everyone started responding with, “Wait, this is exactly him”

It worked perfectly.

This little experiment proved something important about the current AI landscape in 2026:

Specific beats generic.

We don’t always need the smartest model on Earth. Sometimes we need the model that understands the local context, the slang, and the vibe of the city we live in.

Sarvam AI proved that a smaller, domain-specific model trained on Indian context can outperform a massive US model when the task requires local flavor.

And the fact that I could build this in 45 minutes for zero rupees?

That’s the real magic.

Go grab a free key while they last, pick a friend who talks funny, and build your own clone.

It’s way more fun than debugging Kubernetes.

https://docs.sarvam.ai/api-reference-docs/getting-started/pricing#document-intelligence

In case we are meeting for the first time, come over here, it’ll be worth the roller coaster of articles that are gonna come up in the next few weeks.