LangChain agents can burn through search API credits fast when they loop or retry aggressively. This tutorial builds a LangChain-compatible search tool with built-in rate limiting, credit tracking, and budget controls. It prevents runaway costs while keeping search reliable at $0.005/query.
Prerequisites
- Python 3.8+
- langchain library
- A Scavio API key from scavio.dev
- Basic LangChain agent setup
Walkthrough
Step 1: Build the rate-limited search tool
Create a LangChain tool that enforces rate limits and tracks credit usage.
import os, requests, time, json, threading
from datetime import datetime
API_KEY = os.environ['SCAVIO_API_KEY']
SH = {'x-api-key': API_KEY, 'Content-Type': 'application/json'}
class RateLimitedSearch:
def __init__(self, max_per_minute=30, daily_budget=500):
self.max_per_minute = max_per_minute
self.daily_budget = daily_budget
self.calls = []
self.daily_calls = 0
self.daily_reset = datetime.now().strftime('%Y-%m-%d')
self.lock = threading.Lock()
def _check_limits(self):
now = time.time()
# Reset daily counter
today = datetime.now().strftime('%Y-%m-%d')
if today != self.daily_reset:
self.daily_calls = 0
self.daily_reset = today
# Check daily budget
if self.daily_calls >= self.daily_budget:
return False, f'Daily budget ({self.daily_budget}) reached. Cost: ${self.daily_calls * 0.005:.2f}'
# Check per-minute rate
self.calls = [t for t in self.calls if now - t < 60]
if len(self.calls) >= self.max_per_minute:
wait = 60 - (now - self.calls[0])
return False, f'Rate limit: {self.max_per_minute}/min. Wait {wait:.0f}s.'
return True, 'OK'
def search(self, query, num_results=5):
with self.lock:
allowed, reason = self._check_limits()
if not allowed:
return {'error': reason, 'results': []}
self.calls.append(time.time())
self.daily_calls += 1
resp = requests.post('https://api.scavio.dev/api/v1/search',
headers=SH, json={'query': query, 'country_code': 'us'}, timeout=10)
resp.raise_for_status()
data = resp.json()
results = [{'title': r.get('title', ''), 'url': r.get('link', ''),
'snippet': r.get('snippet', '')} for r in data.get('organic_results', [])[:num_results]]
return {'results': results, 'count': len(results),
'credits_used': self.daily_calls, 'budget_remaining': self.daily_budget - self.daily_calls}
rl_search = RateLimitedSearch(max_per_minute=30, daily_budget=500)
result = rl_search.search('langchain search tool tutorial')
print(f'Results: {result.get("count", 0)}')
print(f'Credits used today: {result.get("credits_used", 0)}')
print(f'Budget remaining: {result.get("budget_remaining", 0)}')Step 2: Create the LangChain tool wrapper
Wrap the rate-limited search as a proper LangChain tool.
# LangChain tool definition
# from langchain.tools import Tool
def langchain_search(query: str) -> str:
"""Search the web with rate limiting and budget controls.
Use for finding documentation, current information, or verifying facts."""
result = rl_search.search(query)
if 'error' in result:
return f'Search blocked: {result["error"]}'
output = []
for r in result['results']:
output.append(f'Title: {r["title"]}')
output.append(f'URL: {r["url"]}')
output.append(f'Snippet: {r["snippet"]}')
output.append('')
output.append(f'[Credits: {result["credits_used"]}/{rl_search.daily_budget} today]')
return '\n'.join(output)
# Register with LangChain:
# search_tool = Tool(
# name='web_search',
# func=langchain_search,
# description='Search the web. Rate limited to prevent runaway costs.'
# )
# Test
print(langchain_search('how to build langchain agent with tools'))
print(f'\nRate: {rl_search.max_per_minute}/min, Budget: {rl_search.daily_budget}/day')
print(f'Cost cap: ${rl_search.daily_budget * 0.005:.2f}/day')Step 3: Add monitoring and cost alerts
Track usage patterns and alert when costs approach the budget limit.
def usage_report():
print(f'\n=== Search Usage Report ===')
print(f' Date: {datetime.now().strftime("%Y-%m-%d %H:%M")}')
print(f' Calls today: {rl_search.daily_calls}')
print(f' Budget: {rl_search.daily_calls}/{rl_search.daily_budget}')
print(f' Cost today: ${rl_search.daily_calls * 0.005:.3f}')
print(f' Budget remaining: ${(rl_search.daily_budget - rl_search.daily_calls) * 0.005:.3f}')
# Usage rate
pct = rl_search.daily_calls / rl_search.daily_budget * 100
print(f' Usage: {pct:.0f}%')
if pct > 80:
print(f' WARNING: Over 80% of daily budget used!')
elif pct > 50:
print(f' NOTE: Over 50% of daily budget used.')
# Projected monthly cost
monthly = rl_search.daily_calls * 0.005 * 30
print(f'\n Projected monthly cost: ${monthly:.2f}')
print(f' Max monthly cost: ${rl_search.daily_budget * 0.005 * 30:.2f}')
# Simulate multiple searches
for i in range(5):
rl_search.search(f'test query {i}')
usage_report()
print(f'\n Rate limit: {rl_search.max_per_minute} calls/minute')
print(f' Daily budget: {rl_search.daily_budget} calls (${ rl_search.daily_budget * 0.005:.2f})')
print(f' Per call: $0.005')Python Example
import os, requests, time
SH = {'x-api-key': os.environ['SCAVIO_API_KEY'], 'Content-Type': 'application/json'}
last_call = 0
def rate_limited_search(query, min_interval=2):
global last_call
wait = min_interval - (time.time() - last_call)
if wait > 0: time.sleep(wait)
data = requests.post('https://api.scavio.dev/api/v1/search',
headers=SH, json={'query': query, 'country_code': 'us'}, timeout=10).json()
last_call = time.time()
return data.get('organic_results', [])[:5]
results = rate_limited_search('langchain tools')
print(f'Results: {len(results)}')JavaScript Example
const SH = { 'x-api-key': process.env.SCAVIO_API_KEY, 'Content-Type': 'application/json' };
let lastCall = 0;
async function rateLimitedSearch(query, minInterval = 2000) {
const wait = minInterval - (Date.now() - lastCall);
if (wait > 0) await new Promise(r => setTimeout(r, wait));
const data = await fetch('https://api.scavio.dev/api/v1/search', {
method: 'POST', headers: SH,
body: JSON.stringify({ query, country_code: 'us' })
}).then(r => r.json());
lastCall = Date.now();
return (data.organic_results || []).slice(0, 5);
}
const results = await rateLimitedSearch('langchain tools');
console.log(`Results: ${results.length}`);Expected Output
Results: 5
Credits used today: 1
Budget remaining: 499
Title: LangChain Search Tool Tutorial - Official Docs
URL: https://python.langchain.com/docs/...
Snippet: Learn how to create custom search tools for LangChain agents...
[Credits: 1/500 today]
Rate: 30/min, Budget: 500/day
Cost cap: $2.50/day
=== Search Usage Report ===
Calls today: 6
Budget: 6/500
Cost today: $0.030
Usage: 1%
Projected monthly cost: $0.90
Max monthly cost: $75.00