# Web and files

> Call web APIs, download files, render image templates, ask AI, run saved scripts and find emojis.

These functions reach outside the command: the web, your image templates, AI and your saved scripts.

Write `await` in front of `fetch`, `render_template`, `ask_ai` and `load_script`. The bot adds it for `load_file`.

## Limits

Each run has its own count. Pre-hook and post-hook share it.

| What | Free | Premium |
|---|---|---|
| `fetch` different requests per run | 5 | 20 |
| `load_file` different links per run | 5 | 20 |
| Download size, each | 5 MB | 100 MB |
| Download time, each | 20 seconds | 120 seconds |
| `render_template` per minute, whole server | 20 | 120 |
| Images inside one template | 5 | 15 |
| `ask_ai` | not available | available |

Asking for the same `fetch` or `load_file` link again in the same run is free. The bot reuses the first answer.

> **Note:** The bot only fetches public web addresses. Links to private or local network addresses are refused.

## fetch

Calls a web address and returns the answer.

```python
await fetch(url, method="GET", to="json", dynamic=False, **options)
```

| Argument | What it does |
|---|---|
| `url` | The web address. |
| `method` | `GET`, `POST`, `PUT`, `PATCH` or `DELETE`. |
| `to` | How to read the answer: `json` (a dict or list), `text` or `bytes`. |
| `dynamic` | `True` asks again every time, even for the same link. Use it for random answers. |
| `**options` | Sent with the request, for example `headers=`, `json=` or `data=`. |

Returns `None` when the site answers with an error (status 400 or higher), or when the answer cannot be read as `to`.

**Example: a random cat fact**

```python
data = await fetch("https://catfact.ninja/fact")
if data is None:
    set_error("The cat fact site is down. Try again later.")
set_content(data["fact"])
```

**Example: send JSON to an API**

```python
await fetch(
    "https://example.com/api/report",
    method="POST",
    json={"user": str(user.id), "reason": reason},
    headers={"Authorization": "Bearer " + server_var("api_key")},
)
```

> **Random answers:** Two calls to the same link in one run return the same answer. For a new random result each time, pass `dynamic=True`.

## load_file

Downloads a file from a link so you can attach it to a message.

```python
load_file(file, filename=None)
```

| Argument | What it does |
|---|---|
| `file` | The link to the file. |
| `filename` | The name Discord shows. Optional. |

Returns `None` when the download fails. Fails with *"File size is greater than 5MB"* when the file is too big for your plan.

```python
pic = load_file("https://example.com/banner.png", "banner.png")
send_message(channel, "New banner!", attachments=[pic])
```

## render_template

Draws an image from one of your **Image Templates** and returns it as a file.

```python
await render_template(name, **values)
```

| Argument | What it does |
|---|---|
| `name` | The template name from the dashboard. |
| `**values` | A value for each variable in the template. `avatar` also fills the template's avatar picture. |

```python
card = await render_template(
    "rank-card",
    name=user.display_name,
    avatar=user.avatar_url,
    level=member_var("level", 1),
)
add_attachment(card)
```

Put the result in `attachments=`, `add_attachment(...)`, or a `Message(image=...)`.

Fails when a variable has no value, and names the missing ones. On a free server, going over 20 renders a minute fails. On premium the call waits for a free slot.

## ask_ai

**Premium only.** Asks an AI model a question and returns the answer as text.

```python
await ask_ai(
    prompt,
    instructions=None,
    max_words=None,
    use_context=True,
    cache=False,
)
```

| Argument | What it does |
|---|---|
| `prompt` | The question. |
| `instructions` | How the AI should answer, like a role or a tone. |
| `max_words` | A shorter limit for the answer. The most is 1,024 words. |
| `use_context` | `True` tells the AI about your server and its commands. |
| `cache` | `True` reuses a recent answer to the same question. |

```python
answer = await ask_ai(question, instructions="Answer in one friendly sentence.")
set_content(answer)
```

The longest prompt depends on the plan: Starter 500, Basic 1,000, Advanced 2,000 characters. On a free server it fails with *"ask_ai is a premium feature"*.

## load_script

Runs one of your saved **Scripts** inside this command.

```python
await load_script(script_id, **variables)
```

| Argument | What it does |
|---|---|
| `script_id` | The script's ID from the dashboard. |
| `**variables` | Extra variables the script can read. |

The script sees all of your command's variables, plus the ones you pass. When it changes a plain variable, your command does not see the change.

```python
await load_script("give-xp", amount=10)
```

A script cannot load another script. A disabled script fails and says why.

## emoji

Finds an emoji and returns it ready to put in a message.

```python
emoji(query, as_emoji=False)
```

| Argument | What it does |
|---|---|
| `query` | A custom emoji's name or ID from this server, or a name like `":fire:"`. |
| `as_emoji` | `True` returns the emoji object instead of text. |

```python
set_content(f"GG {emoji('pepe_happy')} {emoji(':tada:')}")
```

Returns nothing when no emoji matches.
