Skip to content

Template variables & built-ins

Insert these tokens anywhere in a command response template:

Token Description
$(user) Display name of the viewer who triggered the command.
$(touser) The first word after the command trigger, exactly as typed — an @ is kept, so !hug @bob gives @bob. When nothing follows the trigger it falls back to the display name of the viewer who ran the command. In a !so message it is the shoutout target’s display name.
$(channel) Login name of the channel.
$(query) Everything after the command trigger, as one line — the same text $(args) gives.
$(args) All arguments joined with spaces.
$(1), $(2), … Individual positional arguments (1-indexed) — $(1) is the first word after the trigger. The brackets are required: a bare $1 is ordinary text, so a response can quote a dollar amount ($5, $1605) without it being eaten. The command editor and the !addcom reply both point out a $1 that looks like it was meant as a variable. Inside a $(counter …) token, $1 is the placeholder that token defines, and does resolve.
$(count) How many times this command has been used in this channel, counting this use. Uses of an alias count toward the command it points to. Outside a command — a !so message or an auto chat — it is 0. If the count can’t be read it resolves to [count: unavailable] rather than to 0.
$(uptime) How long the stream has been live, e.g. 1h 23m (23m 45s under an hour). When Twitch reports that the channel isn’t live it reads stream is offline. If Twitch can’t be read — for example when your Twitch connection needs re-authorizing — it resolves to [uptime: unavailable] instead, so a failed check never tells chat that a live stream has ended.
$(game) Current game/category set on the channel. Cached for up to a minute; resolves to [game_name: unavailable] if Twitch can’t be read.
$(title) Current stream title. Cached for up to a minute; resolves to [title: unavailable] if Twitch can’t be read.
$(followage) How long the viewer who ran the command has followed the channel, e.g. 1y 2mo, 3mo 4d or 5d 6h, or not following. It always describes whoever ran the command and takes no argument, so it can’t look up someone else. A brand-new follow can take up to a minute to show. Resolves to [followage: unavailable] or [followage: error] when Twitch can’t be read.
$(twitch LOGIN [FIELD]) A field from a Twitch account’s profile, looked up by login name (without the @). FIELD is display_name (default), login, id, description, created_at (when the account was made, as a timestamp), broadcaster_type (partner, affiliate or empty) or profile_image_url; a field Twitch doesn’t return comes back empty. LOGIN has to be written out — $(twitch $(1)) does not look up whoever was named in chat, because tokens don’t nest. Resolves to [twitch: user not found] for an account that doesn’t exist, and to [twitch: error] or [twitch: unavailable] when Twitch can’t be read. Cached for a minute.
$(time TZ ["FORMAT"]) The current time in time zone TZ, e.g. $(time Europe/London) → 03:45 PM. TZ is required and must be one of the zones listed under Time zones below, spelled exactly: any other resolves to [time: unknown timezone], and leaving it out resolves to [time: invalid format]. FORMAT, in double quotes, lays the time out yourself: YYYY year, MM month, DD day, HH hour (24-hour clock), mm minute, ss second — e.g. $(time America/New_York "YYYY-MM-DD HH:mm"). Each placeholder is filled once.
$(random MIN-MAX) Random integer between MIN and MAX inclusive, e.g. $(random 1-20). MIN may be negative — $(random -5-5). Use it more than once in a response and each one rolls independently, so $(random 1-6) + $(random 1-6) is two dice.
$(randomchatter [FILTER]) Display name of a random viewer in chat. FILTER is the same comma-separated syntax as the bot API (sub, not_sub, tier1–tier3, vip, mod, follower, not_follower), e.g. $(randomchatter sub); list several and a viewer has to match all of them. Use it more than once in a response and each one picks a different person while chat has enough people, so $(randomchatter) vs $(randomchatter) vs $(randomchatter) is a three-way draw. It draws from Twitch’s list of who is connected to your chat, so it works whether or not you’re live. That list is cached and refreshed every few minutes while the variable is in use, so someone who has only just arrived may not be drawn yet. It resolves to [no chatters] when nobody matches, or when the chatter list can’t be read.
$(randompick A|B|C) One option picked at random from a list. Separate the options with | — $(randompick heads|tails) — or quote them and separate with spaces, which is what an imported StreamElements ${random.pick} looks like: $(randompick 'moonberry tea' 'frog juice'). Apostrophes and quotes inside a phrase need no escaping. Quote an option to protect a | or a ( ) inside it — $(randompick "a (b)" "c|d"). Use it more than once in a response and each one draws independently, so two identical picks can disagree.
$(counter NAME [op N] [of TARGET]) Persistent whole-number counter saved per channel. Defaults to a per-viewer (user-level) tally; of channel makes it one shared channel-wide number. op is add/sub/set; omit it to read, use top for a leaderboard, or closest N for the viewer(s) nearest a number. A response can hold at most 5 $(counter …) tokens — with more, the whole response becomes [error: too many $(counter) operations in template]. See the AI token DSL page for the full grammar.
$(eval EXPR) Evaluates a small sandboxed expression and inserts the result: arithmetic, comparisons, cond ? a : b, let bindings and a fixed set of functions such as random(1, 20) and upper(user). It can read a counter or a channel stat and branch on it. The result is cut to 400 characters, and a mistake in the expression shows in chat as [eval: …]. See the AI token DSL page for the full grammar.
$(followers) Total number of people following the channel, from Twitch. Also accepted as $(followcount). The figure is cached: while the variable is in regular use it trails Twitch by about a minute at most, but the first use after a quiet spell can show a figure up to 15 minutes old (the use after that is fresh). Resolves to [followers: unavailable] when Twitch can’t be read — for example when your Twitch connection needs re-authorizing — rather than to 0.
$(subs) Total number of active subscribers. Also accepted as $(subcount). Affiliate/Partner only — it resolves to [subs: unavailable] on a channel Twitch will not report subscriptions for. Cached the same way as $(followers).
$(subpoints) Total subscriber points (Tier 1 = 1, Tier 2 = 2, Tier 3 = 6) — the number Twitch counts toward sub-point goals. Affiliate/Partner only — [subpoints: unavailable] otherwise. Cached the same way as $(followers).
$(plus [FIELD]) Twitch Plus Program points for the month in progress, the same figure the Numbers module and your Plus goal overlay show. FIELD is points (default), level (Plus level at that figure), next (points needed for the next level in total), needed (points still to go), or share (the revenue split at the current level). Cached the same way as $(followers), and [plus: unavailable] when the figure can’t be read. Readable inside $(eval …) too, so a response can branch on the gap — e.g. $(eval let n = $(plus needed); n > 0 ? …); keep the token outside quoted strings.
$(goal METRIC [FIELD]) A goal from the Numbers Goals page, the same target and figure the dashboard and your goal overlays show. METRIC is follows, subs, newsubs, resubs, gifts, bits, plus or charity. FIELD is current, target, needed, percent or label; with no FIELD it prints current/target, e.g. 412/500. [goal subs: no goal set] when that goal has no target, and [goal: unavailable] when it can’t be read. Cached the same way as $(plus), and readable inside $(eval …).
$(subathon [FIELD]) Your subathon goals ladder. FIELD is current, target, needed, percent, next (the next goal’s name), done (goals reached), rungs (goals in the ladder) or title. A points ladder shows percentages, as the overlay does: $(subathon) prints 62% to <next goal> and needed the percentage still to go. A spoiler goal reads a secret goal. [subathon: no goals set up] with no ladder. Readable inside $(eval …).
$(tipgoal [FIELD]) The Tipping module’s active goal: current, target, needed, percent or label, money in the goal’s currency, e.g. $42.50/$100.00. [tipgoal: no active goal] without one. Readable inside $(eval …), where money is a plain number like 57.5.
$(charity [FIELD]) The running charity campaign, Twitch Charity or a linked Tiltify campaign: current, target, needed, percent, name (the campaign), charity or source (Twitch / Tiltify). With no fundraising target it prints $310.00 raised. [charity: no campaign running] without one. Readable inside $(eval …).
$(hypehigh FIELD) Hype-train all-time-high stats, where FIELD is level (default), total, percent, shared_level, or shared_total. percent is how the current hype train — or the most recent one, between trains — compares to your record, so it counts up live during a train and reads over 100 when one is breaking the record. Your record is picked up during the first hype train StreamerCatalyst sees on your channel. Until then, and for a figure it doesn’t have (such as shared_level before a shared hype train), it resolves to [hypehigh: unavailable] rather than to 0.

Tokens don’t nest. A token written inside another token’s arguments is passed along as plain text rather than filled in first, so $(twitch $(1)) looks up an account literally called $(1), and a $(user) inside a $(urlfetch …) URL is sent as written. The exceptions: $(eval …) can read counters and channel stats (see the AI token DSL reference), and the tokens inside a $(randompick …) option are filled in once that option is drawn — apart from another $(random …) or $(randompick …). A token the bot doesn’t recognise stays in the response exactly as typed.

Counters are the only persistent storage in command templates — a whole-number tally, kept either per viewer or as one channel-wide number.

  • Per-viewer (user-level): $(counter hugs add 1) adds to the triggering viewer’s own tally, and $(counter hugs) reads it.
  • Channel-wide (one shared number): $(counter deaths add 1 of channel) — everyone’s command writes the same counter, and $(counter deaths of channel) reads it.
  • Leaderboard: $(counter hugs top) — lists the top viewers of a per-viewer counter (highest value first). A channel-wide counter is one number and has no leaderboard.
  • Closest: $(counter guesses closest $1) — lists the viewer(s) whose count is nearest a number, with everyone tied for nearest. Add under (closest $1 under) so a count above the number never wins. A viewer whose count is 0 or unset is never listed.

A per-viewer and a channel-wide counter with the same name are two separate numbers, so every token for a channel-wide counter needs of channel, the reads included: $(counter deaths) reads the viewer’s own deaths, which is 0 when deaths are counted of channel.

See AI token DSL reference for the full grammar and examples.

$(time TZ) accepts these zone names, written exactly as shown. Pick the one that shares your local time; any other name resolves to [time: unknown timezone].

  • UTC
  • America: America/New_York, America/Chicago, America/Denver, America/Los_Angeles, America/Anchorage, America/Halifax, America/Sao_Paulo, America/Toronto, America/Vancouver, America/Phoenix, America/Detroit, America/Indiana/Indianapolis
  • Europe: Europe/London, Europe/Paris, Europe/Berlin, Europe/Madrid, Europe/Rome, Europe/Amsterdam, Europe/Brussels, Europe/Stockholm, Europe/Oslo, Europe/Helsinki, Europe/Warsaw, Europe/Prague, Europe/Vienna, Europe/Zurich, Europe/Athens, Europe/Moscow, Europe/Istanbul
  • Asia: Asia/Tokyo, Asia/Seoul, Asia/Shanghai, Asia/Singapore, Asia/Kolkata, Asia/Dubai, Asia/Bangkok, Asia/Hong_Kong, Asia/Taipei, Asia/Jerusalem, Asia/Riyadh, Asia/Jakarta
  • Australia: Australia/Sydney, Australia/Melbourne, Australia/Brisbane, Australia/Perth
  • Pacific: Pacific/Auckland, Pacific/Honolulu
  • Africa: Africa/Cairo, Africa/Johannesburg, Africa/Lagos
Token Description
$(urlfetch URL) Fetches URL and inserts the response body as text. Only the first 400 characters are used, and a response larger than 10 KB may come back empty, so point it at something that answers briefly. HTTPS only; redirects are not followed ([urlfetch: redirects not followed]) and local or private network addresses are refused. It gives up if the server hasn’t answered within 3 seconds ([urlfetch: timeout]), and an error status shows as [error: HTTP 404] and so on. The answer for a URL is cached for 60 seconds. The URL is sent as written — a token inside it, such as $(user), is not filled in. Also accepted as $(customapi URL).
$(urlfetch json URL PATH) Fetches URL, reads the body as JSON and inserts the value at PATH, a dot-separated path such as data.0.name (a number picks an item from a list). Leave PATH out to insert the body as it came. Only the first 400 characters of the response are kept, so the whole JSON body has to fit in 400 characters — a longer one resolves to [urlfetch: invalid JSON]. A path that leads nowhere resolves to nothing, or to [urlfetch: path not found]. Otherwise the same rules as $(urlfetch URL); $(customapi json …) works too.

A response can hold at most 3 $(urlfetch …) / $(customapi …) tokens. With more, the whole response becomes [error: too many remote fetches in template].

Set the permission field of a command to restrict who can trigger it:

Level Who can use it
everyone All viewers
regular Viewers on your regulars list (Chatters → Community → Regulars)
follower Viewers who follow the channel
sub Subscribers, any tier
vip Channel VIPs
mod Moderators, Lead Moderators included
broadcaster The broadcaster only

A higher badge passes every level below it. The levels are ranked in the order above, and a chat badge lets its holder through its own level and all the ones beneath: a VIP can run a sub command, a mod can run vip and sub commands, and a subscriber can run follower and regular ones.

regular and follower have no badge. A viewer without a higher one is checked against your regulars list or your follower list instead, so a regular who doesn’t follow can’t run a follower command, and a follower who isn’t a regular can’t run a regular one. The follower list is cached, so a brand-new follower can wait up to about two hours before a follower command answers them, and if the list can’t be read only badge holders get through.

The broadcaster can run every command and is never held by a cooldown.

Commands support two independent cooldowns:

  • Global cooldown — channel-wide lockout after each use (seconds).
  • User cooldown — per-viewer lockout after each use (seconds).

Each can be 0–86400 seconds. A command added in the dashboard or with !addcom starts at 0 (no cooldown); one created by Paste commands on the Import tab starts at 5 seconds global and 15 seconds per viewer. Change them in the dashboard or with !cooldown. An alias shares its parent command’s cooldowns. Mods are held by cooldowns like everyone else; only the broadcaster skips them.

Field Constraint
Trigger Starts with !, then letters, numbers, dashes or underscores — 30 characters at most in all. Triggers are stored lowercase. One added from chat with !addcom can’t contain a dash
Response template Max 500 characters
Response as sent If a response comes out longer than 500 characters once its variables are filled in, the bot doesn’t send it — leave room for long values such as $(query) or a $(urlfetch …) result
Tokens per response At most 3 $(urlfetch …) / $(customapi …) and 5 $(counter …); more and the whole response becomes an error message
Commands per channel Set by your plan (see Tier comparison), and never more than 500. Aliases count toward it

These are always available. On Chatters → Commands you can switch each one off or give it a different trigger — on the Utility tab, or the Shoutouts tab for !so and !soq — except !addcom, !editcom, !delcom and !cooldown, which are always on under their own names. A custom command you create in the dashboard with a built-in’s trigger answers in its place (!addcom refuses those triggers).

Command What it does
!uptime Replies with how long the stream has been live, or that it is offline.
!game Anyone: !game shows the current category. Mod: !game Fortnite changes it — the name has to match a Twitch category exactly, or the bot suggests up to three close ones.
!title Anyone: !title shows the stream title. !title Road to 1k! changes it — the broadcaster always can; mods and VIPs can if you allow them, and so can any viewer you grant title access (Bot Setup → Settings).
!followage How long the viewer who typed it has been following the channel. It answers only for whoever typed it.
!accountage How old a Twitch account is. !accountage for yourself, !accountage @someone for another viewer.
!watchtime How long a viewer has watched this channel, counted from when tracking started. !watchtime for yourself, !watchtime @someone for another viewer.
!hypecheck Time since the last Hype Train ended and how long until the next one can start.
!commands Posts a link to your public command list page. Says nothing while that page is switched off (Bot Setup → Settings).
!addcom Mod: adds a new custom command. !addcom !hello Hi $(user)! A trigger added from chat can use letters, numbers and underscores (no dashes), and a built-in’s trigger is refused.
!editcom Mod: replaces an existing command’s response. !editcom !hello Hey $(user)!
!delcom Mod: deletes a custom command. !delcom !hello
!cooldown Mod: sets a command’s cooldowns, 0–86400 seconds. !cooldown !hello 10 sets the global cooldown; !cooldown !hello global:30 user:10 sets both.
!so Mod: shoutout with optional native Twitch shoutout. !so @username — uses a separate offline message when the target is offline; $(status) resolves to live/offline. The native shoutout is queued while Twitch’s 2-minute cooldown is active. A repeat !so for the same user within 5 seconds (two mods reacting to the same raid) is ignored, so only one shoutout goes out.
!soq Anyone: !soq lists the native shoutout queue — the next five, with roughly how long until each goes out. Mod: !soq remove <user> takes one out, !soq clear empties it.
!timer Countdown timer, one at a time. Anyone: !timer shows the time left. Mod: !timer 5m [label] starts one (also 1h30m, 90s, 1:30 or 1:30:00, up to 24 hours) and !timer stop cancels it. The bot posts reminders as it counts down and “Time’s up!” at the end.

!game and !title can change your category and title only once the broadcaster has granted one extra Twitch permission; the Utility tab asks for it. Quote commands (!quote, !addquote, !editquote, !delquote) are covered in the Chatters module’s Quotes section.