# OpenAI function calling：モデル用のread_urlツール

> read_urlツールを使ったOpenAI function calling：JSONスキーマと、リンクをモデル用のクリーンなMarkdownに変換するPythonループ。KYC不要、暗号資産で支払い。

- URL: https://webreaderapi.com/ja/docs/openai/
- 最終更新: 2026-10-22
- 発行者: WebReaderAPI (https://webreaderapi.com)

1つの関数、`read_url`を定義し、モデルが要求したときに実行します。どのページを読むかはモデルが選び、あなたのコードがAPIを呼び出してMarkdownを返します。同じスキーマは、tool callingをサポートする他のモデルでも動作します。

**概要 — OpenAIのモデルにfunction callingでWebページを読ませるにはどうすればよいですか？**

`tools`リスト内で`read_url`関数を宣言し、ループを実行します。モデルがツール呼び出しを返したら、URLを指定して**WebReaderAPI**の`/read`エンドポイントを呼び出し、Markdownを`tool`メッセージとして送り返します。各ページは1クレジット、ブラウザ使用時は2クレジットで、$0.07/1Kページ時点の価格は2026年10月現在です。KYC不要、暗号資産で支払い。

## read_urlスキーマはどのような形ですか？

パラメータは2つで十分です。必須の`url`（読み取る対象）と、モデルが長いページを短く保てる任意の`max_chars`です。最も重要なのはdescriptionで、ツールが何を返すか、いつ使うべきかをモデルに伝えます。事実に基づいて簡潔に保ちましょう。

エージェントランタイムの中には、同名のツールをすでに提供しているものがあるため、ツール名は`web_fetch`ではなく`read_url`としています。

*ツール定義*

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

## tool callingのループはどのように動作しますか？

`tools`リストを含めて会話を送信します。応答にツール呼び出しが含まれていれば、それぞれを実行し、結果をrole `tool`と対応する`tool_call_id`を持つメッセージとして追加し、再度モデルに問い合わせます。ツール呼び出しを含まない応答が返ってきたら、それが最終回答です。

ラウンド数には上限を設けましょう。数ページに関する質問なら3〜4回で十分であり、上限はより多くの読み取りを要求し続けるモデルからあなたを守ります。

*Python: 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)})

```

## トークン使用量とコストを抑えるにはどうすればよいですか？

重要な設定は3つあります。`max_chars`を控えめに、1ページあたり約8,000〜20,000文字に保ちましょう。返すすべての文字がモデルに読まれるためです。デフォルトの`main_content=true`を維持すれば、メニューやフッターがコンテキストに入ることはありません。そして上記のようにループに上限を設ければ、1つの質問が数十回の読み取りを引き起こすことはありません。

当社側では、コストは文字数ではなくページ単位です: 1クレジット = ブラウザなしでページを1回読み取り。ページのJavaScriptレンダリングにブラウザが必要な場合は2クレジット。失敗したリクエスト、またはサイトのrobots.txtが拒否したリクエストは課金されません。1日に300ページを読むサポートアシスタントは月に約9,000クレジットを使用し、10,000ページ（$3.75）の範囲内に収まります。

## スキーマは他のモデルでも動作しますか？

はい、ラッパーに小さな変更を加えるだけで動作します。ほとんどのtool calling APIはパラメータ用のJSON Schemaと、ツールの名前・説明を受け取るため、`read_url`はそのままプロバイダー間を移行できます。[MCPページ](https://webreaderapi.com/ja/docs/mcp/)はModel Context Protocolを扱うクライアント向けに同じ関数をパッケージ化し、[LangChainページ](https://webreaderapi.com/ja/docs/langchain/)はLangChainおよびLlamaIndexエージェント向けにラップします。

## ページが拒否されたとき、モデルには何が見えるべきですか？

上記のループが返すような、短く明確な一文です: "Could not read the page: blocked_by_robots"。モデルはこれをうまく処理し、他のソースにフォールバックするか、ユーザーに伝えます。当社はrobots.txtを尊重します。クローラーはWebReaderAPIとして名乗り、自身向けまたはすべてのロボット向けに書かれたルールに従い、サイトが禁止したページは読み取りません。拒否されたページは課金されません。サイトが当社のリクエストを拒否した場合、その回答をそのまま返します。CAPTCHAを解いたり、アカウントにログインしたり、ブロックやペイウォールを回避しようとしたりすることは決してありません。これらの拒否はいずれもクレジットを消費しません。

## Function calling: よくある質問

### Responses APIとChat Completionsのどちらが必要ですか？

この例では`tools`パラメータを指定したChat Completionsを使用しています。同じ関数定義と同じ`read_url`ボディは、function toolsを受け入れる他のOpenAIインターフェースでも動作します。変わるのはそれらを囲むループだけです。

### モデルは1ターンで複数のページを読めますか？

はい。応答には複数のツール呼び出しを含めることができ、ループはそれぞれを実行し、再度モデルに問い合わせる前にすべての結果を返します。モデルが一度に多くのページを読む場合は、`max_chars`を低めに保ちましょう。

### APIキーはモデルに公開されますか？

いいえ。キーはあなたのコードの環境に留まります。モデルが見るのはツールの名前、説明、およびあなたの関数が返すテキストだけです。

### 拒否されたページのコストはいくらですか？

ゼロです。1クレジット = ブラウザなしでページを1回読み取り。ページのJavaScriptレンダリングにブラウザが必要な場合は2クレジット。失敗したリクエスト、またはサイトのrobots.txtが拒否したリクエストは課金されません。

---

WebReaderAPI: WebReaderAPIはAIエージェント向けのWebリーダーAPIで、あらゆる公開WebページをクリーンなMarkdownに変換します。暗号資産で支払い、本人確認書類のアップロードは不要です。$0.07/1Kページから購入できるプリペイドクレジットに有効期限はなく、robots.txtを尊重します。KYC不要 — 身分証もセルフィーも書類も不要。暗号資産で支払い。
