diff --git a/README.md b/README.md
index f8be443..f5f22de 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
Live **PRT (Planet Renox Time)**, milliseconds since `1998-03-14T03:00:00.000Z`, in every [ortus](https://github.com/planetrenox/ortus) unit (`ms s m h d w`), over a dot map of Earth lit by the real-time sun.
-Also on the page: PRT day/week progress, upcoming round-number milestones, next equinox/solstice, a subsolar marker with hover readout (lat/lon, sun altitude, solar time), and a Date ⇄ PRT converter.
+Also on the page: PRT day/week progress, upcoming round-number milestones, next equinox/solstice, a subsolar marker with hover readout (lat/lon, sun altitude, solar time), a Date ⇄ PRT converter, and a live API explorer.
## API
diff --git a/app.js b/app.js
index ad7fb82..fb69259 100644
--- a/app.js
+++ b/app.js
@@ -204,3 +204,32 @@ $('cin').value = '1970-01-01T00:00:00.000' // Unix epoch, shows PRT going negati
$('cin').oninput = $('cz').onchange = conv
$('pin').oninput = $('pu').onchange = pconv
conv(), pconv()
+
+// API explorer
+const EPS = [
+ ['/api/now', '/api/now', 'Current PRT in every unit, JSON'],
+ ['/api/now/d', '/api/now/{unit}', 'One unit, plain text'],
+ ['/api/from/2010-06-01', '/api/from/{date}', 'PRT at an ISO date or Unix ms'],
+ ['/api/to/1000000000000', '/api/to/{n}/{unit?}', 'Date at a PRT value, unit defaults to ms'],
+ ['/api', '/api', 'Epoch, units, endpoint list'],
+]
+$('eps').innerHTML = EPS.map(([p, s, d]) => `
`).join('')
+const esc = s => s.replace(/&/g, '&').replace(/ esc(s).replace(/("[^"]*")(\s*:)?|(-?\d+(?:\.\d+)?(?:e[+-]?\d+)?)/gi, (m, q, k, n) => q ? (k ? `${q}${k}` : `${q}`) : `${n}`)
+let ep = EPS[0][0]
+const tryEp = async p => {
+ ep = p
+ document.querySelectorAll('[data-ep]').forEach(b => b.classList.toggle('on', b.dataset.ep == p))
+ $('rq').innerHTML = `GET ${esc(p)}`
+ try {
+ const r = await fetch(p), t = await r.text()
+ if (ep != p) return
+ let o
+ try { o = JSON.stringify(JSON.parse(t), null, 2) } catch { o = t.trim() }
+ $('rq').innerHTML = `GET ${esc(p)} ${r.status}`
+ $('rs').innerHTML = hl(o)
+ } catch (e) { ep == p && ($('rs').innerHTML = `${esc(e.message)}`) }
+}
+$('eps').addEventListener('click', e => { const b = e.target.closest('[data-ep]'); b && tryEp(b.dataset.ep) })
+$('curl').addEventListener('click', () => navigator.clipboard?.writeText(`curl ${location.origin}${ep}`).then(() => toast('Copied curl')))
+new IntersectionObserver((es, io) => es[0].isIntersecting && (io.disconnect(), tryEp(EPS[0][0]))).observe($('api'))
diff --git a/index.html b/index.html
index 6aea3d1..4e80c93 100644
--- a/index.html
+++ b/index.html
@@ -22,7 +22,7 @@
PRT
-
+
@@ -62,6 +62,17 @@
+
+
API
+
Free JSON over HTTPS. No key, CORS open. Pick an endpoint to try it live.