# OpenAI function calling: een read_url-tool voor je model

> OpenAI function calling met read_url-tool: JSON-schema en Python-loop die links omzet in schone Markdown voor het model. Zonder KYC, betalen met crypto.

- URL: https://webreaderapi.com/nl/docs/openai/
- Laatst bijgewerkt: 2026-10-22
- Uitgever: WebReaderAPI (https://webreaderapi.com)

Beschrijf één functie, `read_url`, en voer die uit wanneer het model erom vraagt. Het model kiest welke pagina's worden gelezen; je code roept de API aan en geeft de Markdown terug. Hetzelfde schema werkt met andere modellen die tool calling ondersteunen.

**Kort antwoord — Hoe laat ik een OpenAI-model webpagina's lezen met function calling?**

Declareer een `read_url`-functie in de `tools`-lijst en voer dan een loop uit: wanneer het model een tool call teruggeeft, roep je het `/read`-endpoint van **WebReaderAPI** aan met de URL en stuur je de Markdown terug als een `tool`-bericht. Elke pagina kost 1 credit, 2 met een browser, vanaf US$ 0,07/1K pagina's per oktober 2026. Zonder KYC; betalen met crypto.

## Hoe ziet het read_url-schema eruit?

Twee parameters zijn genoeg: de `url` die je wilt lezen, verplicht, en een optionele `max_chars` waarmee het model een lange pagina kort kan houden. De description is het belangrijkste onderdeel: die vertelt het model wat de tool teruggeeft en wanneer je hem gebruikt. Houd hem feitelijk en kort.

We noemen de tool `read_url` in plaats van `web_fetch` om een botsing te voorkomen met tools die sommige agent-runtimes al onder die naam leveren.

*De tool-definitie*

```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
    }
  }
}
```

## Hoe werkt de tool-calling-loop?

Stuur het gesprek met de `tools`-lijst. Als het antwoord tool calls bevat, voer je ze allemaal uit, voeg je het resultaat toe als bericht met rol `tool` en de bijbehorende `tool_call_id`, en vraag je het model opnieuw. Komt er een antwoord terug zonder tool call, dan is dat het definitieve antwoord.

Beperk het aantal rondes. Drie of vier is ruim genoeg voor een vraag over een handvol pagina's, en de limiet beschermt je tegen een model dat blijft vragen om meer.

*Python: de volledige loop met het openai-package*

```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)})

```

## Hoe houd ik tokengebruik en kosten laag?

Drie instellingen zijn belangrijk. Houd `max_chars` bescheiden, rond 8.000 tot 20.000 tekens per pagina, want elk teken dat je teruggeeft wordt door het model gelezen. Houd de standaard `main_content=true` aan, zodat menu's en footers nooit in de context terechtkomen. En beperk de loop, zoals hierboven, zodat één vraag niet tientallen reads kan triggeren.

Aan onze kant gelden de kosten per pagina, niet per teken: 1 credit = 1 pagina lezen zonder browser; 2 credits wanneer de pagina een browser nodig heeft om JavaScript te renderen. Een verzoek dat mislukt, of dat de robots.txt van een site weigert, wordt niet in rekening gebracht. Een support-assistent die 300 pagina's per dag leest, gebruikt ongeveer 9.000 credits per maand, binnen 10.000 pagina's (US$ 3,75).

## Werkt het schema met andere modellen?

Ja, met kleine aanpassingen in de wrapper. De meeste tool-calling-API's nemen een JSON Schema voor de parameters en een naam en description voor de tool, dus `read_url` gaat ongewijzigd over naar andere providers. De [MCP-pagina](https://webreaderapi.com/nl/docs/mcp/) verpakt dezelfde functie voor clients die het Model Context Protocol spreken, en de [LangChain-pagina](https://webreaderapi.com/nl/docs/langchain/) verpakt hem voor LangChain- en LlamaIndex-agents.

## Wat moet het model zien wanneer een pagina wordt geweigerd?

Een korte, duidelijke zin, zoals de loop hierboven teruggeeft: "Could not read the page: blocked_by_robots". Modellen gaan daar goed mee om en vallen terug op andere bronnen of vertellen het de gebruiker. We respecteren robots.txt: onze crawler identificeert zich als WebReaderAPI, volgt de regels die voor hem of voor alle robots zijn geschreven, en leest geen pagina die een site niet toestaat. Geweigerde pagina's worden niet in rekening gebracht. Als een site ons verzoek weigert, geven we het antwoord terug: we lossen nooit CAPTCHA's op, loggen niet in op accounts en proberen nooit een blokkade of paywall te omzeilen. Geen van deze weigeringen gebruikt een credit.

## Function calling: veelgestelde vragen

### Heb ik hiervoor de Responses API of Chat Completions nodig?

Het voorbeeld gebruikt Chat Completions met de `tools`-parameter. Dezelfde functiedefinitie en dezelfde `read_url`-body werken met andere OpenAI-interfaces die function tools accepteren; alleen de loop eromheen verandert.

### Kan het model meerdere pagina's in één beurt lezen?

Ja. Een antwoord kan meerdere tool calls bevatten; de loop voert ze allemaal uit en geeft elk resultaat terug voordat het model opnieuw wordt gevraagd. Houd `max_chars` lager wanneer het model veel pagina's tegelijk leest.

### Wordt de API-sleutel aan het model blootgesteld?

Nee. De sleutel blijft in de omgeving van je code. Het model ziet alleen de naam van de tool, de description en de tekst die je functie teruggeeft.

### Wat kost een geweigerde pagina?

Niets. 1 credit = 1 pagina lezen zonder browser; 2 credits wanneer de pagina een browser nodig heeft om JavaScript te renderen. Een verzoek dat mislukt, of dat de robots.txt van een site weigert, wordt niet in rekening gebracht.

---

WebReaderAPI: WebReaderAPI is een web reader-API voor AI-agents die elke openbare webpagina omzet in schone Markdown, betaald met crypto zonder ID te uploaden. Prepaid credits vanaf US$ 0,07/1K pagina's verlopen nooit, en robots.txt wordt gerespecteerd. Zonder KYC — geen ID, geen selfie, geen documenten. Betalen met crypto.
