---
url: https://docs.freetheai.org/concepts/web-search.md
description: >-
  Give any model current information with FreeTheAI's /v1/search and /v1/answer
  endpoints, or let the dog/ and lion/ models search on their own with
  web_search_options.
---

# Web search

Models only know what was in their training data. FreeTheAI has two ways to bring in current information from the web.

| Way | Works with | Use it when |
| --- | --- | --- |
| [`POST /v1/search`](/api/search) and [`POST /v1/answer`](/api/answer) | Any model, any app, any code | You want results or a short answer to use yourself or put in a prompt |
| `web_search_options` and `web_search` tools | `fta/dog/` and `fta/lion/` models | You want the model to search on its own and cite its sources |

## Search and Answer endpoints

`/v1/search` returns current results (title, link, and a text excerpt when available). `/v1/answer` returns a short written answer in Markdown with the sources it used; it takes a few seconds longer than a search.

```bash
curl https://api.freetheai.org/v1/search \
  -H "Authorization: Bearer $FTAI_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "latest stable Python release", "num_results": 5}'
```

Both use your API key and share one small **daily web-search allowance**, separate from your free requests. Every response tells you where you stand with `daily_used` and `daily_limit`, and the allowance resets at 00:00 UTC. Identical queries within an hour return the same results.

### Give the results to any model

```python
import os
import requests
from openai import OpenAI

KEY = os.environ["FTAI_KEY"]
client = OpenAI(base_url="https://api.freetheai.org/v1", api_key=KEY)

question = "What changed in the latest stable Python release?"
found = requests.post(
    "https://api.freetheai.org/v1/search",
    headers={"Authorization": f"Bearer {KEY}"},
    json={"query": "latest stable Python release notes", "num_results": 5},
    timeout=60,
).json()

sources = "\n\n".join(
    f"[{i + 1}] {r['title']} ({r['url']})\n{r.get('text', '')[:1500]}"
    for i, r in enumerate(found["results"])
)

reply = client.chat.completions.create(
    model="fta/zai/glm-5.3",
    messages=[
        {"role": "system", "content": "Answer from the sources below and cite them as [1], [2].\n\n" + sources},
        {"role": "user", "content": question},
    ],
)
print(reply.choices[0].message.content)
```

## Let the model search

The `fta/dog/` and `fta/lion/` models can search the web before they answer. Ask for it in the format you use:

::: code-group

```json [Chat Completions]
{
  "model": "fta/dog/gpt-6-luna",
  "web_search_options": {},
  "messages": [{ "role": "user", "content": "What is the weather in Lisbon this week?" }]
}
```

```json [Responses]
{
  "model": "fta/dog/gpt-6-luna",
  "tools": [{ "type": "web_search" }],
  "input": "What is the weather in Lisbon this week?"
}
```

```json [Anthropic Messages]
{
  "model": "fta/dog/gpt-6-luna",
  "max_tokens": 1024,
  "tools": [{ "type": "web_search_20250305", "name": "web_search" }],
  "messages": [{ "role": "user", "content": "What is the weather in Lisbon this week?" }]
}
```

:::

On Chat Completions the sources come back as `url_citation` annotations on the reply message (or on the stream delta):

```json
{
  "role": "assistant",
  "content": "Expect mild days around 19°C with rain on Thursday...",
  "annotations": [
    { "type": "url_citation", "url_citation": { "url": "https://...", "title": "Lisbon weather forecast" } }
  ]
}
```

Other models do not search this way; leave these options out and use `/v1/search` as shown above. Model IDs change, so check the [Models page](https://freetheai.org/models) for the current `fta/dog/` and `fta/lion/` models.
