How it worksFeaturesPricingAPI
Dashboard

Publish a static site with one request

The Fylo.Host API turns an HTML document into a live website at your-name.fylo.dev, served from Cloudflare’s edge in 330+ cities. JSON in, JSON out, a Bearer key in the header. Built for scripts, CI and AI agents.

v1REST · JSONBearer authCORS enabled.md

Quickstart

Create a key in the dashboard (API tab), then:

# publish
curl -s https://fylo.host/api/v1/sites \
  -H "Authorization: Bearer fylo_live_..." \
  -H "Content-Type: application/json" \
  -d '{"html":"<!doctype html><h1>Hello</h1>","subdomain":"hello-world"}'

# → 201
{ "id": "NEtS3W8axVbi2aig72Jbt", "subdomain": "hello-world",
  "url": "https://hello-world.fylo.dev", "status": "active", ... }

The page is live at that URL before the response arrives. Anyone can open it; no account, no app.

Authentication

Every request needs an API key in the Authorization header:

Authorization: Bearer fylo_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Keys are tied to your account and act with its plan and project limits. They are shown once when created and stored hashed; revoking one takes effect immediately. Keep keys in environment variables or a secrets manager, never in a file you publish.

For AI agents

If you are using Claude Code, Codex, Cursor or any agent that reads skills, install the Fylo skill once and then just say “publish this to Fylo”:

npx skills add raviblogger/fylo-host-skill --skill fylo -g
export FYLO_API_KEY=fylo_live_...

The skill knows how to inline assets, pick a subdomain, remember the site id in .fylo.json and update the same URL on the next publish. Source: github.com/raviblogger/fylo-host-skill. Machine-readable summary of this page: /llms.txt.

Claude Code · Codex · Cursor · any agent

Skip the copy-paste. Tell your agent to publish it.

For coding agents on your machine (Claude Code, Codex CLI, Cursor, Cline): paste this once and it installs the Fylo skill, asks you for a key and puts the page on a live URL. Chat windows (claude.ai, ChatGPT) can’t reach fylo.host from their sandboxes — connect the MCP server instead, below.

Publish this project to Fylo.Host and give me the live URL.
If the Fylo skill is not installed, install it first: npx skills add raviblogger/fylo-host-skill --skill fylo -g
Use my FYLO_API_KEY environment variable; if it is not set, ask me for a key from https://fylo.host/dashboard/settings/api
Publish index.html as one self-contained HTML file (inline CSS and JS) to POST https://fylo.host/api/v1/sites and save the returned id in .fylo.json so future publishes update the same site.
Free account · key from fylo.host/dashboard/settings/api

Use from Claude, ChatGPT and other chat apps (MCP)

Chat apps can’t run scripts against fylo.host, but they can call an MCP server. Fylo.Host is one: connect it once and say “publish this to Fylo” in the chat where you built the page. Sign-in is OAuth — no key to paste.

https://fylo.host/mcp
ClientHow to connect
Claude (claude.ai, Desktop)Settings → Connectors → Add custom connector → name Fylo.Host, URL above → Connect, then Allow on the Fylo page. Pro, Max, Team and Enterprise plans.
ChatGPTSettings → Connectors → Create (developer mode) → MCP server URL above, authentication OAuth.
Claude Codeclaude mcp add --transport http fylo https://fylo.host/mcp then /mcp to sign in.
Cursor, Windsurf, othersAdd an MCP server of type streamable HTTP with the URL above; they open the same sign-in page.

Tools exposed: publish_site (html or files, optional subdomain), update_site (site id + html or files), list_sites, list_versions / restore_site, and the blog set — list_posts, enable_blog, get_post, create_post (HTML body, draft or published), update_post, unpublish_post, delete_post — so “write a post about X on my site and publish it” lands at /blog/<slug>/ with the same plan limits as the editor — and the forms set: create_form, form_status, list_leads, disable_form (Pro and Business); analytics: get_analytics; speed settings: get_performance, set_performance; custom domains: list_domains, add_domain, check_domain, remove_domain (paid plans). They call the same API as above, so plan limits and errors are identical. Connected apps are listed under Settings → API in your dashboard, where Disconnect cuts an app off immediately; tokens also expire after an hour and refresh automatically for 30 days while connected.

Publish a site from chat

Build the page in the chat as usual, then ask for it to go live. Claude sends the code straight to Fylo — nothing to copy.

You sayWhat happens
“Publish this to Fylo”publish_site with the page’s HTML → live at a generated *.fylo.dev URL in a few seconds. Claude keeps the site id for later changes.
“Publish it as my-portfolio”publish_site with subdomain: my-portfolio → https://my-portfolio.fylo.dev. If the name is taken you’re asked for another.
“Publish the whole project” (several pages, CSS, images)publish_site with files → a multi-file site; relative links work as they do locally.
“Make the heading blue and update the site”update_site → same URL, edge cache rebuilt, previous version saved.
“List my sites” / “which sites do I have?”list_sites → ids and URLs, so you can refer to a site by name in later messages.
“Undo that” / “put it back to yesterday’s version”list_versions + restore_site → the chosen snapshot goes live; the current one is saved first.

Every site published this way appears in your dashboard like any other and follows your plan’s site and storage limits.

Add a contact form from chat

Fylo Forms give a static site a working contact form — submissions are spam-checked, emailed to you and listed in the dashboard. Pro and Business plans.

You sayWhat happens
“Add a contact form to my portfolio site”create_form → Claude gets the form snippet, places it in the page (styled to match) and calls update_site. The form works immediately.
“Build a landing page with a signup form and publish it”publish_site for the page, then create_form and update_site with the snippet inside — or Claude asks for the site first, since a form belongs to a site.
“Any new leads this week?”list_leads → the recent submissions with name, email, message, country and time.
“Is the form still on?” / “pause the form”form_status (usage vs. the monthly cap) / disable_form.

The snippet is a plain HTML <form> posting to https://fylo.host/api/forms/<key> with a honeypot and a timestamp; any input names are captured, so Claude can add fields (phone, company, budget…) as long as the hidden fylo_* inputs stay.

Publish a blog post from chat

Once Fylo.Host is connected, a blog post is one message. Claude picks the tools; you never name them.

You sayWhat happens
“Enable the blog on my portfolio site”enable_blog → the blog exists at /blog/ with the default theme. Once per site.
“Write a 400-word post titled Why static sites are fast and publish it”create_post with status: published → live at https://portfolio.fylo.dev/blog/why-static-sites-are-fast/, listed on the blog index and in RSS.
“Draft a post about …, I’ll review it first”create_post as a draft → you get a private preview link; say “publish it” later.
“Fix the typo in the second paragraph” / “change the title to …”get_post + update_post → same URL, blog rebuilt.
“List my posts” / “take the launch post offline”list_posts / unpublish_post.

Posts are written as HTML by the model and sanitized on the server; the free plan allows 10 published posts, paid plans more. Everything an app publishes also appears in the dashboard’s Blog tab, where you can edit it by hand.

Check analytics from chat

Refer to a site by its id, its fylo subdomain or its custom domain.

You sayWhat happens
“How are my sites doing this week?”get_analytics without a site → views per site for the account, plus visitors in the last 5 minutes.
“Show analytics for example.com for the last 30 days”get_analytics with the site and range: 30d → views by day, unique visitors, bounce rate, average time on page, top pages, referrers, countries and devices.

Ranges: today, 7d, 30d, 90d; the free plan covers today and 7d. Views are sampled estimates, counted in IST days.

Tune speed settings from chat

The same switches as the dashboard’s Performance tab. Originals are never modified; every setting is reversible.

You sayWhat happens
“What speed settings are on for my-portfolio?”get_performance → every setting and its value.
“Turn on WebP images and lazy-load iframes”set_performance with images_webp and lazy_iframes → saved, the site is rebuilt, and the reply reports what is actually stored.
“JS delay broke my menu, turn it off”set_performance with delay_js: false.

Settings: fonts, font_mode, preload_fonts, minify, inline_css, delay_js, js_mode, js_exclude, selfhost_external, selfhost_exclude, images_webp, images_exclude, image_quality, responsive_images, responsive_exclude, size_images, lazy_images, lazy_images_exclude, lazy_iframes, youtube_previews, preload_lcp, preload_lcp_image. On sites with many pages, change one or two per message — each change rebuilds the site.

Connect a custom domain from chat

Starter, Pro and Business plans. The app registers the domain; the DNS records are yours to create at your DNS provider.

You sayWhat happens
“Which domains do I have?”list_domains → each domain, the site it points to, verified or not.
“Connect example.com to my portfolio site”add_domain → the domain and its www form are registered, and you get two CNAME records (@ and www → fylo.host) to add.
“I’ve added the records, check it”check_domain → DNS and SSL status; the domain is marked verified once active.
“Remove example.com”remove_domain → Claude asks you to confirm first. The domain and its www form are detached; the site stays live on its fylo subdomain.

A connected app can never delete a site, change billing or manage API keys — those need your dashboard login.

New tools not showing in Claude? claude.ai keeps the tool list it saw when the connector was first added. Remove the connector and add it again as https://fylo.host/mcp?v=2 (same server, fresh list), then start a new chat.

Endpoints

Base URL https://fylo.host/api/v1. All bodies are application/json.

GET/me

Returns the account behind the key. Handy to verify a key.

{ "id": "dkeV...", "email": "you@example.com", "name": "You", "plan": "pro" }
GET/sites

Lists up to 100 of the account’s sites, newest first.

{ "sites": [ { "id", "subdomain", "url", "status", "created_at" }, ... ] }
POST/sites

Creates a site from one HTML document.

FieldTypeNotes
htmlstringRequired. A complete HTML document, up to 10 MB. CSS and JS inline; images as https:// URLs or data URIs.
subdomainstringOptional. 1–40 lowercase letters, digits, hyphens. Omit to get one generated.
filesarrayInstead of html: a list of { path, content, encoding? } objects.[ { "path": "index.html", "content": "<!doctype html>..." }, { "path": "css/app.css", "content": "..." }, { "path": "img/logo.png", "content": "iVBOR...", "encoding": "base64" } ]Text files as strings, binaries as base64. Must include at least one .html; index.html becomes the entry page. Up to 2,000 files and 20 MB per request.

Returns 201 with the site object plus expires_at (always null for API publishes — they belong to your account and stay up).

Multi-file sites: send files instead of html. Relative links between your files work as they would on any host. For sites over 20 MB, upload a ZIP from the homepage or dashboard.
GET/sites/:id

Returns one site.

PUT/sites/:id

Replaces the site’s content. Same body as create: html for a single page (all plans), or files to replace the whole site (paid plans; the free plan gets 402 plan_limit and can use html). The URL stays the same and the edge cache is rebuilt for you. Prefer this over creating a new site for every change — each new site uses a project slot on your plan.

curl -s -X PUT https://fylo.host/api/v1/sites/NEtS3W8axVbi2aig72Jbt \
  -H "Authorization: Bearer fylo_live_..." \
  -H "Content-Type: application/json" \
  -d '{"html":"<!doctype html><h1>Hello, v2</h1>"}'

Version history

Every time a site is replaced — from the dashboard, the API or an MCP client — the previous content is saved as a whole-site version first. Free keeps 3, Starter 5, Pro 20, Business 24; older ones are pruned automatically. Versions count against your plan’s storage: when a new snapshot wouldn’t fit, the oldest versions are removed first, and a publish is never blocked by its own backup. The dashboard’s Storage figure includes them.

GET /api/v1/sites/:id/versions

{ "site_id": "…", "limit": 20, "versions": [ { "id": "v_…", "created_at": 1758000000, "source": "mcp:Claude", "files": 12, "bytes": 348112, "pages": 3 } ] }

POST /api/v1/sites/:id/restore

{ "version_id": "v_…" }

Puts that version live at the same URL and rebuilds the cache. The current content is saved as a new version first (source: "pre-restore"), so a restore is always reversible. MCP clients get the same as list_versions and restore_site — “put my site back to yesterday’s version” works in chat.

Errors

Every error is { "error": { "code", "message" } } with a matching HTTP status.

StatuscodeMeaning
401unauthenticatedKey missing, malformed or revoked.
400missing_html / invalid_subdomainBody is missing html, or the slug breaks the rule.
409subdomain_takenPick another slug or omit it.
402plan_limitProject limit for the account’s plan reached. Update an existing site, delete one, or upgrade.
413too_largehtml over 10 MB.
429rate_limitedOver 30 requests per minute on this key. Retry after 60 s.
400invalid_path / duplicate_path / invalid_contentA files entry has a bad path (no .., no leading slash), repeats a path, or has non-string / bad base64 content.
422rejectedA file was blocked by content moderation (executables, pirated-media names).
502partial_publishThe site was created with its entry page but attaching the other files failed; the response includes site. Retry with PUT /sites/:id.
404not_foundUnknown endpoint or a site that isn’t yours.

Limits

Managing keys

Create, list and revoke keys under Settings → API in your dashboard. The same actions are available to a logged-in browser session at GET/POST /api/keys and DELETE /api/keys/:id; these endpoints use your session cookie, not a Bearer key.

Ready to script it?

Create a key, run the quickstart, and your first page is live in under a minute.

Get an API key
Copied