FMOps developer resources
Last updated: 27 August 2026.
The short version. Everything FMOps publishes for machines lives at a predictable URL on https://fmops.ie. The site index is /llms.txt, the full text is /llms-full.txt, the API is described by /openapi.json and catalogued at /.well-known/api-catalog, there is a CLI (npx @fmops/cli slots), and every page will hand you markdown instead of HTML if you ask for it. Nothing here needs a key.
Machine-readable files
| URL | Type | What it is |
|---|---|---|
| /llms.txt | text/plain | The whole site as a list: every page with a one-line summary, plus the facts about the business. Start here. |
| /llms-full.txt | text/plain | The full text of every page in one file. |
| /openapi.json | application/json | OpenAPI 3.1 description of the public API below. |
| /pricing.md | text/markdown | Every published price, in markdown. |
| /sitemap.xml | application/xml | Sitemap with a last-modified date on each page. |
| /robots.txt | text/plain | Crawl rules. Every named AI crawler is allowed; only /api/ is closed. |
| /.well-known/api-catalog | application/linkset+json | RFC 9727 API catalogue: the spec, the docs, the terms and the contact for the API, as one linkset. |
/md/<page>.md | text/markdown | The markdown mirror of each page, generated from the same HTML at build time. Reach it through Accept negotiation rather than linking to it directly. |
Markdown from any page
fmops.ie follows the acceptmarkdown.com convention. Ask any page for text/markdown and you get the page content as markdown from the same URL, with the navigation, styles and scripts stripped out. Browsers are unaffected: they ask for text/html and get the page as normal.
curl -s -H "Accept: text/markdown" https://fmops.ie/automation
Responses carry Content-Type: text/markdown; charset=utf-8 and Vary: Accept, Accept-Encoding, so a cache can never hand you the wrong variant. A request that will accept neither HTML nor markdown gets 406 Not Acceptable. A path that does not exist gets a real 404 with a markdown body that points you at the files above, never a 200 with an app shell.
curl -sI -H "Accept: text/markdown" https://fmops.ie/about
curl -s -o /dev/null -w "%{http_code}\n" https://fmops.ie/no-such-page # 404
The public API
Three endpoints are open to anyone. No key, no sign-up, and Access-Control-Allow-Origin: * on all of them, so they work from a browser. The full description, including every field, is in /openapi.json. Call the versioned paths: the unversioned form still works and always will, but /api/v1/… is the one to pin.
| Endpoint | What it returns |
|---|---|
GET /api/v1/slots | How many founding AI-visibility pilot slots are left. |
GET /api/v1/testimonials | Client testimonials that the client agreed to publish. Newest first. |
GET /api/v1/ai-check?id=<uuid> | A finished AI visibility report. Add &format=html for the rendered version. |
POST /api/v1/ai-check | Runs a free AI visibility check for a business. Five per IP address per hour. |
curl -s https://fmops.ie/api/v1/slots
curl -s https://fmops.ie/api/v1/testimonials
Everything else under /api/ is administrative and needs an X-Admin-Token header. Those endpoints are deliberately undocumented and are not open to the public.
Versioning and deprecation
Every public endpoint is served from a version prefix: /api/v1/…. That is the path to pin. The unversioned form, /api/slots, is a permanent alias of the current major version, kept working for callers written before the prefix existed.
Every API response carries API-Version: 1, so a client can assert on the version it is actually talking to rather than the one it meant to call.
The rules, so you can decide whether this is safe to build on:
- A breaking change gets a new prefix.
/api/v2/…. It never changes what an existing prefix means. - Additive changes happen inside v1. A new field, a new endpoint, a new optional parameter. Ignore fields you do not recognise and nothing will break.
- Nothing is removed without 12 months' notice.
When a version or an endpoint is on its way out, it is announced on the responses themselves, not only in prose:
| Header | What it means |
|---|---|
Deprecation: @<unix-timestamp> | RFC 9745. The moment the deprecation was announced. Present on every response from the deprecated endpoint. |
Sunset: <HTTP-date> | RFC 8594. The date the endpoint stops answering. At least 12 months after the Deprecation date. |
Link: <…>; rel="deprecation" | The note explaining what to move to. |
Nothing is deprecated today, so no response carries those headers yet. An unknown version answers 404 with code: "not_found" and a hint naming the versions that do exist.
curl -sI https://fmops.ie/api/v1/slots | grep -i '^api-version'
curl -s https://fmops.ie/api/v9/slots # 404, JSON, tells you v1 is what exists
Rate limits
Every response carries its own budget, so you can pace yourself instead of discovering the limit by hitting it. Both the IETF draft headers and the older trio are sent, because clients read one or the other.
| Header | Example | What it means |
|---|---|---|
RateLimit-Policy | "default";q=600;w=3600 | The quota: 600 requests in a 3600-second window. Exact even on a cached response. |
RateLimit | "default";r=597;t=3421 | Live state: 597 left, window resets in 3421 seconds. |
RateLimit-Limit | 600 | The same quota, in the older form. |
RateLimit-Remaining | 597 | Requests still allowed in this window. |
RateLimit-Reset | 3421 | Seconds until the window resets. |
Retry-After | 600 | Seconds to wait. Sent on every 429. |
The budgets: 600 reads an hour per IP address, and 5 checks an hour per IP address for POST /api/v1/ai-check, because each one asks several AI assistants a set of real questions.
One caveat worth knowing rather than finding out: read endpoints are cached at the edge for 60 to 300 seconds, so on a cache hit the live counters can be that much stale. RateLimit-Policy is always exact.
Errors
Every error is application/json, whatever went wrong — including a request to a path under /api/ that does not exist. You will never get an HTML error page or a bare NOT_FOUND from the hosting platform, because there is nothing useful in either for a program.
{
"ok": false,
"code": "not_found",
"message": "No endpoint at /api/pricing.",
"error": "No endpoint at /api/pricing.",
"hint": "Public endpoints are versioned: try /api/v1/pricing. The full list is in https://fmops.ie/openapi.json.",
"status": 404,
"docs": "https://fmops.ie/developers",
"spec": "https://fmops.ie/openapi.json"
}
Branch on code. It is the contract; message is written for a person and may be reworded. hint says what to do next. details appears on invalid_request and names the fields that were wrong.
| Code | Status | What to do |
|---|---|---|
invalid_request | 400 | Fix the fields named in details and retry. |
unauthorized | 401 | An administrative endpoint. There is no public sign-up; nothing behind it is part of the public API. |
not_found | 404 | No such endpoint, or no such record. Check /openapi.json. |
method_not_allowed | 405 | Use the method the spec lists for that path. |
rate_limited | 429 | Wait Retry-After seconds. |
server_error | 500 | Retry once. If it persists, email fionn@fmops.ie with the time of the request. |
unavailable | 503 | A dependency is down or unconfigured. Retry later, or email and it gets done by hand. |
The command-line tool
@fmops/cli wraps the API above so you do not have to write an integration to script against it. No dependencies, no key, no configuration.
npx @fmops/cli slots
npx @fmops/cli check "Riverside Physiotherapy" physiotherapist Waterford --email you@example.ie
npx @fmops/cli page /automation # any page, as markdown
npx @fmops/cli doctor # reachability and the current rate-limit budget
Everything prints JSON on stdout except page and llms, which print markdown and plain text, so the output pipes into jq or into an agent. A failed request prints the API's own code, message and hint on stderr and exits non-zero. fmops help lists every command.
What FMOps does not publish
Being straight about the gaps saves you the search. There is no MCP server, no webhook subscription API, no OAuth, no SDK for any language beyond the CLI above, and no sandbox. If you need one of those for a real project, say so and I will tell you honestly whether it is worth building.
Terms and contact
Use of these endpoints is covered by the terms of service. Be reasonable with request rates: this is a one-person business on modest hosting. The exact budgets, and the headers that report them, are in Rate limits above.
API and developer questions go to fionn@fmops.ie, or see the contact page for phone and other options. FMOps is run by Fionn Murphy from Waterford, Ireland.