/api/v1 is an unauthenticated, read-only JSON API with five endpoints, CORS open to everyone and a rate limit instead of a signup form. Here is every route and what it returns.
Novus Visualizers already publishes a machine-readable surface over MCP, which is exactly right if your client speaks JSON-RPC and useless if it does not. curl does not. Neither does a spreadsheet, a crawler, or the twelve-line script someone writes on a Tuesday. /api/v1 is the way in for all of those.
It is unauthenticated, read-only, rate-limited, and identical in shape on every Novus site, so one client can walk the whole estate from any entry point. There are no keys to request and no dashboard to sign up for, because there is nothing to meter beyond a request budget.
/api/v1 returns the index: the site name, its one-line description, the API version, a map of every other endpoint, and readOnly: true stated in the body./api/v1/health returns status, the deployed version and an ISO time. It is the only route that is never cached./api/v1/site returns the site's name, canonical URL, description, version, and the URL of its MCP endpoint./api/v1/capabilities returns a count and a list of what this site can do, each entry a name and a description./api/v1/siblings returns a count and every other Novus property, so a client that found one site can find them all.Responses are JSON, pretty-printed with a trailing newline, which means piping one straight into a terminal produces something a person can read without a formatter.
The obvious way to build a /capabilities endpoint is to write an array of endpoint descriptions by hand. That array is a second copy of the MCP tool catalog, and the two drift the first time a tool is renamed, silently, because nothing compares them.
So this one is derived. Each capability is an MCP tool's own name and its own description, taken verbatim from the catalog the site already built for its MCP server. On Visualizers that means you get list_capabilities, find_engine, get_engine, list_overlays and list_novus_sites over REST, with the same wording an AI client sees. Rename a tool and the REST endpoint renames it in the same commit, or a test fails.
A capability is not an endpoint
These entries describe what the site can tell you, not five more URLs you can GET. They are JSON-RPC tools with no page of their own, which is why none of them carries a link. To actually call one, use the MCP endpoint; to know what exists without speaking JSON-RPC, read this.
Every route answers GET and OPTIONS, and nothing else. Send a POST, PUT, PATCH or DELETE and you get 405 with a body of { "error": "method_not_allowed" }, the message This API is read-only. Use GET., and an Allow: GET, HEAD, OPTIONS header naming what would have worked.
That is enforced twice on purpose: the route files export no write verb at all, so the framework itself would refuse one, and the shared handler refuses a write verb even if a route ever exported it by accident. Either guard alone can be defeated by a single edit; both together cannot be defeated by one.
There is no admin layer either. No key issuance, no usage console, no per-caller quota you can raise. If those are ever wanted they are a product decision, not a quiet addition to a read endpoint.
Every response carries Access-Control-Allow-Origin: *, allows GET, OPTIONS, accepts a content-type request header and caches the preflight for a day. OPTIONS is exported alongside GET on every route, because without it a browser preflight would hit a framework 405 and every browser caller would fail before its real request.
The permissive origin protects nothing to give away, because there is nothing to protect: every byte served here is already public on the site's own pages, no response reads a row belonging to a person, and there is deliberately no Access-Control-Allow-Credentials, so a browser cannot be talked into attaching a session to one of these requests. Restricting the origin would block exactly the browser clients the API exists for.
The four cacheable routes send public, max-age=300, s-maxage=600, stale-while-revalidate=86400. A burst after expiry is served instantly from the edge while a single request refreshes behind it, which is what keeps a crawler from becoming the origin's problem. /health sends no-store, because a cached health check reports the past and a health check that reports the past is worse than none.
Beyond the cache there is a rate limit of 60 requests per minute, keyed on the caller and scoped per route. The scoping is the useful part: someone hammering /capabilities cannot exhaust the budget for /health and take the site's own monitoring down as collateral. Exceed it and you get a rejection carrying a Retry-After telling you how many seconds to wait.
Poll politely
Five minutes of freshness is baked into the cache headers, so polling faster than that mostly buys you the same bytes. For a status board, once a minute against /health is inside the budget with room to spare and is the only route that will ever tell you something new that quickly.
/api/v1/siblings is the endpoint that makes the rest of it worth building. It lists the portfolio hub plus every Novus app, and because the API's shape is identical on all of them, a client can request siblings from whichever site it happened to find, then request site and capabilities from each one in turn. Discovery from any entry point, in three request types, with no registry to keep in sync.
It will not render or export a visualizer. Every frame Novus produces is drawn in a visitor's own browser tab from audio that visitor chose, so there is no server holding a canvas to ask. It will not return your saved projects, your library or anything else behind a sign-in; those are per-visitor and are deliberately unreachable here. And it will not accept a write, ever, at this version.
If what you actually want is the catalog in depth (which modes an engine has, which controls follow the audio, what a scene costs to run), that is what the engine reference publishes for humans and what find_engine and get_engine answer for machines. For the terms used in both, the glossary is the shared vocabulary.
Reviewed by the editorial team. Report a correction.
Anonymous. Only this page’s address and your yes/no are recorded, and only if you allowed analytics cookies.
Ready to make your own?
Open the editor, drop in a track, and export a watermark-free video: free, no account.
Open the Editor