MLB promotions widget
Never miss bobblehead night again — your team’s upcoming home-game giveaways and theme nights, held on the wall so anyone walking past knows what’s worth the trip.
The baseball.promotions widget (type = "baseball.promotions", from the baseball plugin) pulls the home-game promotions feed for a tracked team and renders each promo as a held card on a wide sign or a scrolling crawl on a narrow one. Promotions come from MLB’s free StatsAPI schedule feed — no API key, no account, it just works.

That held card is what a mid-width sign shows by default, and what layout = "card" pins on any sign (it’s what the GIF above does). By default the widest signs run a hi-res crawl of the same fields instead, and the narrowest scroll classic promo lines — same widget, no config change.
Requires led-ticker-core >= 4.9. The demo = true mode used for the GIF on this page needs led-ticker-baseball >= 1.6.0.
Install the plugin first (steps just below this snippet), then drop it into a section:
[[playlist.section]]mode = "slideshow"hold_time = 6
[[playlist.section.widget]]type = "baseball.promotions"team = "TOR"highlight = ["Loonie Dogs"]team is the only required field. Here any promo whose name contains “Loonie Dogs” is emphasized — highlighted promos render in amber and sort first.
To install the plugin on a Docker deploy, add its line to the plugin manifest and restart:
# First plugin? Create your manifest from the example:cp -n config/requirements-plugins.example.txt config/requirements-plugins.txt# Add the package (one name per line), then restart:echo "led-ticker-baseball==1.6.0" >> config/requirements-plugins.txtdocker compose restartOther install paths (web UI Store tab, dev checkout) are on the Plugins page.
Try it in demo mode
Section titled “Try it in demo mode”Set demo = true to render a fixed set of same-day promos with no StatsAPI call and no network:
[[playlist.section]]mode = "slideshow"hold_time = 4
[[playlist.section.widget]]type = "baseball.promotions"demo = truelayout = "card"This is the exact widget block that rendered the hero GIF above (its full render config is baseball-promotions.toml, a [display] at default_scale = 4 with this one section). The layout = "card" line pins the held card on the wide panel — on a wide sign auto would run the crawl, so this shows off the richer card. It needs no team, since the fixture supplies its own promos — the fastest way to see the card shape without waiting for a home game.
One widget, every sign
Section titled “One widget, every sign”On a narrow sign each promo scrolls past as a classic line; a mid-width sign holds each promo as a card with paging dots; the longest sign runs a hi-res crawl of the same fields. You don’t pick the default: layout (omit it — the default "auto") resolves at draw time from your sign’s real physical width. The GIF above pins the held card (layout = "card") on a long panel — the richest of the three, and worth forcing on a wide sign if you prefer it to the crawl.
Not sure which you have? Most short DIY builds are smallsign (scale-1); the 256- and 512-pixel-wide panels are bigsign and longboi — see the hardware guides. New here? Get a sign running first.
| Your sign | What you see |
|---|---|
| smallsign (~160×16, scale-1) | classic scrolling lines, one per promo (TOR Jun 22 · Retro Domer Hat Giveaway) |
| bigsign (~256px, scale>1) | a held card, one promo per story with paging dots |
| longboi (~512px, scale>1) | a hi-res crawl — the same fields as one continuous scrolling run |
Pin a shape explicitly with layout = "ticker" (force the crawl on any sign) or layout = "card" (force the held card). The held card’s anatomy: the date in amber, the promo name in white (auto-scrolling in a clipped band if it’s too long), a team chip plus VS <opponent> in violet, <offer> · BY <sponsor> in cyan, and the start time in amber. Either the offer or the sponsor half is dropped when the feed doesn’t supply it — the widget never invents copy.
The widget shows today’s promos when there’s a home game today, otherwise the next home game’s. Sponsor tails (“presented by …”) are stripped and near-duplicate feed entries collapsed. With nothing to show it falls back to a team-prefixed Next home game: Jun 22, No home games soon (road trip), or Opens <date> (offseason).
Options
Section titled “Options”team is required (unless demo = true); everything else is optional.
| Field | Type | Default | Notes |
|---|---|---|---|
team | str | required | MLB team abbreviation (e.g. "TOR"). Case-insensitive. Optional when demo = true. |
layout | "auto" / "ticker" / "card" | "auto" | Explicit override; omit to auto-select from real panel width — see One widget, every sign. |
highlight | list of str | [] | Case-insensitive substrings; matching promos render amber and sort first. Applies to every layout. |
filter | list of str | [] | If non-empty, only promos matching one of these substrings are shown. Applies to every layout. |
limit | int | 0 | Max promos shown (0 = no limit). Applied after highlight sorting, so highlighted promos are never the ones dropped. |
lookahead_days | int | 14 | How far ahead to look for the next home game with promotions. |
title | str | "<Team> Promos" | Section title override. |
timezone | str (IANA) | system local | Timezone governing the “Today” label and date formatting. See Scheduling. |
font_color | color / "rainbow" / "color_cycle" / "shimmer" / table | callout colors | RGB list tints promo names; the team prefix, date, and amber highlights keep their callout colors. Scale-1 only. See Color providers. |
font | str | "6x12" | Display font. A hires name needs font_size. Scale-1 only. |
bg_color | [r, g, b] | none | Background fill behind the messages. Scale-1 only. |
padding | int, px | 6 | Horizontal spacing after each line when scrolling. |
update_interval | int, seconds | 21600 (6 hours) | How often to poll the StatsAPI schedule feed for fresh promotions — see Data source. |
demo | bool | false | Render the fixed promo set instead of calling StatsAPI — see Try it in demo mode. |
schedule | table | none | Core visibility window — only show this widget during set hours. See Scheduling. |
Data source
Section titled “Data source”Promotions come from MLB’s StatsAPI schedule feed — free and keyless. The widget refreshes every update_interval seconds (21600, or 6 hours, by default — often enough to keep the “Today” label honest after midnight) and shows the target date’s promos: today’s home game if there is one, else the next home game within lookahead_days that has matching promos. A doubleheader whose two games each carry a promo shows both — one card or crawl segment per game, matching the per-game data the API returns.
If it doesn’t work
Section titled “If it doesn’t work”- Nothing shows up at all — a blank panel. Most likely the widget block isn’t nested inside a
[[playlist.section]]— a bare[[playlist.section.widget]]yields zero sections and a blank sign. Every snippet on this page shows the wrapper. Also rule out that thebaseballplugin isn’t installed (see the install step above), which usually surfaces asunknown widget type 'baseball.promotions'at config-load. - It says “Next home game” instead of a promo. The next home game within
lookahead_dayshas no promotions in the feed, or your team is on a road trip. Raiselookahead_daysto reach further ahead, or check the team’s schedule — not every home game has a giveaway. - My
filteris hiding everything.filteris a strict allow-list — only promos whose name contains one of its substrings show. Loosen or remove it; usehighlightinstead if you just want to emphasize some promos without dropping the rest. - A highlighted promo isn’t amber.
highlightmatches case-insensitive substrings of the promo name — check the substring actually appears in the feed’s wording (sponsor tails are stripped, so match on the promo name, not “presented by …”).
Haven’t tried it yet? Try it in demo mode above takes about two minutes, no key and no network. Implementation notes live in the baseball package README.
The rest of the baseball family
Section titled “The rest of the baseball family”The baseball plugin ships four more widgets that share this data and design. Put your promos in a game-day rotation next to baseball.scores for the live game, baseball.standings for the division race, the family showpiece baseball.statcast — the day’s longest home run drawing its own trajectory arc — and baseball.attendance for last night’s crowd. It also brings the baseball.roll* rolling-ball sprite transition and the :baseball.ball: emoji.