Everything you need to call these tools from a script, a function-calling loop or an MCP client.
Every tool is one POST with a JSON body. There is no session, no handshake and no SDK.
curl -X POST https://YOUR-HOST/api/v1/summarize \
-H 'content-type: application/json' \
-d '{"url":"https://example.com","style":"bullets","targetWords":120}'Tools that produce a file stream the bytes directly:
curl -X POST https://YOUR-HOST/api/v1/markdown-to-pdf \
-H 'content-type: application/json' \
-o report.pdf \
-d '{"markdown":"# Q3 report\n\n- Revenue up 12%","theme":"print"}'…unless you ask for JSON, which is what you usually want inside an agent:
curl -X POST 'https://YOUR-HOST/api/v1/markdown-to-pdf?output=json' \
-H 'content-type: application/json' \
-d '{"markdown":"# Q3 report"}'
# => {"ok":true,"data":{"mime":"application/pdf","filename":"...","bytes":24518,"url":"https://…"}}JSON responses are always wrapped:
{ "ok": true, "tool": "summarize", "ms": 812, "data": { … } }Errors use the same shape, and retryable tells an agent whether trying again could help:
{
"ok": false,
"tool": "html-to-pdf",
"error": {
"code": "quota_exhausted",
"message": "Browser Rendering is at its free-plan limit…",
"retryable": true
}
}| Code | HTTP | Meaning | Retryable |
|---|---|---|---|
| invalid_input | 400 | A parameter failed validation. details lists the offending fields. | no |
| unsupported_media | 415 | The file type is not supported by the converter. | no |
| payload_too_large | 413 | The request body exceeded MAX_INPUT_BYTES. | no |
| rate_limited | 429 | Per-caller rate limit. Honour the Retry-After header. | yes |
| upstream_blocked | 403 | The target URL was refused by the SSRF policy (private address, non-http scheme). | no |
| upstream_failed | 502 | The target site or an upstream model returned an error. | yes |
| binding_unavailable | 503 | A required Cloudflare binding is not configured on this deployment. | no |
| quota_exhausted | 503 | The daily free allowance for Browser Rendering or Workers AI is used up. | yes |
| timeout | 504 | The page or model took too long. | yes |
| internal | 500 | Unexpected server error. | yes |
| Slug (REST) | Name (MCP) | Summary | Cost |
|---|---|---|---|
| html-to-pdf | html_to_pdf | Render raw HTML or a live URL into a print-ready PDF with real Chromium. | browser |
| screenshot | screenshot | Capture any URL or HTML string as PNG / JPEG / WebP, at any device size. | browser |
| fetch-page | fetch_page | Retrieve a page as a chosen user-agent — raw HTTP or fully JS-rendered. | free |
| scrape | scrape | Pull named fields out of a page with CSS selectors — deterministic, no AI tokens. | free |
| html-to-markdown | html_to_markdown | Strip the chrome, keep the article. Typically 80–90% fewer tokens than raw HTML. | free |
| document-to-markdown | document_to_markdown | PDF, DOCX, XLSX, CSV, ODT and images in — Markdown out. | AI |
| markdown-to-html | markdown_to_html | GitHub-flavoured Markdown to a styled, self-contained HTML document. | free |
| markdown-to-pdf | markdown_to_pdf | One call from model-generated Markdown to a shareable PDF. | browser |
| summarize | summarize | Compress text or a whole web page into Markdown at a length you choose. | AI |
| extract-data | extract_data | Turn messy text or a web page into JSON that matches your schema. | AI |
| translate | translate | Translate text between 100+ languages while keeping Markdown structure intact. | AI |
| describe-image | describe_image | Read text out of an image, or write alt text and captions for it. | AI |
The endpoint speaks MCP over Streamable HTTP and is completely stateless — POST JSON-RPC, get JSON back. GET /mcp returns 405 by design; there is no server-push channel to open.
{
"mcpServers": {
"ai-function-nodes": {
"type": "http",
"url": "https://YOUR-HOST/mcp"
}
}
}Supported methods: initialize, tools/list, tools/call, ping, plus empty resources/list and prompts/list so probing clients do not error.
Tool failures come back as isError: true inside a normal result (with a structured error in structuredContent) rather than as JSON-RPC protocol errors, because most clients surface protocol errors as a hard crash instead of something the model can react to.
If you are not using MCP, fetch ready-made tool definitions:
# OpenAI / Anthropic style tools[] array
curl https://YOUR-HOST/openai-tools.json
# Full OpenAPI 3.1 document
curl https://YOUR-HOST/openapi.json
# Compact catalog with JSON Schemas and examples
curl https://YOUR-HOST/api/v1/toolsThere is also /llms.txt, a plain-text briefing written for a model that has just been pointed at this host.
Every endpoint here is fully public: no account, no API key, nothing to sign in with. That also means the free-tier allowances below are shared by everyone hitting the deployment, so the only access control is a per-IP rate limit — not identity, just "slow down."
| Resource | Free allowance | What we do about it |
|---|---|---|
| Browser Rendering | 10 min/day, 3 concurrent, 1 new browser / 20s | Warm sessions are reused instead of relaunched; browser tools are limited to a few calls per minute per caller. |
| Workers AI | 10,000 neurons/day | AI tools default to the cheapest capable model; each tool page states its rough cost. |
| Worker CPU | 10 ms / request | HTML parsing is streamed through HTMLRewriter rather than buffered into a DOM. |
| Requests | 100,000 / day | Static pages are served by the edge and never invoke the Worker. |
When an allowance runs out you get quota_exhausted with a Retry-After header rather than a mystery 500. Check /health for what is currently wired up.
POST /api/v1/html-to-markdown
{ "url": "https://example.com/long-article", "readability": true, "includeLinks": false }
# data.stats reports the compression, typically 0.10-0.20 of the original HTMLPOST /api/v1/markdown-to-pdf?output=json
{ "markdown": "# Weekly report\n\n…", "theme": "print", "displayHeaderFooter": true,
"footerTemplate": "<div style='font-size:9px;width:100%;text-align:center'><span class='pageNumber'></span></div>" }POST /api/v1/fetch-page { "url": "https://example.com", "userAgent": "chrome-desktop", "include": ["text"] }
POST /api/v1/fetch-page { "url": "https://example.com", "userAgent": "googlebot", "include": ["text"] }
# diff the two text bodies# Stable DOM? Deterministic, no neurons:
POST /api/v1/scrape { "url": "…", "selectors": { "price": ".price", "title": "h1" } }
# Irregular layout? Fall back to the model:
POST /api/v1/extract-data { "url": "…", "fields": ["title", "price:number"] }The repository is MIT licensed and deploys to a Cloudflare account in a few minutes — see docs/SETUP-CLOUDFLARE.md. The AI, Browser and rate-limit bindings need no account-specific IDs, so a fresh clone deploys as-is.