Basics
Every endpoint below is a GET request to https://cwtch.es/api/public-api.php with an endpoint query parameter, returning JSON. Responses are open to cross-origin requests (CORS enabled), so you can call this directly from client-side JavaScript on your own site — no server-side proxy needed.
There's no authentication, no account, and no API key — anyone can use it. In exchange, there's a rate limit of 120 requests per hour per IP address across all endpoints combined. Going over it returns a 429 status with a Retry-After header. If you need more than that, please cache responses on your end rather than polling — none of this data changes more than a few times a day.
Every response includes a success boolean. On an error, you'll also get an error string explaining what went wrong, alongside an appropriate HTTP status code.
Embedding a tool on your own site
Every tool on cwtch.es runs entirely in the browser (nothing gets uploaded to us), which is exactly what makes it embeddable — the iframe below is doing the full generation/conversion/whatever itself, client-side, same as if someone visited the real page directly. Pick a tool on the embed-code page for ready-made, copy-paste snippets; here's how it works generally.
Option 1 — the widget loader (recommended)
Handles building the iframe and auto-resizing it to fit the tool's real content — a fixed guessed height looks wrong the moment a generated result makes the page longer or shorter.
<div data-cwtch-tool="qrcode"></div> <script src="https://cwtch.es/embed.js" async></script>
data-cwtch-tool takes a tool's id or filename (either works) — see the tools endpoint below for the full list. An optional data-height="900" sets the starting height before auto-resize kicks in.
Option 2 — a raw iframe
No script needed, but you're setting a fixed height yourself. Get the exact markup (and a sensible default height per tool) from the embed endpoint below, or just build it directly:
<iframe src="https://cwtch.es/qrcode.html?embed=1&credit=1" width="100%" height="700" style="border:0;border-radius:12px;" loading="lazy" title="QR Code Generator — cwtch.es"></iframe>
Add ?embed=1 to any tool page's own URL and it drops the site nav, footer, and ads. Add &credit=1 too and it leaves a small "cwtch.es" credit link in the corner — worth including when you're embedding a tool on your own site, since a visitor there might not otherwise know where it came from. Leave it off for a clean, credit-free view — that's what an OBS Browser Source link or similar wants. Listen for a {cwtchEmbed:true, height} message posted to window if you want auto-resize without embed.js.
Option 3 — oEmbed
Paste a tool's page URL into anything that already speaks oEmbed — WordPress, Notion, Discord/Slack unfurls — and it embeds automatically with zero setup, via the discovery link every tool page carries in its <head>. To call it directly yourself:
GET https://cwtch.es/api/oembed.php?url=https://cwtch.es/qrcode.html&format=json
{
"version": "1.0",
"type": "rich",
"provider_name": "Cwtch.es",
"provider_url": "https://cwtch.es/",
"title": "Free QR Code Generator",
"html": "<iframe src=\"https://cwtch.es/qrcode.html?embed=1&credit=1\" ...></iframe>",
"width": 600,
"height": 700
}
Also accepts format=xml, and maxwidth/maxheight to size the returned iframe.
Endpoints
?endpoint=tools
Every currently-enabled, publicly-listed tool on the site — the same list the sidebar nav is built from. Disabled or unlisted tools are excluded automatically.
{
"success": true,
"count": 112,
"tools": [
{
"id": "watermark-maker",
"name": "Watermark Maker",
"file": "watermark-maker.html",
"url": "https://cwtch.es/watermark-maker.html",
"category": "Image",
"subcategory": null
},
...
]
}
?endpoint=embed
Ready-to-use embed markup for one tool — both the raw <iframe> and the data-cwtch-tool snippet for the embed.js loader, plus a sensible default height and a direct oEmbed URL. This is exactly what powers each tool page's own "Embed" panel.
| Param | Required | Description |
|---|---|---|
tool | Yes | A tool id or filename, from the tools endpoint above. |
{
"success": true,
"tool": { "id": "qrcode", "name": "Free QR Code Generator", "file": "qrcode.html", "url": "https://cwtch.es/qrcode.html", ... },
"embed_url": "https://cwtch.es/qrcode.html?embed=1&credit=1",
"default_width": "100%",
"default_height": 700,
"auto_resize": true,
"iframe_html": "<iframe src=\"...\" width=\"100%\" height=\"700\" ...></iframe>",
"script_html": "<div data-cwtch-tool=\"qrcode\"></div>\n<script src=\"https://cwtch.es/embed.js\" async></script>",
"oembed_url": "https://cwtch.es/api/oembed.php?url=...&format=json"
}
?endpoint=stats
Anonymous, all-time usage counts per tool — the same numbers shown on the public stats page. Keyed by tool id.
{
"success": true,
"stats": {
"watermark-maker": 42,
"gif-maker": 1830,
...
}
}
?endpoint=changelog
The same content shown on the changelog page, grouped by month, newest first.
| Param | Required | Description |
|---|---|---|
limit | No | Return only the N most recent months (1–100). Omit for the full history. |
{
"success": true,
"months": [
{
"month": "September 2026",
"title": "...",
"sections": [ { "type": "launched", "label": "🚀 Launched", "items": ["..."] } ]
},
...
]
}
?endpoint=roadmap
Public entries from the roadmap / tool-request board — anything an admin has triaged past "pending", sorted by votes. Pending and declined requests are never included, matching what the board itself shows.
| Param | Required | Description |
|---|---|---|
status | No | One of approved, planned, in_progress, shipped, or all (default). |
{
"success": true,
"requests": [
{
"id": 14,
"title": "Add dark mode toggle",
"description": "...",
"status": "planned",
"votes": 23,
"submitted_at": "2026-09-10T14:22:00+00:00"
},
...
]
}
Example: fetch in JavaScript
fetch("https://cwtch.es/api/public-api.php?endpoint=tools")
.then(res => res.json())
.then(data => console.log(data.tools));
Something missing, or a question?
This API only exposes what's already public elsewhere on the site — if there's other public data you'd find useful as an endpoint, or something here doesn't behave as documented, let us know or add it to the roadmap board.