---
title: JSON API
description: The read-only JSON API every Blume site serves — page index, per-page documents, navigation, search — and the OpenAPI 3.1 description that lets function-calling frameworks build tools from it.
---

Every Blume site also serves its docs as a small read-only **JSON API** — the REST twin of the [MCP server](/docs/discoverability/mcp)'s tools, over the same page snapshot, for agents and function-calling frameworks that speak plain HTTP rather than MCP. It's on by default and needs no configuration:

| Endpoint | Returns |
| --- | --- |
| `/api/docs/pages.json` | Every page with its route, title, description, content type, locale, facets, and the URLs of its rendered, Markdown, and JSON forms. |
| `/api/docs/pages/{route}.json` | One page: its index entry plus the agent Markdown (the same body `get_page` returns). `{route}` is the page route without the leading slash, `index` for home. |
| `/api/docs/navigation.json` | The navigation tree — header tabs and the sidebar hierarchy. |
| `/api/docs/search?q=` | Full-text search, with the same `limit`, `contentTypes`, `locale`, `version`, and `filters[key]` scoping as `search_docs`. Server output only. |
| `/openapi.json` | The OpenAPI 3.1 description of the whole machine-readable surface. |

The page index, per-page documents, and navigation are prerendered, so a static site serves them as files from any host. Search is a live endpoint and exists only under [server output](/docs/deployment#server-rendering), where it runs the same index `search_docs` does.

## Errors

Errors are [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457) problem details (`application/problem+json`) with a stable `code`, a `detail`, and a `resolution` hint telling the agent where to go next — a missing page, a blank search query, or on server output any `/api/…` URL no endpoint answers:

```json
{
  "code": "API_ROUTE_NOT_FOUND",
  "detail": "No API route exists at /api/nope.",
  "instance": "/api/nope",
  "links": [
    {
      "href": "https://docs.example.com/openapi.json",
      "label": "OpenAPI description"
    },
    {
      "href": "https://docs.example.com/api/docs/pages.json",
      "label": "Page index"
    }
  ],
  "resolution": "Discover the available operations through the OpenAPI description at https://docs.example.com/openapi.json, or list every page at https://docs.example.com/api/docs/pages.json.",
  "status": 404,
  "title": "API route not found",
  "type": "about:blank"
}
```

## OpenAPI description

The **OpenAPI document** at `/openapi.json` is generated per build from your config, so it describes only what the deployed site serves: every JSON endpoint with a unique `operationId`, typed parameters, and response schemas, plus the text surfaces alongside — the [`.md` mirrors](/docs/discoverability/markdown), [`llms.txt`](/docs/discoverability/llms-txt) and `llms-full.txt`, [`agent-readability.json`](/docs/discoverability/agent-discovery#agent-readability) — and the [MCP endpoint](/docs/discoverability/mcp) when it's enabled. Frameworks that build tools from an OpenAPI description get the same reach an MCP client has. The document is linked from the [API catalog](/docs/discoverability/agent-discovery#api-catalog), the [readability manifest](/docs/discoverability/agent-discovery#agent-readability), the homepage `Link` header as `rel="service-desc"`, and `llms.txt`.

None of this touches your own [API reference](/docs/advanced/api-reference): a documented spec is rendered into pages, never served at `/openapi.json`, and the catalog lists both. A `public/openapi.json` you ship yourself takes over that route (the JSON endpoints stay). The `/api/…` catch-all steps aside when a docs section is served from the `/api` namespace (`content/api/overview.md`) or a custom page owns a rest route under `/api/`, so those pages keep winning.

## Turning it off

Set `ai.api` to `false` to publish none of it:

```ts blume.config.ts lineNumbers
ai: {
  api: false,
}
```
