Docs

API

One request:

curl -X POST https://sharehtml.org/api/v1/pages \
  -H 'Authorization: Bearer $SHAREHTML_TOKEN' \
  -H 'Content-Type: text/html' \
  --data-binary @page.html

returns {"url":"https://<name>.sharehtml.org/","id":"...","bytes":...}. Anonymous uploads need no token and get a manage key back instead. PUT replaces the file (same URL, new version, history kept on Pro). DELETE removes it. Limits: 5 MB free, 25 MB Pro, HTML only, 5 uploads a minute per IP. Every error is JSON with a stable code.

Base URL https://sharehtml.org/api/v1. All responses are JSON.

Authentication

Precedence, first match wins:

  1. Authorization: Bearer <token>. API tokens are created from your dashboard once signed in. Pages belong to your account and count toward its quota.
  2. The session cookie, when calling from the sharehtml.org site itself.
  3. Nothing: the upload is anonymous. The response includes manageKey and manageUrl, shown once. Send the key as X-Manage-Key on later GET, PUT and DELETE calls. Lose it and the page can still be claimed from the same browser after signing in, but not from curl.

POST /pages

Creates a page. Three body forms are accepted:

Raw HTML

Content-Type: text/html, the file as the body. Optional ?slug= query (Pro).

curl -X POST https://sharehtml.org/api/v1/pages \
  -H 'Authorization: Bearer $SHAREHTML_TOKEN' \
  -H 'Content-Type: text/html' \
  --data-binary @page.html

JSON

{"html": "...", "slug"?: "..."}

curl -X POST https://sharehtml.org/api/v1/pages \
  -H 'Content-Type: application/json' \
  -d '{"html":"<!doctype html><title>Hi</title><h1>Hi</h1>","slug":"hello-there"}'

Multipart form

Field file, optional slug, optional cf-turnstile-response. This is what the browser sends.

curl -X POST https://sharehtml.org/api/v1/pages \
  -F 'file=@page.html' \
  -F 'cf-turnstile-response=<token from the widget>'

From JavaScript

const res = await fetch('https://sharehtml.org/api/v1/pages', {
  method: 'POST',
  headers: { 'Content-Type': 'text/html' },
  body: html,
})
const page = await res.json() // page.url, page.id, page.manageKey

Response: 201

{
  "id": "2b6f0c1e-7d2a-4d5a-9b1c-4f2e8a9d3c71",
  "url": "https://k3x9q2a.sharehtml.org/",
  "slug": "k3x9q2a",
  "bytes": 18342,
  "visibility": "unlisted",
  "status": "active",
  "manageUrl": "https://sharehtml.org/m/<manage-key>",
  "manageKey": "<manage-key>",
  "qrSvg": "<svg xmlns=\"http://www.w3.org/2000/svg\" ...>"
}

Anonymous limits

GET /pages/{id}

Owner via bearer or session, or anonymous with X-Manage-Key.

curl https://sharehtml.org/api/v1/pages/<id> \
  -H 'X-Manage-Key: <manage-key>'
{
  "id": "2b6f0c1e-7d2a-4d5a-9b1c-4f2e8a9d3c71",
  "url": "https://k3x9q2a.sharehtml.org/",
  "slug": "k3x9q2a",
  "bytes": 18342,
  "visibility": "unlisted",
  "indexable": false,
  "status": "active",
  "version": 1,
  "views": 12,
  "title": "Q3 report",
  "createdAt": "2026-09-03T10:15:00.000Z",
  "updatedAt": "2026-09-03T10:15:00.000Z"
}

PUT /pages/{id}

Replaces the file. Same auth and body forms as POST. Returns 200 with the POST shape. The URL does not change. Pro keeps the previous versions; free keeps the latest only.

curl -X PUT https://sharehtml.org/api/v1/pages/<id> \
  -H 'Authorization: Bearer $SHAREHTML_TOKEN' \
  -H 'Content-Type: text/html' \
  --data-binary @page.html

PATCH /pages/{id}

Owner only. JSON body, any subset of:

private, password, indexable and slug need Pro; otherwise 403 pro_required. Returns 200 with the GET shape.

curl -X PATCH https://sharehtml.org/api/v1/pages/<id> \
  -H 'Authorization: Bearer $SHAREHTML_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"visibility":"password","password":"correct horse","slug":"q3-report"}'

DELETE /pages/{id}

Returns 204. The bytes are removed and the slug stays reserved, so nobody can reuse an address you once shared.

curl -X DELETE https://sharehtml.org/api/v1/pages/<id> \
  -H 'X-Manage-Key: <manage-key>'

GET /me

Bearer or session. Returns your id, email, plan and live page count.

curl https://sharehtml.org/api/v1/me \
  -H 'Authorization: Bearer $SHAREHTML_TOKEN'
{"id":"...","email":"you@example.com","plan":"pro","pages":14}

Errors

Every error is {"error":{"code","message"}}:

{"error":{"code":"too_large","message":"Free pages are limited to 5 MB."}}
StatusCodeMeaning
413too_largeover 5 MB (free) or 25 MB (Pro)
415not_htmlthe body is not an HTML document (PDF, ZIP, image, SVG and JSON magic bytes are rejected)
422invalid_utf8the body is not valid UTF-8 (a NUL byte is 415 not_html)
422rejectedthe page failed the abuse heuristics (phishing patterns, droppers, hidden redirects)
422slug_invalidthe slug is not 2-63 lowercase letters, digits or hyphens, or is reserved or a brand word
409slug_takenthat slug belongs to another page
429rate_limitedmore than 5 uploads a minute; Retry-After is set when known
429quota_exceededthe daily anonymous cap, or 25 live pages (free) / 1,000 (Pro)
403turnstile_failedthe cf-turnstile-response token did not verify
403pro_requiredprivate, password, indexable or a custom slug on a free page
401unauthorizedbad or missing bearer token or manage key
404not_foundno such page, or not yours

Codes are stable. Match on code, not on message. See limits for the numbers behind them.

What your page is served with

CLI and MCP

A CLI is planned. Until then, the API is the interface; for agents has the copy-paste snippet.