# Give an agent a tool — fetch a URL

Add one Fetch tool to an agent so the model can call a real HTTP API mid-conversation and answer from live data.

You'll take an agent and give it a single **Fetch** tool pointed at an HTTP API,
so the model can pull live data into a conversation instead of answering from its
training alone.

## What you need

- A provider and a model that supports tool calls.
- One HTTP endpoint you want to reach, as a URL — public, or one you hold an API
  key for.
- The API key, if the endpoint needs one (it is saved as a secret in your device
  keystore, not in the tool's configuration).

## Steps

1. Open **Tools**, tap the add button, and pick `fetch_url` to open the fetch tool
   form.

2. Give the endpoint an **Endpoint name** (up to 54 characters) and a one-line
   **Endpoint description**. The model reads the description to decide when to call
   the tool, and the name becomes part of the tool it sees.

3. Write the **URL** as a template. Plain text is literal; wrap the parts the
   model should fill as `{variable}` — for example
   `https://api.example.com/search?q={query}`. `{query}` is URL-encoded for you;
   use `{+path}` when a value must be inserted without encoding, and `{?tags*}` to
   expand a list into query parameters.

4. If the endpoint needs an API key, reference it in the URL or a header as
   `{secrets.apiKey}` and choose the secret in the **API key** field: tap it, then
   pick a saved secret or **Create new** to name and type one. The secret is stored
   in your device's keystore and substituted only at call time — it never appears
   in the tool's saved configuration. A tool written with the older `{apiKey}`
   still works, and its form says how to write it now.
   An endpoint that needs a second secret, such as an organization token beside the
   key, places it as `{secrets.<name>}` — `{secrets.org}` — and gets a field of its
   own under **Saved secrets**. A secret goes alone in its braces, in the path, the
   query, a header or the body, never in the host.
   If the endpoint also answers without a key, tick **The endpoint also answers
   without an API key**: with no secret on the device, the headers that use
   `{secrets.apiKey}` are left off, and the tool is not flagged as missing one. This
   is only offered for a key sent in headers, since a URL or a body can't drop it.

5. Optionally refine the request: choose the HTTP **Method** and, for
   POST/PUT/PATCH/DELETE, a **Body content-type** and **Body**; add **Headers**;
   declare each **Variable**'s type (Text, Integer, Decimal, Boolean), whether it's
   an array, whether it's required, and a default; and set **Retry** behaviour for
   endpoints that rate-limit (429, optionally 503, with a backoff entered in
   milliseconds). Under each template field, chips list the names it can read — the
   tool's variables, its secrets and constants, the request metadata — and tap to
   insert. A JSON body can be switched from **Placeholders** to **MiniJinja** when it
   has to be built from the arguments: a list from an array, a field only when an
   argument is present. See [Tools](/docs/features/tools#a-json-body-written-in-minijinja).

6. Optionally turn on **Detect response errors** under **Error handling** so a
   failed response (by default any 4xx/5xx) is flagged as an error rather than
   passed off as a normal answer: the output filters are skipped and the raw error
   body comes back, optionally prefixed with the HTTP status. If a raw error page
   isn't useful to the model, set **What the model reads for an error** to **A
   template** and fill in the **Error message template** — a small template
   rendered over the error body to produce a clean failure line.
   `{{status}}`, `{{reason}}`, and `{{body}}` are always available, and a JSON
   error body exposes its own fields too, so
   `Request failed ({{status}}): {{message}}` turns a `429` with
   `{"message": "quota exceeded"}` into `Request failed (429): quota exceeded`.
   Switch the template to **MiniJinja** for Jinja's syntax: it reads the same
   `status`, `reason` and `body`, and a JSON error body as `json`, so the same
   line is `Request failed ({{ status }}): {{ json.message }}`. Saving checks the
   names it reads, and an error that lacks a value it reads — an HTML page has no
   `json` — comes back as the raw body rather than a half-filled line.

7. Tick the box above **Save** confirming that the endpoint's operator sees
   everything the tool sends it, then save. To try the tool before an agent
   does, tap the floating button on its page: it runs with arguments you type and
   shows the raw response.

8. Open the agent you want to extend — or build a simple one first (see
   [Build a simple agent](/docs/use-cases/simple-agent)) — expand its **Tools** section,
   tap **Add tools**, and pick the new tool.

![A Fetch tool attached to an agent, shown in the agent form's Tools section](/images/agent_fetch_tool.png)

9. Save the agent and start a conversation. Ask something that needs the
   endpoint: the model calls the Fetch tool, the response comes back, and the
   agent answers from it.

![The agent calling the fetch tool mid-conversation — the tool call's arguments and result, then the answer](/images/conversation_tool_call.png)

## Make it yours

- **Search the web** by pointing a Fetch tool at a search API's endpoint — there
  is no separate search tool; a fetch tool _is_ how search works.
- **Trim noisy responses** with a **Template** output filter, so the model sees
  only the fields it needs instead of a whole JSON payload.
- **Pick out exactly what matters** with a **jq** output filter — filter an
  array, compute a total, keep three fields of forty — before a Template turns it
  into prose.
- **Let a model do the trimming** with a **Model** output filter when the
  response has no fields to pick — a whole HTML page, say. It asks one question
  about the body and passes the answer on; point it at a small local model and
  the agent never sees the page at all.
- **Add several Fetch tools** to one agent — one per endpoint — to hand it a
  small toolbox of APIs.

More in [Use Cases](/docs/use-cases): [Build a simple agent](/docs/use-cases/simple-agent) · [Run a model fully offline](/docs/use-cases/offline-embedded)

---

[Bring Your LM](/) · [Docs](/docs/features) · [Marketplace](/marketplace)

[Articles](/articles) · [Releases](/releases) · [Bundle Format](/marketplace/bundle-format) · [Marketplace Terms](/terms) · [App Privacy](/privacy) · [Website Privacy](/website-privacy) · [Open Source](/open-source) · [llms.txt](/llms.txt)
