---
title: "API"
url: "https://michalak.world/api/"
description: "Every page on michalak.world is published as JSON, Markdown, and plain text over anonymous HTTP GETs, described by an OpenAPI 3.1 document and listed in an RFC 9727 API catalog."
summary: "The machine-readable endpoints this site publishes: a JSON index of every page, a Markdown twin of each page, and two plain-text digests, all listed in an RFC 9727 API catalog."
date: 2026-08-18
lastmod: 2026-08-18
author: "Michael Michalak"
site: "https://michalak.world/"
---

# API

Every page on michalak.world is published as JSON, Markdown, and plain text over anonymous HTTP GETs, described by an OpenAPI 3.1 document and listed in an RFC 9727 API catalog.

Every published page on this site has a machine-readable twin alongside its
HTML, so an agent, script, or LLM never has to scrape a rendered page to read
it. Everything below is an anonymous `GET`: no key, no signup, no request body,
and `Access-Control-Allow-Origin: *` on every response so browser-based agents
can read them too.

If you would rather not learn any of these URLs, ask for Markdown by name and
any page will answer in it:

```bash
curl -s https://michalak.world/about/ -H "Accept: text/markdown"
```

## Content negotiation

Send `Accept: text/markdown` and a page returns its Markdown instead of its
HTML, at the page's own URL. Nothing needs to be appended and nothing needs to
be discovered first — if you have a link to a page, you can read it as Markdown.
Browsers ask for `text/html` ahead of anything else, so they still get the
rendered page.

The response is `text/markdown; charset=utf-8`, carries `Vary: Accept` so caches
keep the two representations apart, and includes two headers worth reading:

| Header | Meaning |
| --- | --- |
| `x-markdown-tokens` | Estimated tokens in the response, for sizing a context window or picking a chunk size. |
| `Content-Location` | The URL of the Markdown itself, if you want to cache or cite it directly. |

This is served from the same file you would get by appending `index.md`, so the
two routes are interchangeable — use whichever fits your client. Pages that have
no Markdown twin, such as the numbered pages of a paginated list, fall back to
HTML rather than erroring.

## Endpoints

| Endpoint | Returns |
| --- | --- |
| [`/index.json`](/index.json) | Every page in one JSON array: title, description, summary, full body text, tags, and dates. |
| `/{page}/index.md` | That page's source Markdown, headings and code fences intact. Append `index.md` to any page URL, or ask the page itself for `text/markdown`. |
| [`/llms.txt`](/llms.txt) | A curated map of the site in the [llmstxt.org](https://llmstxt.org) shape: expertise, key pages, every article with a description. |
| [`/llms-full.txt`](/llms-full.txt) | The full text of every page concatenated into one response. |
| [`/api/openapi.json`](/api/openapi.json) | OpenAPI 3.1 description of everything in this table. |
| [`/api/status.json`](/api/status.json) | Which build the CDN is currently serving. |
| [`/.well-known/api-catalog`](/.well-known/api-catalog) | The [RFC 9727](https://www.rfc-editor.org/rfc/rfc9727) catalog listing these APIs. |

[`/sitemap.xml`](/sitemap.xml) and [`/index.xml`](/index.xml) are published in
the usual places as well.

## Start here

Read the catalog to find the APIs, then the OpenAPI document to find their
shapes:

```bash
curl -s https://michalak.world/.well-known/api-catalog | jq '.linkset[].anchor'
curl -s https://michalak.world/api/openapi.json | jq '.paths | keys'
```

Ingest the whole site in one request, or take a single page as Markdown:

```bash
curl -s https://michalak.world/index.json | jq '.[] | {title, permalink, tags}'
curl -s https://michalak.world/about/index.md
```

The `permalink` field of each index entry is the path to append `index.md` to —
or to request with `Accept: text/markdown`, which returns the same document.
Either way a two-step crawl is straightforward: read the index, pick the pages
worth reading in full, fetch their Markdown.

## What to expect

Responses are static files served from Cloudflare's edge. They change only when
the site is rebuilt, so caching them for a few minutes costs nothing in
freshness — `/api/status.json` names the build you are being served if you need
to tell a live deployment from a stale copy.

There is no versioned URL prefix. The OpenAPI document carries a version, and
the response shapes described there will not change incompatibly without it
changing too; new fields may appear at any time, so parse defensively and ignore
what you do not recognize.

Nothing here is rate limited beyond ordinary CDN protection. AI crawlers are
welcome: [`/robots.txt`](/robots.txt) allows the training, search, and
user-initiated fetchers by name. Reuse is subject to the
[terms of use](/terms-of-use/), and attribution with a link back is appreciated.

## Discovery

The catalog is advertised three ways, so an agent can find it without guessing.
The well-known URI is one. A `HEAD` of the site root is another: it returns
`Link` headers for `api-catalog`, `service-desc`, `service-doc`, and
`describedby` without downloading a byte of HTML. And every page carries
`<link rel="api-catalog">` in its `<head>`, for an agent that landed mid-site
rather than at the front door.

Inside the catalog, each entry anchors one endpoint and carries the same three
links: `service-desc` to the OpenAPI document, `service-doc` to this page, and
`status`.

Questions, or something you want exposed that is not here? [Get in
touch](/contact/).

