Free forever·no account, no tracking, no third-party requests ·open data, free API
showstack / build

Build something on it

The whole dataset is plain JSON at a fixed URL with permissive CORS. No key, no account, no rate limit, no tracking, and it will still be there when this site is not — the same files ship inside the npm and PyPI packages and the repository. Here is what is in it and eight things you could ship this week.

Everything is CC BY 4.0. Use it commercially, embed it in a product, print it on a wall. The only ask is attribution and, if you fix something, a pull request so the next person gets it too.

The endpoints

Five collections, one index, and the gaps

Static files, cacheable at the edge, and they work from a file on a USB stick backstage with no network at all.

EndpointWhat it holdsEntries
/api/v1/index.jsonCounts, build date and the list of endpoints. Start here.—
/api/v1/protocols.jsonWire protocols with ports, transports, multicast behaviour, gotchas and sources.92
/api/v1/standards.jsonStandards with the publishing body, scope, jurisdiction and whether it is free to read.103
/api/v1/software.jsonSoftware with licence, platforms, price model and — the useful field — what it speaks.109
/api/v1/hardware.jsonHardware with category, form factor, physical ports and what it speaks.108
/api/v1/terms.jsonBilingual EN / 繁中 vocabulary with domain, regional variants and false friends.144
/api/v1/gaps.jsonEvery field we know is missing, by entry. The to-do list, published.—
/showstack.jsonAll of the above in one file, if you would rather fetch once.556
/llms.txtA plain-text summary for language models and anything else that reads prose.—
speaks
On software and hardware: [{protocol, direction, note, confidence}]. The single most useful field, and the one to check before you promise an integration.
default_ports
On protocols: [{number, transport, role, iana_registered}]. This is what the port pages and the CLI are built from.
confidence
Everywhere: verified checked against the primary standard or real hardware, reported documented by a credible source, unverified community knowledge. Filter on it.
sources
Everywhere, and never empty: [{title, url, publisher, accessed, primary}]. An entry with no source would be an opinion.
gotchas
Free text, and the part practitioners actually read. Short, specific, and written by people who got caught by it.
id
A stable slug. It is the URL, the filename and the join key, and it does not change once published.

Start in ten seconds

The smallest possible thing that works

# in a terminal
curl -s https://showstack-inky.vercel.app/api/v1/protocols.json | jq '.[] | select(.id=="sacn") | .default_ports'

# in a browser, on any page, with no build step
const p = await (await fetch('https://showstack-inky.vercel.app/api/v1/protocols.json')).json()
const sacn = p.find(x => x.id === 'sacn')

# or install it and never make a network call at all
npm i showstack        # the dataset ships inside the package
pip install showstack
note
The data is bundled into the packages, not fetched by them. That is deliberate: a show machine on an isolated network still gets the whole index, and a build that pinned a version keeps working when this site does not.

Ready kit

Eight things you could ship this week

Each of these is short enough to read in full. None of them needs an account, a server, or permission.

for a show technician

A port lookup on your phone, offline

The question you actually have at 1am is "what is on udp 6454". One HTML file, no build, works from a USB stick.

<!-- one file. Open it from anywhere, including file:// -->
<input id="q" placeholder="6454"><pre id="out"></pre>
<script type="module">
const data = await (await fetch('https://showstack-inky.vercel.app/api/v1/protocols.json')).json()
q.oninput = () => {
  const n = +q.value
  out.textContent = data
    .filter(p => (p.default_ports ?? []).some(x => x.number === n))
    .map(p => `${p.name} — ${p.summary}`).join('\n\n') || 'nothing on that port'
}
</script>

Cache the JSON in a service worker or just save the file with the data inlined, and it works with the network unplugged.

for an integrator

Answer "can these two talk?" automatically

Cross-reference what two boxes speak. This is the question a rider is actually asking, and it is a set intersection.

const [hw, sw] = await Promise.all([
  fetch('https://showstack-inky.vercel.app/api/v1/hardware.json').then(r => r.json()),
  fetch('https://showstack-inky.vercel.app/api/v1/software.json').then(r => r.json()),
])
const speaks = e => new Set((e.speaks ?? []).map(s => s.protocol))

const a = hw.find(x => x.id === 'grandma3-full-size')
const b = sw.find(x => x.id === 'disguise-designer')
const shared = [...speaks(a)].filter(p => speaks(b).has(p))

console.log(shared)  // the protocols both ends actually claim

Add a confidence filter and you can separate "documented by the vendor" from "somebody on a forum said so", which is usually the difference that matters.

for a lighting programmer

A console macro that checks a patch

Most consoles script in Lua or Python. Load the dataset once at startup and you can validate a universe against what a protocol actually allows.

# Python, inside TouchDesigner, a console plugin, or a check script
import json, urllib.request

url = "https://showstack-inky.vercel.app/api/v1/protocols.json"
protocols = json.load(urllib.request.urlopen(url))
sacn = next(p for p in protocols if p["id"] == "sacn")

print(sacn["universe_model"])   # how universes are numbered
for g in sacn.get("gotchas", []):
    print("!", g)             # the things that bite, printed at load-in

Print the gotchas into the show log on startup. It costs nothing and it puts the warning where somebody will read it.

for a production manager

A spreadsheet of everything, refreshed

Sheets and Excel both fetch JSON. Point one at the index and you have a live equipment reference nobody has to maintain.

// Google Apps Script, bound to a sheet
function refresh() {
  const rows = JSON.parse(UrlFetchApp.fetch(
    'https://showstack-inky.vercel.app/api/v1/hardware.json').getContentText())
    .map(h => [h.name, h.vendor, h.category, h.form_factor ?? '',
               (h.speaks ?? []).map(s => s.protocol).join(', ')])
  const sheet = SpreadsheetApp.getActiveSheet()
  sheet.clear()
  sheet.getRange(1, 1, rows.length, 5).setValues(rows)
}

Set it on a weekly trigger. The sheet stays current and nobody has to remember to update it.

for a developer

A Companion module, or any control surface

Bitfocus Companion modules are Node. A button that shows what a device speaks, or that looks up a port, is about twenty lines.

// Node — no dependency beyond the runtime
import data from 'showstack/showstack.json' with { type: 'json' }

export const lookupPort = (n) =>
  data.protocols
    .filter(p => (p.default_ports ?? []).some(x => x.number === n))
    .map(p => ({ id: p.id, name: p.name, roles:
      p.default_ports.filter(x => x.number === n).map(x => x.role) }))

// bundled, so the surface works on an isolated show network

If you build one, say so on the repo and it goes on the list.

for an AI tool

A tool definition a model can actually call

The dataset is small, structured and cited, which makes it a good grounding source. Give a model the lookup rather than the whole file.

// A tool definition — the model calls this instead of guessing
{
  name: 'showstack_lookup',
  description: 'Look up a live-entertainment protocol, standard, ' +
    'software or hardware entry. Returns cited data, never a guess.',
  input_schema: {
    type: 'object',
    properties: {
      collection: { type: 'string',
        enum: ['protocols', 'standards', 'software', 'hardware', 'terms'] },
      id: { type: 'string', description: 'the slug, e.g. "sacn"' },
    },
    required: ['collection', 'id'],
  },
}

Every entry carries its sources, so a model using this can cite rather than assert — which is the specific failure mode described on the AI page.

for a student or teacher

A quiz, a poster, a revision deck

The glossary is bilingual and domain-tagged, which makes it a ready-made deck. This is a flashcard generator in nine lines.

const terms = await (await fetch('https://showstack-inky.vercel.app/api/v1/terms.json')).json()

const deck = terms
  .filter(t => t.domain === 'lighting')
  .map(t => ({ front: t.en, back: t.zh_hant ?? t.definition, tag: t.domain }))

// export as CSV for Anki, or render straight onto a page
console.log(deck.map(c => `"${c.front}","${c.back}"`).join('\n'))

CC BY 4.0 means you can print it, sell the printout, and put it on a classroom wall. Attribution is the only condition.

for anyone

Find what is missing, and fill one field

The gaps file is the to-do list, published. Pick a gap in something you own and you have a five-minute contribution with your name on it permanently.

curl -s https://showstack-inky.vercel.app/api/v1/gaps.json | jq -r '
  .[] | select(.collection=="hardware")
      | "\(.id): missing \(.missing | join(", "))"' | head

# then edit one file in the browser:
# https://github.com/deliseph/showstack/edit/main/data/hardware/<id>.yaml

Every entry has an Edit this entry link at the bottom. One file, one field, one pull request, and your handle goes on it.

The rules

What you may do with it

CC BY 4.0 The data. Use it commercially, modify it, redistribute it. Credit showstack and link back.

MIT The code in the repository, including the generators and the calculators.

no key No account, no rate limit, no tracking, no analytics on the API. If you hammer it you are only hurting a CDN.

Two honest requests rather than conditions. Pin a version if you depend on it — the packages exist for exactly that, and the shape of the data is versioned in the path. And send corrections back: if you found something wrong while building on it, you are the best-placed person on earth to fix it, and it is one file.

Built something?

Open an issue and it goes on the list, whatever it is — a macro, a spreadsheet, a Companion module, a poster on a workshop wall. Tell us about it. And if a field you needed was empty, say which one — that is more useful than almost any other feedback.