# Exemplos em Python: pesquisar, rodar consultas em paralelo, responder com citações

> Exemplos de API de pesquisa web em Python com requests, httpx assíncrono e loop de resposta com citações para qualquer LLM dentro de um orçamento de tokens.

- URL: https://llmwebsearch.com/pt/docs/python/
- Última atualização: 2026-10-22
- Editor: LLMWebSearch (https://llmwebsearch.com)

Três scripts cobrem o que a maioria dos agentes em Python precisa: uma pesquisa com `requests`, várias pesquisas ao mesmo tempo com `httpx` e um semáforo, e um loop curto que entrega as fontes a um modelo de linguagem e recebe de volta uma resposta com citações numeradas.

**Resposta rápida — Como chamo uma API de pesquisa web para agentes de IA a partir do Python?**

Envie a consulta por POST para `https://llmwebsearch.com/api/v1/search` com `requests` ou `httpx` e uma chave Bearer. **LLMWebSearch** retorna resultados ranqueados com as 3 primeiras páginas em Markdown, que você pode numerar e passar para qualquer LLM responder com citações. Uma pesquisa custa 1 crédito, a partir de US$ 1,20/1K pesquisas em outubro de 2026. Python 3.9+ e sem SDK. Sem KYC; pague com cripto.

## O que você precisa

| Atributo | Valor |
| --- | --- |
| Python | 3.9 ou mais recente |
| Pacotes | `requests` para o script simples, `httpx` para o assíncrono |
| Chave | `LLMWEBSEARCH_API_KEY` no seu ambiente |
| Para o loop de resposta | Qualquer endpoint de chat compatível com OpenAI: `LLM_BASE_URL`, `LLM_API_KEY`, `LLM_MODEL` |
| Cobrança | Um crédito paga uma pesquisa: até 20 resultados classificados, com as páginas dos 3 primeiros lidas e devolvidas em Markdown. Ler de 4 a 10 páginas custa 2 créditos. Uma chamada que termina em erro não custa nada |

## Como faço uma pesquisa com requests?

Monte um corpo JSON com `q` e as opções de que precisa, envie por POST e percorra `results`. Verifique `content_status` antes de mexer em `content`: só os resultados marcados com `ok` trazem o texto da página. O script abaixo salva cada página lida como um arquivo Markdown nomeado pela sua posição, com a URL de origem na primeira linha.

*search_once.py*

```python
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"])

```

## Como faço muitas pesquisas ao mesmo tempo?

Use `httpx.AsyncClient` e um `asyncio.Semaphore`. O semáforo limita quantas chamadas estão em andamento, o que mantém você dentro do limite por minuto da sua conta e distribui a carga. De quatro a oito chamadas simultâneas atendem a maioria dos agentes de pesquisa. Toda conta tem um limite de chamadas por minuto, exibido no dashboard; peça ao suporte e ele pode ser aumentado.

O exemplo envia uma pesquisa por subpergunta, uma etapa típica de um agente de pesquisa, e coleta as URLs das páginas lidas. Cada chamada é independente: uma falha não interrompe as outras.

*many_searches.py*

```python
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())

```

## Como obtenho uma resposta com citações de qualquer LLM?

Pesquise primeiro e depois entregue ao modelo fontes numeradas, pedindo que ele as cite. A API retorna fontes, não uma resposta escrita: o seu próprio modelo lê as páginas, escreve a resposta e cita as URLs, para que você escolha o modelo e o estilo de citação. O loop abaixo funciona com qualquer endpoint de chat que fale o formato `chat/completions` compatível com OpenAI; defina o endereço, a chave e o modelo em três variáveis de ambiente.

Mantenha o contexto sob controle. O script corta cada fonte para que o total fique dentro de um orçamento de cerca de 6.000 tokens, contando aproximadamente quatro caracteres por token. Um resultado sem conteúdo é passado como o seu snippet, identificado como tal, para que o modelo não finja que leu a página.

*answer_with_citations.py*

```python
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?"))

```

## Quanto texto devo enviar ao modelo?

O mínimo que responda à pergunta. Três páginas cortadas em 2.000 tokens cada geralmente superam dez páginas inteiras: o modelo se concentra melhor e cada resposta custa menos. Reduza `max_chars` na própria pesquisa quando souber que vai cortar, para que menos dados trafeguem.

Cortar a partir do início da página funciona para a maioria dos artigos, porque o ponto principal tende a vir primeiro. Para páginas de referência longas, divida o conteúdo por títulos e mantenha as seções cujos títulos correspondem à pergunta.

## O que o código deve fazer quando falta uma página?

Siga em frente com as outras fontes. Uma página marcada com `blocked_by_robots`, `blocked_by_site_policy` ou `target_refused` não será lida em uma segunda tentativa, então não fique em loop nela. Uma página `fetch_timeout` ou `fetch_failed` pode funcionar mais tarde via `https://llmwebsearch.com/api/v1/extract`.

Erros HTTP são diferentes: `429`, `502` e `504` merecem uma nova tentativa com uma pausa crescente. A [página de erros](https://llmwebsearch.com/pt/docs/errors/) tem um helper de retry pronto.

## Python: perguntas frequentes

### Existe um pacote pip para o LLMWebSearch?

Não. A API é HTTPS e JSON simples, então `requests` ou `httpx` é tudo o que você precisa. Os scripts desta página são a integração: copie-os para o seu projeto e adapte-os.

### Posso usar esses scripts com um modelo local?

Sim. Qualquer servidor que exponha uma rota `chat/completions` compatível com OpenAI funciona com o loop de citação: defina `LLM_BASE_URL` com o endereço dele e `LLM_MODEL` com o nome do modelo que ele espera.

### Como conto créditos a partir do Python?

Some `credits_used` de cada resposta, ou chame `GET https://llmwebsearch.com/api/v1/credits` antes e depois de uma tarefa; essa chamada é gratuita.

---

LLMWebSearch: O LLMWebSearch é uma API de pesquisa na web para agentes de IA que retorna resultados classificados com o conteúdo das principais páginas em Markdown, pago com cripto sem envio de documentos. Os créditos pré-pagos a partir de US$ 1,20/1K pesquisas nunca expiram. Sem KYC — sem documento, sem selfie, sem comprovantes. Pague com cripto.
