Réponse rapide
Comment appeler une API de recherche web pour agents IA depuis Python ?
Envoyez la requête en POST à https://llmwebsearch.com/api/v1/search avec requests ou httpx et une clé Bearer. LLMWebSearch renvoie des résultats classés avec les 3 premières pages en Markdown, que vous pouvez numéroter et transmettre à n'importe quel LLM pour répondre avec citations. Une recherche coûte 1 crédit, à partir de 1,20 $US/1K requêtes en octobre 2026. Python 3.9+ et aucun SDK. Sans KYC ; payez en crypto.
Ce qu'il vous faut
| Python | 3.9 ou plus récent |
|---|---|
| Packages | requests pour le script simple, httpx pour la version asynchrone |
| Clé | LLMWEBSEARCH_API_KEY dans votre environnement |
| Pour la boucle de réponse | Tout endpoint de chat compatible OpenAI : LLM_BASE_URL, LLM_API_KEY, LLM_MODEL |
| Facturation | Un crédit paie une recherche : jusqu'à 20 résultats classés, avec les pages des 3 premiers lues et renvoyées en Markdown. Lire 4 à 10 pages coûte 2 crédits. Un appel qui se termine sur une erreur ne coûte rien |
Comment lancer une seule recherche avec requests ?
Construisez un corps JSON avec q et les options voulues, envoyez-le en POST, puis parcourez results. Vérifiez content_status avant de toucher à content : seuls les résultats marqués ok contiennent le texte de la page. Le script ci-dessous enregistre chaque page lue dans un fichier Markdown nommé d'après sa position, avec l'URL source sur la première ligne.
import os
import pathlib
import requests
resp = requests.post(
"https://llmwebsearch.com/api/v1/search",
headers={"Authorization": "Bearer " + os.environ["LLMWEBSEARCH_API_KEY"]},
json={"q": "how to size a postgres connection pool", "max_results": 5, "read": 3},
timeout=60,
)
resp.raise_for_status()
data = resp.json()
out = pathlib.Path("sources")
out.mkdir(exist_ok=True)
for r in data["results"]:
if r["content_status"] != "ok":
print("skip", r["position"], r["content_status"], r["url"])
continue
path = out / f"{r['position']:02d}.md"
path.write_text("Source: " + r["url"] + "\n\n" + r["content"], encoding="utf-8")
print("saved", path, r["word_count"], "words")
print("credits used:", data["credits_used"])
Comment lancer plusieurs recherches à la fois ?
Utilisez httpx.AsyncClient et un asyncio.Semaphore. Le sémaphore limite le nombre d'appels en cours, ce qui vous garde sous la limite par minute de votre compte et répartit la charge. Quatre à huit appels simultanés conviennent à la plupart des agents de recherche. Chaque compte a une limite d'appels par minute, affichée dans le dashboard ; demandez au support et elle peut être augmentée.
L'exemple envoie une recherche par sous-question, une étape typique d'un agent de recherche, et collecte les URL des pages qui ont été lues. Chaque appel est indépendant : un échec n'arrête pas les autres.
import asyncio
import os
import httpx
API = "https://llmwebsearch.com/api/v1/search"
HEADERS = {"Authorization": "Bearer " + os.environ["LLMWEBSEARCH_API_KEY"]}
QUESTIONS = [
"what is connection pooling in postgres",
"recommended pool size per cpu core",
"pgbouncer transaction mode limitations",
"connection pool exhaustion symptoms",
]
async def search(client, sem, q):
async with sem:
resp = await client.post(API, json={"q": q, "read": 2, "max_chars": 4000})
if resp.status_code != 200:
return q, resp.json().get("error", {}).get("code"), []
data = resp.json()
read = [r["url"] for r in data["results"] if r["content_status"] == "ok"]
return q, "ok", read
async def main():
sem = asyncio.Semaphore(4)
async with httpx.AsyncClient(headers=HEADERS, timeout=60) as client:
for q, status, urls in await asyncio.gather(*(search(client, sem, q) for q in QUESTIONS)):
print(status, q, urls)
asyncio.run(main())
Comment obtenir une réponse avec citations de n'importe quel LLM ?
Cherchez d'abord, puis donnez au modèle des sources numérotées et demandez-lui de les citer. L'API renvoie des sources, pas une réponse rédigée : c'est votre propre modèle qui lit les pages, rédige la réponse et cite les URLs, vous choisissez donc le modèle et le style de citation. La boucle ci-dessous fonctionne avec tout endpoint de chat qui parle le format chat/completions compatible OpenAI ; indiquez son adresse, sa clé et son modèle dans trois variables d'environnement.
Gardez le contexte sous contrôle. Le script tronque chaque source pour rester dans un budget d'environ 6 000 tokens, en comptant à peu près quatre caractères par token. Un résultat sans contenu est transmis sous forme de snippet, signalé comme tel, pour que le modèle ne prétende pas avoir lu la page.
import os
import requests
SEARCH = "https://llmwebsearch.com/api/v1/search"
KEY = os.environ["LLMWEBSEARCH_API_KEY"]
BUDGET_CHARS = 6000 * 4 # about 6,000 tokens of sources
def sources_for(question):
resp = requests.post(SEARCH, headers={"Authorization": "Bearer " + KEY},
json={"q": question, "max_results": 5, "read": 3}, timeout=60)
resp.raise_for_status()
results = resp.json()["results"]
share = BUDGET_CHARS // max(len(results), 1)
blocks = []
for i, r in enumerate(results, start=1):
if r["content_status"] == "ok":
body = r["content"][:share]
else:
body = "(snippet only) " + r["snippet"]
blocks.append(f"[{i}] {r['title']} - {r['url']}\n{body}")
return "\n\n".join(blocks)
def answer(question):
prompt = ("Answer the question using only the sources below. "
"Cite them as [1], [2] and so on. Say so if the sources do not answer it.\n\n"
+ sources_for(question) + "\n\nQuestion: " + question)
resp = requests.post(
os.environ["LLM_BASE_URL"].rstrip("/") + "/chat/completions",
headers={"Authorization": "Bearer " + os.environ["LLM_API_KEY"]},
json={"model": os.environ["LLM_MODEL"], "messages": [{"role": "user", "content": prompt}]},
timeout=120,
)
resp.raise_for_status()
return resp.json()["choices"][0]["message"]["content"]
print(answer("How should I size a Postgres connection pool?"))
Combien de texte dois-je envoyer au modèle ?
Le moins possible pour répondre à la question. Trois pages tronquées à 2 000 tokens chacune donnent généralement de meilleurs résultats que dix pages complètes : le modèle se concentre mieux et chaque réponse coûte moins cher. Baissez max_chars dans la recherche elle-même quand vous savez que vous allez tronquer, pour que moins de données circulent.
Tronquer depuis le début d'une page fonctionne pour la plupart des articles, car l'idée principale vient généralement en premier. Pour les longues pages de référence, découpez le contenu par titres et gardez les sections dont le titre correspond à la question.
Que doit faire le code quand une page manque ?
Continuez avec les autres sources. Une page marquée blocked_by_robots, blocked_by_site_policy ou target_refused ne sera pas lue lors d'une nouvelle tentative : ne bouclez donc pas dessus. Une page fetch_timeout ou fetch_failed peut fonctionner plus tard via https://llmwebsearch.com/api/v1/extract.
Les erreurs HTTP sont différentes : 429, 502 et 504 méritent une nouvelle tentative avec une pause croissante. La page des erreurs propose un helper de retry prêt à l'emploi.
Python : questions fréquentes
Existe-t-il un package pip pour LLMWebSearch ?
Non. L'API est du HTTPS et du JSON classiques, donc requests ou httpx suffit. Les scripts de cette page constituent l'intégration : copiez-les dans votre projet et adaptez-les.
Puis-je utiliser ces scripts avec un modèle local ?
Oui. Tout serveur qui expose une route chat/completions compatible OpenAI fonctionne avec la boucle de citation : définissez LLM_BASE_URL sur son adresse et LLM_MODEL sur le nom de modèle qu'il attend.
Comment compter les crédits depuis Python ?
Additionnez credits_used de chaque réponse, ou appelez GET https://llmwebsearch.com/api/v1/credits avant et après une tâche ; cet appel est gratuit.