Skip to content
Guide 03

Data sources

Point the bot at a web address once, click the value you want, and a command reads it out live.

01What a data source is

at a glanceOne command, one web address, one value the bot reads out loud.

A data source is a web address you save once. When a viewer runs a command that quotes it, the bot calls that address, takes one value out of the answer, and says it in chat. Weather, a game stat, the queue length on your own server: if it answers over https and returns JSON or plain text, a command can read it.

One command, one live number.

Behind that line is an ordinary custom command. Its response, as typed in the editor:

It is {urlfetch:weather}°C in Montreal right now.

The token names the source, not the value. Which value the bot pulls out is saved on the source itself, as a path: an address inside the answer. The response is the building, current is the floor, temperature_2m is the door. Write it out with dots and you get current.temperature_2m. Section three does that clicking for you, and the analogy can go home now.

Note Data sources are free on every plan. A premium channel is served by a different internal lane, and every cap on this page is the same for both.

02Add one from the command editor

at a glanceSix clicks, without leaving the command you are writing.

Everything happens inside Commands, in the editor that docks beside your command list.

  1. Put the cursor where the value goes

    Open a command, click into Response, and leave the cursor at the spot the number belongs in the sentence.

  2. Open the palette

    Under the response, beside the {user} and {args} pills, sits a chip labelled Data source.

  3. Start a new one

    + New data source opens the modal titled Add a data source: "Point it at any web API, fetch one real response, then click the value you want to show in chat."

  4. Name it and paste the address

    Display name is for you. It auto-slugs into Data source name, which is the word that goes inside the token: "Lower-case letters, digits and underscores. Used inside {urlfetch:name}." Then Web address, https, up to 512 characters.

  5. Fetch one real response

    Fetch a sample calls your API for real, about once every 10 seconds. If it answers in a shape the modal cannot read, take the link or paste a response instead and paste one in by hand.

  6. Click the value, then add the source

    The answer becomes a tree under "Click the value you want to show in chat." Click temperature_2m and the modal reads Showing current.temperature_2m. Add source saves it, and the palette row inserts {urlfetch:weather} at your cursor.

  • Display name is the one you read in lists. It auto-slugs into the data source name below.
  • Data source name is the word inside the token: lower-case letters, digits and underscores, up to 32 characters.
  • Web address: https, absolute, up to 512 characters. The bot sends it exactly as typed, every time.
  • Fetch a sample makes a real request against your API. Roughly one test every 10 seconds.
  • Clicking a value saves its path on the source. "Use the whole response instead" switches to plain text.
The "Add a data source" modal with one value already picked.

Once a source exists, the same chip lists it. Every saved source shows its path so you can tell two weather feeds apart at a glance, and clicking a row drops the token where your cursor was.

  • The Data source chip sits with the token pills under Response, next to Counter.
  • Each row shows the path saved on the source, or "Plain text" when it prints the whole answer.
  • + New data source opens the modal without losing the command you are writing.
The "Data source" chip open beside the token pills.
Tip A channel holds 20 data sources. Past that the editor says "You've reached the 20 data source limit for your channel. Delete one to make room." Deleting is a two-tap control, and the server names the commands that quote the source before it lets go: "These commands quote it. They lose this data if you delete:".

03Picking the value

at a glanceA path is dots between steps. Click a value below and watch the token write itself.

This is the same tree the dashboard shows, with the same rules. Only values are clickable; a branch like current holds other things, so it cannot be the end of a path. Edit the response on the left and the tree follows, which is the fastest way to rehearse your own API before you save anything.

Click a value
The tree appears once the JSON parses.
The path grammar, the same one the editor checks when you save.
RuleWhat it means
Dots between stepscurrent.temperature_2m opens current, then takes temperature_2m out of it.
Lists use bare digitsitems.0.name is the first entry. Square brackets are not part of the grammar.
DepthUp to 8 steps. Past that the tree refuses the value: "Deeper than 8 levels. Pick something closer to the top."
Each stepLetters, digits, underscore and hyphen, up to 64 characters each.
The end of the pathLands on a value: text, a number, true or false. Stopping on an object, a list or an empty field counts as a broken data source, and chat shows your |default, or the raw token if there is none.
A path inside the tokenWins over the one saved on the source. {urlfetch:weather.current.wind_speed_10m} reads a different value from the same address.
The nameCase-insensitive. {URLFETCH:Weather} and {urlfetch:weather} are the same source.
The valueCleaned, trimmed, then cut at 100 bytes before it reaches chat.
Tip One saved address can feed several commands. Save {urlfetch:weather} on current.temperature_2m for !weather, then write {urlfetch:weather.current.wind_speed_10m} in !wind. Same source, same 20 data source budget, two different answers.

04APIs that need a key

at a glanceKeys live on your account, sealed, and travel as an Authorization header.

Some APIs want a key before they answer. Add it once in Settings, on the API keys panel: a label up to 32 characters so you recognise it later, and the secret itself, up to 512 characters, then Add key. The panel's own hint says it best: "API keys your data sources send with each request. People with shared access can use them but never see them."

The secret is sealed before it is written down and it is never shown back. You get the label and the last four characters, which is enough to tell two keys apart when you rotate one. When a data source is attached to a key, the bot sends it as an Authorization: Bearer header on every fetch, and the address itself stays clean.

In the "Add a data source" modal, pick the key in the API key field. "No key needed" is the right answer for most public APIs. If you have no key on file yet, the field says "No API keys yet. Add one in Settings, then pick it here." and an Open API keys link takes you to Settings.

Watch out Keep the key out of the Web address and out of the command response. An address is stored as text and anyone with dashboard access can read it, and a response goes to chat where everyone can. If your API only accepts the key as a query parameter, treat that key as public and rotate it on a schedule.

05Limits and timing

at a glanceEverything is capped, and the cache does most of the work.

The numbers below are the live ones. You will meet the cache first: an answer the bot liked is reused for 30 seconds, so a command that runs 40 times in a minute still calls your API twice.

Identical on every plan.
LimitThe number
Data sources per channel20.
Data sources in one response3. The editor refuses to save a fourth.
Web addresshttps only, absolute, up to 512 characters.
Addresses refusedIP literals, localhost, and anything ending in .local or .internal. Checked when you save and again on every fetch.
Response sizeThe bot reads up to 1 MiB of an answer, measured after decompression.
Sample sizeThe editor shows a tree only for a sample of readable text up to 128 KB. A larger or unreadable answer gives "The API answered, but its response could not be read automatically. Paste one below instead." A pasted sample over 128 KB gives "That sample is over 128 KB. Paste a shorter one."
Content typeapplication/json or text/*.
TimeoutsThe API gets 2.5 seconds, the fetch service 3 seconds, and the bot stops waiting at 3.5 seconds.
CacheA good answer is held 30 seconds. A refusal, like a 404 or a path that found nothing, is held 15 seconds. An outage is not cached.
Requests per channel6 a minute, counted across every data source you own.
Requests per data source30 a minute.
Requests per API host120 a minute, counted across every channel pointed at that host.
RedirectsUp to 3 hops, each one staying on https.
A host that keeps failingFive transport failures in a row and that host is rested for 60 seconds.
"Fetch a sample"About one test every 10 seconds: "Too many test runs. Each one calls the real API. Wait about 10 seconds and try again."
Data sources your commands quote
Requests your API actually receives
2 per minute
Answered from the 30-second cache
10 per minute
Refused by the 6 per minute channel cap
0 per minute

The cache exists for your API quota. Without it, one raid could spend a month of calls in an evening, and every other channel pointed at the same host would feel it too.

Note Two seconds of the bot's patience is a long time in chat. If your API is slow, expect a blank where the value goes during a busy stream, so write the sentence so it still reads if the number is missing, or add a fallback after a pipe.

06What chat shows when it fails

at a glanceA failed fetch leaves a blank or your fallback. A source that cannot answer at all shows your |default, or the raw token if there is none.

A command never goes silent because of a data source. The rest of the sentence is sent and the value is left blank, unless you wrote a fallback after a pipe: {urlfetch:weather|unavailable} says "unavailable" when the fetch fails. A source that is paused, missing or unable to produce a value works the same way: chat and timers show the token's |default. Without a |default, chat shows the raw token exactly as typed, and that is why a raw {urlfetch:weather} in chat means "check this data source".

What happenedWhat chat shows
The API refused, it rate limited you, it returned an error, it took longer than the bot waits, the path found nothing, or the message is a replay of an older oneNothing where the value goes, or your |fallback text.
The source is paused, missing, never saved, or could not produce a valueYour |fallback text. With no |fallback, the token printed exactly as typed: {urlfetch:weather}.
#your_channel

PrincessBarney!weather

ItsBagelBotIt is °C in Montreal right now.

Your key was rejected, or the endpoint turned the request away. Test it in the dashboard and you get the same verdict: "The API refused the request. Check the URL, and pick the right key under API key if this API needs one."

Tip Write a fallback for anything that can fail during a stream: {urlfetch:weather|unavailable}. Then a slow API reads as "unavailable" instead of a gap in the sentence.

If something goes wrong

What you seeWhat it meansWhat to do
The API refused the request. Check the URL, and pick the right key under API key if this API needs one.The API turned the test away: a wrong or missing key, or a wrong address.Check the web address. If the API needs a key, add it in Settings under API keys, pick it in the API key field, and test again.
Rate limited. Wait a moment and test again.A request cap was reached.Wait a minute and test again.
The API returned an error. Chat sees nothing, unless the token has a |default: {urlfetch:name|default}.The API answered with an error.Check the API status, or add a fallback after a pipe.
The API timed out. Chat sees nothing, unless the token has a |default: {urlfetch:name|default}.The API took longer than the bot waits.Try again, and add a fallback after a pipe.
This data source could not produce a value. Until it does, chat shows the token's |default value if it has one, otherwise {urlfetch:name} as typed. Check the web address and the API key, then click Fetch a sample again.The source is paused, or its path or address cannot give a value.Resume the source if it is paused. Otherwise fix the address or key, fetch a sample, and click the value again.
The fetch service did not answer. Try again in a moment.The service that calls your API did not reply in time.Wait a moment and test again.
The API answered, but its response could not be read automatically. Paste one below instead.The answer is not readable text, or it is over 128 KB.Click "or paste a response instead" and paste a real response by hand.
That is not valid JSON. Fix it and the tree appears here.The pasted sample is not JSON.Paste the response again, from the first brace to the last.
That sample is over 128 KB. Paste a shorter one.The pasted sample is too large for the tree.Paste a smaller slice of the response.
Too many test runs. Each one calls the real API. Wait about 10 seconds and try again.Fetch a sample allows about one test every 10 seconds.Wait 10 seconds.
You've reached the 20 data source limit for your channel. Delete one to make room.A channel holds 20 data sources.Delete a source you no longer use.
A data source named "name" already exists.Another source already has that name.Pick a different name, or edit the existing source.
A timer message can reference at most 3 different fetched values ({urlfetch:…}).Timer messages are capped at 3 data sources.Quote fewer sources in the timer message.
A raw {urlfetch:name} in chatThe source is paused, missing, or cannot produce a value, and the token has no |default. Tokens past the per-message cap also stay raw.Open the command, check the name matches a saved source, and resume it if paused. Add a |default so chat shows a fallback instead.

07What it will not do

at a glanceThe edges, in one screenful, before you design a command around one.
  • The address is fixed when you save it. {args} and {user} are left alone inside a web address, so a viewer cannot steer the request at your API.
  • Requests are GET, and headers are not yours to set. The one the bot adds is Authorization: Bearer, and only when the source carries a key.
  • Tokens expand in custom command responses, after the permission, live and cooldown checks have passed, and in timer messages, each time the timer fires. A timer message can quote up to 3 different data sources. Counters leave the token alone.
  • A replayed chat message never fetches twice. If the bot re-reads an older event, chat gets a blank (or your fallback) rather than a second call to your API.
  • Private and local addresses are turned down at both ends: when you save, and again at fetch time.
Tip Quote the same source twice in one response and the bot fetches once. Write {urlfetch:weather} in the sentence and {urlfetch:weather.current.wind_speed_10m} right after it: two values, one request, one line of your quota.
Note Last checked: 12 October 2026.