Lorsque vous utilisez plusieurs API de recherche, chacune a différents niveaux de tarification, crédits gratuits et limites de taux. Un routeur de coût sélectionne automatiquement le fournisseur le moins cher disponible pour chaque requête, en épuisant d'abord les niveaux gratuits avant de dépenser de l'argent. Ce tutoriel construit un routeur entre Scavio (250 gratuits/mois, 0,005$ ensuite), Brave (5$ de crédit mensuel gratuit) et Tavily (1 000 gratuits/mois). Le routeur suit l'utilisation, respecte les limites de taux et bascule gracieusement.
Prérequis
- Python 3.9+ installé
- Clés API pour au moins deux fournisseurs de recherche
- bibliothèque requests installée
Parcours
Étape 1: Définir les configurations des fournisseurs
Configurer chaque fournisseur avec ses tarifs, son niveau gratuit, ses limites de taux et le format d'appel API.
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}]')Étape 2: Construire le routeur de coût avec suivi d'utilisation
Le routeur suit l'utilisation mensuelle par fournisseur, utilise d'abord les crédits gratuits, puis achemine vers le fournisseur payant le moins cher.
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)}Étape 3: Implémenter les appels API propres à chaque fournisseur
Ajouter la logique d'appel API réelle pour chaque fournisseur. Le routeur distribue automatiquement vers le bon.
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"]}')Exemple 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"]}')Exemple 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`));Sortie attendue
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)