Documentation

Everything you need to call these tools from a script, a function-calling loop or an MCP client.

Quickstart

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://…"}}

Response envelope

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
  }
}

Error codes

CodeHTTPMeaningRetryable
invalid_input400A parameter failed validation. details lists the offending fields.no
unsupported_media415The file type is not supported by the converter.no
payload_too_large413The request body exceeded MAX_INPUT_BYTES.no
rate_limited429Per-caller rate limit. Honour the Retry-After header.yes
upstream_blocked403The target URL was refused by the SSRF policy (private address, non-http scheme).no
upstream_failed502The target site or an upstream model returned an error.yes
binding_unavailable503A required Cloudflare binding is not configured on this deployment.no
quota_exhausted503The daily free allowance for Browser Rendering or Workers AI is used up.yes
timeout504The page or model took too long.yes
internal500Unexpected server error.yes

Tool index

Slug (REST)Name (MCP)SummaryCost
html-to-pdfhtml_to_pdfRender raw HTML or a live URL into a print-ready PDF with real Chromium.browser
screenshotscreenshotCapture any URL or HTML string as PNG / JPEG / WebP, at any device size.browser
fetch-pagefetch_pageRetrieve a page as a chosen user-agent — raw HTTP or fully JS-rendered.free
scrapescrapePull named fields out of a page with CSS selectors — deterministic, no AI tokens.free
html-to-markdownhtml_to_markdownStrip the chrome, keep the article. Typically 80–90% fewer tokens than raw HTML.free
document-to-markdowndocument_to_markdownPDF, DOCX, XLSX, CSV, ODT and images in — Markdown out.AI
markdown-to-htmlmarkdown_to_htmlGitHub-flavoured Markdown to a styled, self-contained HTML document.free
markdown-to-pdfmarkdown_to_pdfOne call from model-generated Markdown to a shareable PDF.browser
summarizesummarizeCompress text or a whole web page into Markdown at a length you choose.AI
extract-dataextract_dataTurn messy text or a web page into JSON that matches your schema.AI
translatetranslateTranslate text between 100+ languages while keeping Markdown structure intact.AI
describe-imagedescribe_imageRead text out of an image, or write alt text and captions for it.AI

MCP

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.

Function calling

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/tools

There is also /llms.txt, a plain-text briefing written for a model that has just been pointed at this host.

No login, but shared quota — rate limits and the free tier

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."

ResourceFree allowanceWhat we do about it
Browser Rendering10 min/day, 3 concurrent, 1 new browser / 20sWarm sessions are reused instead of relaunched; browser tools are limited to a few calls per minute per caller.
Workers AI10,000 neurons/dayAI tools default to the cheapest capable model; each tool page states its rough cost.
Worker CPU10 ms / requestHTML parsing is streamed through HTMLRewriter rather than buffered into a DOM.
Requests100,000 / dayStatic 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.

Recipes

Feed a web page to a model without wasting tokens

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 HTML

Turn a model's Markdown into a PDF in one call

POST /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>" }

Check whether a site cloaks for search engines

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

Structured extraction, cheapest path first

# 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"] }

Deploy your own

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.