Flatpage API and MCP server
Everything you can do in the dashboard's publish flow, from code or from an AI assistant: publish a page, a zipped site or a PDF, update it at the same link, list, rename and delete sites, and read views. On every plan, Free included.
Authentication
Create an API key in your dashboard under Settings. It's shown once; we store only a hash of it. Send it on every call:
Authorization: Bearer fpk_...Claude, ChatGPT and other MCP clients don't need a key: they sign in with OAuth (see MCP server). Revoke a key or disconnect an app in Settings and it stops working at once.
Quick start
export FLATPAGE_API_KEY=fpk_...
curl -X POST "https://flatpage.app/v1/sites?linkName=spring-menu" \
-H "Authorization: Bearer $FLATPAGE_API_KEY" \
-H "Content-Type: text/html" \
--data-binary @index.htmlAnswer when it's live (usually within a few seconds):
HTTP/1.1 201 Created
{
"uploadId": "8d0c…",
"status": "published",
"site": {
"id": "3f5e…",
"slug": "spring-menu",
"url": "https://spring-menu.flatpage.page",
"kind": "html",
"status": "active",
"version": 1,
…
}
}If processing takes longer than about 20 seconds you get 202 Accepted with "status": "processing"; poll GET /v1/uploads/{uploadId} every couple of seconds. Update the same site later with PUT /v1/sites/spring-menu and the same body.
Endpoints
Base URL https://flatpage.app/v1. {site} is a site's id or its link name. Bodies and answers are JSON unless noted.
- GET/v1/me
- Your email, plan and what you've used of it today.
- GET/v1/sites
- Your sites, newest first, with links and views.
- POST/v1/sites
- Publish a new site. Body: the file (text/html, application/zip, application/pdf) or JSON {"html", "linkName"}. ?linkName= names it.
- GET/v1/sites/{site}
- One site.
- PUT/v1/sites/{site}
- Replace a site's content with a new version. Same body as publishing. The link doesn't change.
- PUT/v1/sites/{site}/link-name
- Rename: {"linkName": "new-name"}. The old link redirects for 30 days.
- GET/v1/sites/{site}/stats
- Views per day for the last 30 days, this month and all time.
- DELETE/v1/sites/{site}
- Take a site offline and delete it.
- GET/v1/link-names/{name}
- Whether a link name is valid and free.
- GET/v1/uploads/{uploadId}
- A publish that answered 202: its status, and the site once it's live.
- POST/v1/uploads
- Files over 25 MB, step 1: {"fileName", "bytes", "siteId"?, "linkName"?} answers a putUrl.
- POST/v1/uploads/{uploadId}/complete
- Step 3, after you PUT the file to putUrl with the contentType it gave you.
Errors and limits
Errors answer with an HTTP status and {"error": "..."} in plain words you can show to a person:
400bad input, such as an invalid link name.401missing or revoked key.403over your plan's site limit.404no such site on your account.409link name taken.413file too big.422the upload was rejected (for example a zip without index.html, or a virus).429a rate limit.- Each API key, and each connected app, gets 200 calls a day, reset at midnight UTC.
- Your plan's limits apply as in the dashboard: number of sites, file size and uploads per day (see pricing).
- Bodies sent straight to
POST /v1/sitescan be up to 25 MB. Bigger files use the two-step upload. - Everything is checked like a dashboard upload: virus scan, phishing checks and the acceptable use policy.
MCP server
Remote MCP server (streamable HTTP) at https://flatpage.app/mcp. It signs in with OAuth 2.1 (dynamic client registration, PKCE), so it can be added as a custom connector in Claude and ChatGPT with no key. Setup steps for each app are in Publish from Claude and ChatGPT. Tools:
publish_html: html, link_name?: publish a page as a new site.publish_file: file_name, content_base64, link_name?: publish a .zip, .pdf or .html (up to 25 MB).update_site: site, then html or file_name + content_base64: new version, same link.list_sites: Your sites with links, status and views.get_site_stats: site: views for 30 days, this month, all time.rename_site: site, link_name: change the link; the old one redirects.delete_site: site, confirm: deletes only when confirm is the site's link name.
Each tool call counts as one API call. Clients that can't do OAuth can send an API key as a Bearer token instead.
Claude skill
A skill that teaches Claude to publish with Flatpage using the connector or this API: download the zip for claude.ai, or SKILL.md for Claude Code.
Big files: two-step upload
# 1. Ask for an upload URL
curl -X POST https://flatpage.app/v1/uploads -H "Authorization: Bearer $FLATPAGE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"fileName":"brochure.pdf","bytes":48000000,"linkName":"brochure"}'
# → {"uploadId":"…","putUrl":"https://…","contentType":"application/pdf",…}
# 2. PUT the file there with exactly that Content-Type
curl -X PUT "$PUT_URL" -H "Content-Type: application/pdf" --data-binary @brochure.pdf
# 3. Tell us it's there
curl -X POST https://flatpage.app/v1/uploads/$UPLOAD_ID/complete -H "Authorization: Bearer $FLATPAGE_API_KEY"To replace an existing site this way, send its id or link name as siteId in step 1 instead of linkName.