Reading The Public API
The public API provides anonymous, read-only JSON for Events, Venues, Artists, Series and Editions. A consumer needs the API base URL and an ordinary HTTP client. No account, API key, cookie or X-Wavemap-Viewer-Address header is required. The trusted edge supplies its own origin credentials and viewer information; those are operator concerns.
The Development endpoint is https://api.dev.wavemap.app. It can sleep, restart or enter maintenance and is not a production availability commitment. Use a configured base URL so an integration can move environments deliberately. Local development uses http://localhost:6002; see Public API Foundation for the disposable fixture and server setup.
Browser And Server Quick Start
Section titled “Browser And Server Quick Start”Modern browsers and Node support the same platform fetch call:
const apiOrigin = "http://localhost:6002"const url = new URL("/v1/venues", apiOrigin)url.search = new URLSearchParams({ locality: "Montreal", fields: "publicId,name,updatedAt,location", limit: "20",}).toString()
const response = await fetch(url, { credentials: "omit", redirect: "error" })if (!response.ok) throw new Error(`Public API returned ${response.status}`)const page = await response.json()console.log(page.data, page.links.next)Browser reads use public CORS. Do not send authenticated application cookies or proxy-only headers. For a server integration, store the base URL in application configuration and apply your own timeouts, concurrency limits and retry budget. This basic snippet surfaces failures; the bounded retry example below handles wake and throttling advice.
Repository examples live in apps/wavemap-public-api/examples/. They are readable platform-only modules, not a separately published SDK. Copy the needed modules together or import them from a local checkout. The browser demo and Node examples exercise the same collection logic, including a guard of at most 100 pages per traversal.
Select A Resource And Follow Its Links
Section titled “Select A Resource And Follow Its Links”Start with /v1/venues, /v1/artists or /v1/event-series to discover resources independently of their Events. Use the returned publicId for detail and relationship routes. IDs are opaque and case-sensitive: do not construct them from names or internal database IDs. Collections default to 20 rows and permit at most 50 per page. Use fields to request only needed public fields; publicId remains present. Unsupported fields, includes and filters produce validation errors.
The resource guide describes every traversal and its supported filters. Event calendars can select a Venue and a local-date window using venueId, startLocalDateGte and startLocalDateLt. include=artists,venues adds the requested related summaries; it does not turn a summary into an unlimited nested collection. Follow each relationship’s own continuation metadata when present.
Series expose structureCode. A direct-events Series has Events at /v1/event-series/{seriesId}/events. An editions Series first exposes /v1/event-series/{seriesId}/editions; select an actual Edition and read /v1/event-series/{seriesId}/editions/{editionId}/events. An official Venue relationship is separate from the Venues at which Events happen. Do not infer one from the other, or query a direct-Series program as an Edition program.
For pagination, resolve links.next against the API origin and follow it unchanged until it is null. Keep filters and sort fixed for the traversal. Do not decode, edit or persist a cursor as a durable synchronization checkpoint. Guard the destination against a different origin or collection path, deduplicate by publicId, and commit a candidate collection only when every page succeeds. A failed later page must leave the previous collection intact.
Conditional Reads And Refresh
Section titled “Conditional Reads And Refresh”The API returns an ETag for a successful representation. Keep that validator together with the response body for the exact request URL, including query parameters. Send If-None-Match on a later read of that same URL. A 304 has no JSON body: reuse the matching cached body. A validator without its body cannot reconstruct data. Browser HTTP caching may perform this reuse automatically; the repository examples use cache: "no-store" to make their explicit per-URL cache behavior visible.
For incremental candidates, request sort=updatedAt, include updatedAt in fields, and pass an inclusive updatedSince lower bound. Preserve returned six-digit fractional timestamps as strings; converting through a JavaScript Date loses microseconds. Advance the checkpoint from returned markers only after complete traversal. An empty page, HTTP Date, cache Age or client clock is not progress.
Each resource owns its own marker. A Venue rename or Series membership change does not necessarily advance the Event marker. Related summaries can change while an Event’s updatedAt stays the same. Refresh those resources independently and periodically traverse the complete selected collection to reconcile membership. Missing incremental results are not deletions; a successful complete reconciliation can remove IDs absent from the selected view. Handle detail 404 as an observation that the addressed resource is unavailable, not as a reason to retry indefinitely.
The Event consumer demonstrates a five-minute overlap and periodic full reconciliation:
import { createVenueEventsConsumer } from "./examples/consumer.mjs"import { createRetryingExampleFetch } from "./examples/retry.mjs"
const controller = new AbortController()const consumer = createVenueEventsConsumer({ apiOrigin: "http://localhost:6002", venueId: "Venue1234567", // Replace with an ID returned by Venue discovery. startLocalDateGte: "2026-01-01", startLocalDateLt: "2027-01-01", fetchImplementation: createRetryingExampleFetch({ signal: controller.signal }),})
const initial = await consumer.reconcile()const refreshed = await consumer.refresh()// Reconcile again periodically and after a long outage; persist state only after success.console.log(initial.events, refreshed.events)That finite overlap is an example policy, not a lossless change feed. A transaction can commit later than the overlap with an older marker. Pagination is not a cross-request database snapshot. Full reconciliation is still required, especially for removals, cancellations, filter membership and independently changed relationships. Keep cancellation status in your selected fields when your UI distinguishes cancelled Events from active ones; do not equate cancellation with deletion.
Wake, Throttling And Failures
Section titled “Wake, Throttling And Failures”An asleep Development host returns 503 with Retry-After: 60 and Cache-Control: no-store while a qualifying read requests wake. Your client decides whether to wait and retry. Waking is not guaranteed to finish at sixty seconds, and maintenance can return the same status without allowing wake. A retryable response is advice, not a promise of eventual success.
The retry.mjs helper retries GET 429 and 503 responses only when Retry-After is usable. It honors seconds or an HTTP date, makes at most three attempts within a 150-second request budget, supports cancellation while waiting, and returns the final failure for the caller to display. It leaves 304 for the caller’s body cache, rejects writes, and does not retry malformed requests, missing resources or network exceptions. Its limits are sample client policy, not server guarantees. Large integrations should also coordinate retries across workers to avoid synchronized bursts.
Preserve your last successful view when refresh fails and show when it was last refreshed. Let a user retry later after the bounded attempt fails. Do not poll readiness or health as a substitute for a resource read; those probes intentionally do not wake or retain the Development host.
Compatibility And Data Use
Section titled “Compatibility And Data Use”Use the /v1 routes and the schema-derived reference for the currently implemented contract. Tolerate additive response fields, treat IDs as opaque, and validate the fields your integration actually needs. Development can change; a production compatibility and deprecation commitment has not been approved. Pin and review the OpenAPI artifact used to generate a client instead of silently regenerating it against an unknown revision.
Public read access does not establish a data or media reuse license. Reuse terms, attribution requirements and production support expectations remain release decisions. Do not infer permission to republish images or other third-party material merely because metadata contains a public URL. The API intentionally omits private account and internal database information; integrations should use only the exposed public fields.
This guide follows the executable examples and public request/response schemas. Update it when those contracts, retry behavior or environment policy changes. Maintainers can run the focused consumer tests described in Public API Foundation; operators own the runtime, maintenance and recovery path.