Embedding in a web page
The API answers browser requests from any origin (Access-Control-Allow-Origin: *), so a studio's website can show live schedules and prices with a few lines of JavaScript and no server of its own.
Use a publishable key
Anything in a web page is public, including the key. Use a publishable key (hg_pk_…), which exists for exactly this. Never put a secret key (hg_sk_…) in a page, an app bundle or a public repository.
The pattern
<section>
<h2>Class schedule</h2>
<div id="schedule">Loading…</div>
</section>
<script>
const KEY = 'hg_pk_…'
fetch('https://go.hotglue.app/api/v1/classes?limit=50', { headers: { 'X-API-Key': KEY } })
.then(r => r.ok ? r.json() : Promise.reject(r.status))
.then(({ data }) => {
const el = document.getElementById('schedule')
if (data.length === 0) { el.innerHTML = '<p>New classes coming soon.</p>'; return }
el.innerHTML = data.map(c => `
<article>
<h3>${esc(c.name)}</h3>
<p>${esc(c.schedule_description ?? '')}</p>
<p>${esc(c.price_display)}</p>
<a href="${c.links.checkout}">Enroll</a>
</article>`).join('')
})
.catch(() => { document.getElementById('schedule').innerHTML = '<p>Schedule temporarily unavailable.</p>' })
function esc(s) { return String(s).replace(/[&<>"']/g, ch => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' })[ch]) }
</script>
Three states, always: loading, empty, and error. A studio with no classes yet should see a friendly line, not a blank box; a network blip should not leave "Loading…" on the page forever.
Rendering tips
- Escape text you insert into HTML (names, descriptions). The studio typed them; treat them as untrusted. The
links.*URLs are built by HotGlue, not typed by the studio, and are safe to put straight into anhref. - Use the ready-made fields.
schedule_descriptionandprice_displayare formatted the way the studio shows them.links.*are complete URLs: send visitors there instead of building HotGlue URLs by hand. - Null is normal.
category,location,description,featured_image_urland others are null when the studio hasn't set them, and null is the only blank you'll see (never""). Usec.location?.name. - Hide finished classes. A class stays listed until the studio archives it, even after its
end_date. Passends_after=<today>on/classesunless you want history. - Don't invent fields. If it isn't in the reference, it isn't there.
- Capacity is live.
capacity.spots_remainingcounts seats as of the request. Show "3 spots left", not a number you saved yesterday.
Caching and rate limits
Responses are private, no-store, so the browser won't cache them and neither does any CDN. Each page view costs one request per endpoint you call. That is fine for a schedule page; a widget that refetches every few seconds is not, and the 300-requests-per-minute-per-IP limit will stop it. If you need to go higher, cache on your own server with a secret key.