# OpenAI function calling: uma ferramenta read_url para o seu modelo

> OpenAI function calling com read_url: o schema JSON e o loop em Python que transforma links em Markdown limpo para o modelo. Sem KYC, pague com cripto.

- URL: https://webreaderapi.com/pt/docs/openai/
- Última atualização: 2026-10-22
- Editor: WebReaderAPI (https://webreaderapi.com)

Descreva uma função, `read_url`, e execute-a quando o modelo pedir. O modelo escolhe quais páginas ler; seu código chama a API e devolve o Markdown. O mesmo schema funciona com outros modelos que suportam tool calling.

**Resposta rápida — Como faço para um modelo OpenAI ler páginas web com function calling?**

Declare uma função `read_url` na lista `tools` e então rode um loop: quando o modelo retornar um tool call, chame o endpoint `/read` da **WebReaderAPI** com a URL e envie o Markdown de volta como uma mensagem `tool`. Cada página custa 1 crédito, 2 com navegador, a partir de US$ 0,07/1K páginas em outubro de 2026. Sem KYC; pague com cripto.

## Como é o schema do read_url?

Dois parâmetros bastam: o `url` a ler, obrigatório, e um `max_chars` opcional que permite ao modelo encurtar uma página longa. A descrição é a parte mais importante: ela diz ao modelo o que a ferramenta retorna e quando usá-la. Mantenha factual e curta.

Nomeamos a ferramenta `read_url` em vez de `web_fetch` para evitar conflito com ferramentas que alguns runtimes de agente já fornecem com esse nome.

*A definição da ferramenta*

```json
{
  "type": "function",
  "function": {
    "name": "read_url",
    "description": "Read a public web page and return its main content as Markdown, with its title and source URL. Use it when the user gives a link or when a page must be read to answer.",
    "parameters": {
      "type": "object",
      "properties": {
        "url": { "type": "string", "description": "The http or https address of the page." },
        "max_chars": { "type": "integer", "description": "Most characters to return, 1000 to 500000. Default 20000." }
      },
      "required": ["url"],
      "additionalProperties": false
    }
  }
}
```

## Como funciona o loop de tool calling?

Envie a conversa com a lista `tools`. Se a resposta contiver tool calls, execute cada uma, anexe o resultado como uma mensagem com role `tool` e o `tool_call_id` correspondente, e pergunte ao modelo de novo. Quando uma resposta voltar sem tool call, ela é a resposta final.

Limite o número de rodadas. Três ou quatro bastam para uma pergunta sobre um punhado de páginas, e o limite protege você de um modelo que fica pedindo mais.

*Python: o loop completo com o pacote openai*

```python
import json
import os

import requests
from openai import OpenAI

READ_URL = {
    "type": "function",
    "function": {
        "name": "read_url",
        "description": "Read a public web page and return its main content as Markdown, with its title and source URL.",
        "parameters": {
            "type": "object",
            "properties": {
                "url": {"type": "string", "description": "The http or https address of the page."},
                "max_chars": {"type": "integer", "description": "Most characters to return. Default 20000."},
            },
            "required": ["url"],
            "additionalProperties": False,
        },
    },
}


def read_url(url, max_chars=20000):
    resp = requests.get(
        "https://webreaderapi.com/api/v1/read",
        headers={"Authorization": "Bearer " + os.environ["WEBREADER_API_KEY"]},
        params={"url": url, "max_chars": max_chars},
        timeout=75,
    )
    body = resp.json()
    if resp.status_code != 200:
        return "Could not read the page: " + body["error"]["code"]
    return "# " + (body.get("title") or url) + "\nSource: " + body["final_url"] + "\n\n" + body["markdown"]


client = OpenAI()
model = os.environ.get("OPENAI_MODEL", "gpt-4.1-mini")
messages = [{"role": "user", "content": "What does https://example.com/blog/first-post announce? Answer in two sentences."}]

for _ in range(4):
    reply = client.chat.completions.create(model=model, messages=messages, tools=[READ_URL])
    msg = reply.choices[0].message
    if not msg.tool_calls:
        print(msg.content)
        break
    messages.append(msg)
    for call in msg.tool_calls:
        args = json.loads(call.function.arguments)
        messages.append({"role": "tool", "tool_call_id": call.id, "content": read_url(**args)})

```

## Como mantenho o uso de tokens e o custo baixos?

Três configurações importam. Mantenha `max_chars` modesto, em torno de 8.000 a 20.000 caracteres por página, porque cada caractere que você retorna é lido pelo modelo. Mantenha o `main_content=true` padrão, para que menus e rodapés nunca cheguem ao contexto. E limite o loop, como acima, para que uma pergunta não dispare dezenas de leituras.

Do nosso lado, o custo é por página, não por caractere: 1 crédito = 1 página lida sem navegador; 2 créditos quando a página precisa de um navegador para renderizar JavaScript. Uma requisição que falha, ou que o robots.txt de um site recusa, não é cobrada. Um assistente de suporte que lê 300 páginas por dia usa cerca de 9.000 créditos por mês, dentro de 10.000 páginas (US$ 3,75).

## O schema funciona com outros modelos?

Sim, com pequenas mudanças no wrapper. A maioria das APIs de tool calling aceita um JSON Schema para os parâmetros e um nome e uma descrição para a ferramenta, então `read_url` passa entre provedores como está. A [página de MCP](https://webreaderapi.com/pt/docs/mcp/) empacota a mesma função para clientes que falam o Model Context Protocol, e a [página de LangChain](https://webreaderapi.com/pt/docs/langchain/) a envolve para agentes LangChain e LlamaIndex.

## O que o modelo deve ver quando uma página é recusada?

Uma frase curta e clara, como o loop acima retorna: "Could not read the page: blocked_by_robots". Modelos lidam bem com isso e recorrem a outras fontes ou avisam o usuário. Respeitamos o robots.txt: nosso crawler se identifica como WebReaderAPI, segue as regras escritas para ele ou para todos os robôs e não lê uma página que o site proíbe. Páginas recusadas não são cobradas. Se um site recusa nossa requisição, devolvemos a resposta dele: nunca resolvemos CAPTCHAs, nunca entramos em contas e nunca tentamos contornar um bloqueio ou um paywall. Nenhuma dessas recusas usa um crédito.

## Function calling: perguntas frequentes

### Isso precisa da Responses API ou do Chat Completions?

O exemplo usa Chat Completions com o parâmetro `tools`. A mesma definição de função e o mesmo corpo `read_url` funcionam com outras interfaces da OpenAI que aceitam function tools; só o loop em volta delas muda.

### O modelo pode ler várias páginas em um turno?

Sim. Uma resposta pode conter vários tool calls; o loop executa cada um e retorna todos os resultados antes de perguntar ao modelo de novo. Mantenha `max_chars` menor quando o modelo lê muitas páginas de uma vez.

### A chave de API fica exposta ao modelo?

Não. A chave permanece no ambiente do seu código. O modelo só vê o nome da ferramenta, a descrição dela e o texto que sua função retorna.

### Quanto custa uma página recusada?

Nada. 1 crédito = 1 página lida sem navegador; 2 créditos quando a página precisa de um navegador para renderizar JavaScript. Uma requisição que falha, ou que o robots.txt de um site recusa, não é cobrada.

---

WebReaderAPI: O WebReaderAPI é uma API de leitor web para agentes de IA que converte qualquer página web pública em Markdown limpo, pago com cripto e sem envio de documentos. Os créditos pré-pagos a partir de US$ 0,07/1K páginas nunca expiram, e o robots.txt é respeitado. Sem KYC — sem documento, sem selfie, sem envio de arquivos. Pague com cripto.
