Live updates

Instead of polling /waits, open one connection: Parky sends the whole board once, then only what changes. The server checks every 5 seconds and pushes a ride only when Disney or Universal changes it; posted waits usually change every few minutes, so quiet minutes are normal.

WebSocket

Connect to wss://api.parkypass.com/v1/stream, wait for ready, then subscribe to up to 10 parks, or "*" for all of them. Send a key as ?key= or an Authorization header on the upgrade. This page keeps Magic Kingdom live:

HTML + JavaScript
<ul id="parky-waits"></ul><!-- Required: keep the "Live data from parkypass.com" link visible on every page that shows this data. --><a id="parky-credit" href="https://parkypass.com/">Live data from parkypass.com</a><script>  // Add ?key=pk_live_... to the URL for higher limits (optional).  const rides = new Map();  const list = document.getElementById("parky-waits");  const credit = document.getElementById("parky-credit");  const socket = new WebSocket("wss://api.parkypass.com/v1/stream");  function render(message) {    // Required: keep the "Live data from parkypass.com" link visible on every page that shows this data.    credit.href = message.attribution.url;    credit.textContent = message.attribution.text;    if (message.type === "snapshot") rides.clear();    for (const ride of message.rides) rides.set(ride.ride_id, ride);    list.replaceChildren(...Array.from(rides.values(), (ride) => {      const item = document.createElement("li");      item.textContent = ride.name + ": " + (ride.wait_minutes !== null ? ride.wait_minutes + " min" : ride.status);      return item;    }));  }  socket.onmessage = (event) => {    const message = JSON.parse(event.data);    // Subscribe only once the server says it is ready.    if (message.type === "ready") socket.send(JSON.stringify({ type: "subscribe", parks: ["magic-kingdom"] }));    if (message.type === "snapshot" || message.type === "changes") render(message);  };</script>

Key your rides by ride_id. A snapshot replaces everything for its park; each changes message carries only the rides that changed, each in full, so you can replace them as they come.

Messages you send

subscribe
Up to 10 parks, written any way {park} accepts, or "*" for every park.{"type":"subscribe","parks":["magic-kingdom","epcot"]}
unsubscribe
Stop following some parks.{"type":"unsubscribe","parks":["epcot"]}
ping
Optional; the server answers with pong.{"type":"ping"}

Messages Parky sends

Every message has type, attribution and generated_at.

ready
WebSocket only, on connect: authorized, send subscribe next.{"type":"ready","max_parks":10,"idle_timeout_seconds":120,…}
subscribed
Your current parks, after each subscribe; unsubscribed after each unsubscribe.{"type":"subscribed","parks":["magic-kingdom","epcot"],…}
snapshot
Once per park on subscribe: the whole board, in the same shape as /waits, plus park and park_key.{"type":"snapshot","park":"magic-kingdom","park_key":"magic-kingdom","data_status":"live","park_open":true,"rides":[…],"attribution":{…},…}
changes
Only the entries whose status, wait, Single Rider or Lightning Lane changed, each in full. The server checks every 5 seconds and pushes a ride only when Disney or Universal changes it; posted waits usually change every few minutes, so quiet minutes are normal.{"type":"changes","park":"magic-kingdom","park_key":"magic-kingdom","data_status":"live","rides":[…],"attribution":{…},…}
park
When a park opens or closes.{"type":"park","park":"magic-kingdom","park_key":"magic-kingdom","park_open":false,"data_status":"live",…}
ping
Every 25 seconds. Server-Sent Events send a ": ping" comment line instead.{"type":"ping",…}
error
A rejected subscription or connection, with the same codes as REST errors. The socket stays open unless a close follows.{"type":"error","error":{"code":"not_found","message":"…","docs_url":"…"},…}

Close codes

1000
idle: no subscriptions for 2 minutes.
1001
The server is restarting. Reconnect.
1008
invalid_key or rights_unavailable. Don't retry unchanged.
1011
Internal error. Reconnect with backoff.
1013
rate_limited, too_many_streams or upstream_unavailable. Retry later.

A socket with no subscriptions closes after 2 minutes, and a client that falls about 1 MB behind is dropped. Reconnect with a short backoff and subscribe again.

Server-Sent Events

For one park, /v1/parks/{park}/waits/stream sends the same messages from subscribed on, as unnamed data: lines, so onmessage receives them all. It adds retry: 5000 and a : ping comment every 25 seconds, and there is no ready.

HTML + JavaScript
<ul id="parky-waits"></ul><!-- Required: keep the "Live data from parkypass.com" link visible on every page that shows this data. --><a id="parky-credit" href="https://parkypass.com/">Live data from parkypass.com</a><script>  // Add ?key=pk_live_... to the URL for higher limits (optional).  const rides = new Map();  const list = document.getElementById("parky-waits");  const credit = document.getElementById("parky-credit");  const events = new EventSource("https://api.parkypass.com/v1/parks/magic-kingdom/waits/stream");  function render(message) {    // Required: keep the "Live data from parkypass.com" link visible on every page that shows this data.    credit.href = message.attribution.url;    credit.textContent = message.attribution.text;    if (message.type === "snapshot") rides.clear();    for (const ride of message.rides) rides.set(ride.ride_id, ride);    list.replaceChildren(...Array.from(rides.values(), (ride) => {      const item = document.createElement("li");      item.textContent = ride.name + ": " + (ride.wait_minutes !== null ? ride.wait_minutes + " min" : ride.status);      return item;    }));  }  // Server-Sent Events subscribe to the park in the URL; there is no ready message.  events.onmessage = (event) => {    const message = JSON.parse(event.data);    if (message.type === "snapshot" || message.type === "changes") render(message);  };</script>

Limits

Streams open at once: 2 per IP without a key, 10 per free key, 100 per partner key. Each connection counts once against the per-minute request limit. Over the limit, the connection is refused with too_many_streams.