当您使用多个搜索 API 时,每个 API 都有不同的定价等级、免费积分和速率限制。成本路由器会自动为每个查询选择最便宜的可用提供商,在花钱之前先用尽免费套餐。本教程跨 Scavio(250 免费/月,之后 0.005 美元)、Brave(每月免费 5 美元)和 Tavily(1K 免费/月)构建一个路由器。路由器跟踪使用情况、遵守速率限制并优雅地回退。
前置条件
- 已安装 Python 3.9+
- 至少两个搜索提供商的 API 密钥
- 请求已安装库
操作指南
步骤 1: 定义提供商配置
设置每个提供商的定价、免费套餐、速率限制和 API 调用格式。
Python
import os
providers = {
'scavio': {
'cost_per_query': 0.005,
'free_monthly': 250,
'rate_limit_per_sec': 10,
'api_key': os.environ.get('SCAVIO_API_KEY', ''),
'priority': 1, # lower = preferred
},
'brave': {
'cost_per_query': 0.005,
'free_monthly': 1000, # $5 free credit = 1K queries
'rate_limit_per_sec': 5,
'api_key': os.environ.get('BRAVE_API_KEY', ''),
'priority': 2,
},
'tavily': {
'cost_per_query': 0.03,
'free_monthly': 1000,
'rate_limit_per_sec': 5,
'api_key': os.environ.get('TAVILY_API_KEY', ''),
'priority': 3,
},
}
for name, p in providers.items():
status = 'configured' if p['api_key'] else 'no key'
print(f' {name}: ${p["cost_per_query"]}/query, {p["free_monthly"]} free/mo [{status}]')步骤 2: 构建具有使用情况跟踪的成本路由器
路由器跟踪每个提供商的每月使用情况,首先使用免费积分,然后路由到最便宜的付费提供商。
Python
import time, requests
from datetime import datetime
class CostRouter:
def __init__(self, providers: dict):
self.providers = providers
self.usage = {name: 0 for name in providers}
self.month = datetime.now().month
self.last_call = {name: 0.0 for name in providers}
def _reset_if_new_month(self):
current_month = datetime.now().month
if current_month != self.month:
self.usage = {name: 0 for name in self.providers}
self.month = current_month
def _get_cost(self, name: str) -> float:
p = self.providers[name]
if self.usage[name] < p['free_monthly']:
return 0.0
return p['cost_per_query']
def _can_call(self, name: str) -> bool:
p = self.providers[name]
if not p['api_key']:
return False
elapsed = time.time() - self.last_call[name]
return elapsed >= (1.0 / p['rate_limit_per_sec'])
def select_provider(self) -> str:
self._reset_if_new_month()
available = [(name, self._get_cost(name), self.providers[name]['priority'])
for name in self.providers if self._can_call(name)]
if not available:
return list(self.providers.keys())[0] # fallback to first
# Sort by cost, then priority
available.sort(key=lambda x: (x[1], x[2]))
return available[0][0]
def search(self, query: str) -> dict:
provider = self.select_provider()
cost = self._get_cost(provider)
results = self._call_provider(provider, query)
self.usage[provider] += 1
self.last_call[provider] = time.time()
return {'results': results, 'provider': provider,
'cost': cost, 'usage': dict(self.usage)}步骤 3: 实施特定于提供商的 API 调用
为每个提供商添加实际的 API 调用逻辑。路由器会自动分派到正确的路由器。
Python
def _call_provider(self, name: str, query: str) -> list:
p = self.providers[name]
try:
if name == 'scavio':
resp = requests.post('https://api.scavio.dev/api/v1/search',
headers={'x-api-key': p['api_key'], 'Content-Type': 'application/json'},
json={'query': query, 'country_code': 'us', 'num_results': 10},
timeout=10)
return [{'title': r['title'], 'link': r['link'], 'snippet': r.get('snippet', '')}
for r in resp.json().get('organic_results', [])]
elif name == 'brave':
resp = requests.get('https://api.search.brave.com/res/v1/web/search',
headers={'X-Subscription-Token': p['api_key']},
params={'q': query, 'count': 10}, timeout=10)
return [{'title': r['title'], 'link': r['url'], 'snippet': r.get('description', '')}
for r in resp.json().get('web', {}).get('results', [])]
elif name == 'tavily':
resp = requests.post('https://api.tavily.com/search',
json={'api_key': p['api_key'], 'query': query, 'max_results': 10},
timeout=10)
return [{'title': r.get('title', ''), 'link': r['url'], 'snippet': r.get('content', '')}
for r in resp.json().get('results', [])]
except Exception as e:
print(f'{name} failed: {e}')
return []
return []
# Initialize and test
router = CostRouter(providers)
result = router.search('best search API 2026')
print(f'Provider: {result["provider"]}, Cost: ${result["cost"]:.3f}')
print(f'Results: {len(result["results"])}')
print(f'Usage: {result["usage"]}')Python 示例
Python
import requests, os, time
SCAVIO_KEY = os.environ.get('SCAVIO_API_KEY', '')
BRAVE_KEY = os.environ.get('BRAVE_API_KEY', '')
usage = {'scavio': 0, 'brave': 0}
def route_search(query):
# Use free credits first
if usage['scavio'] < 250 and SCAVIO_KEY:
resp = requests.post('https://api.scavio.dev/api/v1/search',
headers={'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json'},
json={'query': query, 'country_code': 'us', 'num_results': 10})
usage['scavio'] += 1
return {'results': resp.json().get('organic_results', []), 'provider': 'scavio', 'cost': 0}
if usage['brave'] < 1000 and BRAVE_KEY:
resp = requests.get('https://api.search.brave.com/res/v1/web/search',
headers={'X-Subscription-Token': BRAVE_KEY}, params={'q': query, 'count': 10})
usage['brave'] += 1
return {'results': resp.json().get('web', {}).get('results', []), 'provider': 'brave', 'cost': 0}
# Paid fallback
resp = requests.post('https://api.scavio.dev/api/v1/search',
headers={'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json'},
json={'query': query, 'country_code': 'us', 'num_results': 10})
usage['scavio'] += 1
return {'results': resp.json().get('organic_results', []), 'provider': 'scavio', 'cost': 0.005}
result = route_search('best search api 2026')
print(f'{result["provider"]}: {len(result["results"])} results, ${result["cost"]}')JavaScript 示例
JavaScript
const SCAVIO_KEY = process.env.SCAVIO_API_KEY;
const BRAVE_KEY = process.env.BRAVE_API_KEY;
const usage = { scavio: 0, brave: 0 };
async function routeSearch(query) {
if (usage.scavio < 250 && SCAVIO_KEY) {
const resp = await fetch('https://api.scavio.dev/api/v1/search', {
method: 'POST',
headers: { 'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({ query, country_code: 'us', num_results: 10 })
});
usage.scavio++;
return { results: (await resp.json()).organic_results || [], provider: 'scavio', cost: 0 };
}
if (usage.brave < 1000 && BRAVE_KEY) {
const resp = await fetch(`https://api.search.brave.com/res/v1/web/search?q=${encodeURIComponent(query)}&count=10`, {
headers: { 'X-Subscription-Token': BRAVE_KEY }
});
usage.brave++;
return { results: (await resp.json()).web?.results || [], provider: 'brave', cost: 0 };
}
const resp = await fetch('https://api.scavio.dev/api/v1/search', {
method: 'POST',
headers: { 'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({ query, country_code: 'us', num_results: 10 })
});
usage.scavio++;
return { results: (await resp.json()).organic_results || [], provider: 'scavio', cost: 0.005 };
}
routeSearch('best search api').then(r => console.log(`${r.provider}: ${r.results.length} results`));预期输出
JSON
scavio: $0.005/query, 250 free/mo [configured]
brave: $0.005/query, 1000 free/mo [configured]
tavily: $0.030/query, 1000 free/mo [no key]
Provider: scavio, Cost: $0.000
Results: 10
Usage: {'scavio': 1, 'brave': 0, 'tavily': 0}
First 250 queries: $0.00 (Scavio free)
Next 1000 queries: $0.00 (Brave free)
Remaining: $0.005/query (cheapest paid)