REST API

Programmatic publish, read, update, and delete operations for HTML pages.

The Share HTML REST API publishes HTML and manages the pages and API keys associated with a Share HTML account. All endpoints are versioned under /api/v1; responses are JSON.

Base URL - https://share-html.com/api/v1

Authentication#

The API uses a single credential: an account-scoped API key, sent as a bearer token.

CredentialHeaderFormatScopeLifetime
API keyAuthorization: Bearer …sh_live_ + 48 hex charactersEvery page owned by the calling accountLong-lived. Create and revoke at share-html.com/app/keys.

Every endpoint requires the API key except GET /pages/{slug}, which is public. Publishing without an account is available only in the browser editor - it is a first-party convenience, not part of this API.

Rate limits#

Authenticated operations are limited per account; the public read is limited per client IP. All buckets are one-hour rolling windows.

OperationLimit
POST /pages200 / hour per account
PATCH /pages/{slug}200 / hour per account
DELETE /pages/{slug}200 / hour per account
GET /me/*200 / hour per account
GET /pages/{slug}600 / hour per IP

When a limit is exceeded the API returns 429 rate_limited with a Retry-After: 3600 header.

Errors#

Every error response shares the same envelope. Validation failures additionally carry a details array of field issues.

{
  "error": {
    "code": "bad_request",
    "message": "Validation failed",
    "details": [{ "path": "ttl", "message": "Invalid enum value" }]
  }
}
HTTPcodeMeaning
400bad_requestRequest body is not valid JSON or fails schema validation.
401unauthorizedA bearer credential is required but was missing or unrecognized.
403forbiddenThe API key is valid but is not associated with a user account.
404not_foundThe page does not exist. Also returned when an API key authenticates successfully but does not own the requested page - ownership and existence are intentionally indistinguishable from the caller's perspective.
409conflictThe request would violate a uniqueness or self-revocation rule.
413payload_too_largeThe HTML body exceeds the 5 MB limit.
429rate_limitedA rate-limit bucket is full. Retry after the Retry-After interval.
500internalAn unexpected server error occurred.

Create a page#

POST/api/v1/pages

Publishes an HTML document and returns its public URL and slug.

Authentication - Required. Authorization: Bearer sh_live_…. The page is associated with the calling account.

Body parameters#

FieldTypeRequiredDescription
htmlstringyesFull HTML document. 1 byte to 5 MB.
slugstringnoCustom slug. 4 to 40 characters, lowercase alphanumeric plus -. Defaults to an 8-character slug from an alphabet that excludes ambiguous characters (no 0, 1, i, l, o).
ttlenumno"24h", "7d", "30d", or "never". Defaults to "never".
passwordstringnoPassword gate. 1 to 128 characters.

Request#

curl -X POST https://share-html.com/api/v1/pages \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "html": "<!DOCTYPE html><h1>Launch notes</h1>",
    "slug": "launch-notes",
    "ttl": "30d"
  }'
const res = await fetch("https://share-html.com/api/v1/pages", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${apiKey}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    html: "<!DOCTYPE html><h1>Launch notes</h1>",
    slug: "launch-notes",
    ttl: "30d",
  }),
});
const { url, slug } = await res.json();
import requests

res = requests.post(
    "https://share-html.com/api/v1/pages",
    headers={"Authorization": f"Bearer {api_key}"},
    json={
        "html": "<!DOCTYPE html><h1>Launch notes</h1>",
        "slug": "launch-notes",
        "ttl": "30d",
    },
)
data = res.json()

Response201#

{
  "url": "https://pages.share-html.com/launch-notes",
  "slug": "launch-notes"
}

Errors#

HTTP
Code
Meaning
400
bad_request
Body is not valid JSON, html is missing, slug doesn't match the regex, or ttl is not one of the four allowed values.
401
unauthorized
No API key supplied.
403
forbidden
API key is not associated with a user account.
413
payload_too_large
html exceeds the 5 MB limit.
429
rate_limited
Hourly publish bucket exhausted.
500
internal
Storage or database write failed.

Get page metadata#

GET/api/v1/pages/{slug}

Returns non-secret metadata for a page. The HTML body itself is served at https://pages.share-html.com/{slug} and is not exposed through the REST API.

Authentication - None. Anyone with the slug can read its metadata.

Path parameters#

FieldTypeDescription
slugstringThe slug returned by POST /pages.

Request#

curl https://share-html.com/api/v1/pages/launch-notes

Response200#

{
  "slug": "launch-notes",
  "url": "https://pages.share-html.com/launch-notes",
  "title": "Launch notes",
  "owner": "user",
  "size_bytes": 4217,
  "has_password": false,
  "discoverable": false,
  "view_count": 132,
  "expires_at": null,
  "created_at": "2026-05-12T09:31:00.000Z",
  "updated_at": "2026-05-19T14:02:00.000Z"
}

owner is "anonymous" or "user". title is extracted from the HTML at publish time and may be null. expires_at is null when ttl is "never". discoverable mirrors the indexing hint set on the page.

Errors#

HTTP
Code
Meaning
404
not_found
The page does not exist or has expired.

Update a page#

PATCH/api/v1/pages/{slug}

Updates one or more fields on a page owned by the calling account. Any field omitted from the body is left unchanged. Sending "password": null clears an existing password gate.

Authentication - Required. Authorization: Bearer sh_live_….

Path parameters#

FieldTypeDescription
slugstringThe slug to update.

Body parameters#

At least one field is required.

FieldTypeDescription
htmlstringReplacement HTML document. 1 byte to 5 MB.
ttlenum"24h", "7d", "30d", or "never".
passwordstring | nullNew password (1 to 128 characters), or null to clear an existing password.
discoverablebooleanWhether the page is included in search indexing hints. Defaults to false at publish time.

Request#

curl -X PATCH https://share-html.com/api/v1/pages/launch-notes \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"html": "<!DOCTYPE html><h1>Launch notes - v2</h1>"}'
await fetch(`https://share-html.com/api/v1/pages/${slug}`, {
  method: "PATCH",
  headers: {
    Authorization: `Bearer ${apiKey}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ html: "<!DOCTYPE html><h1>Launch notes - v2</h1>" }),
});
import requests

requests.patch(
    f"https://share-html.com/api/v1/pages/{slug}",
    headers={"Authorization": f"Bearer {api_key}"},
    json={"html": "<!DOCTYPE html><h1>Launch notes - v2</h1>"},
)

Response200#

{
  "url": "https://pages.share-html.com/launch-notes",
  "slug": "launch-notes"
}

Errors#

HTTP
Code
Meaning
400
bad_request
Body is not valid JSON, no fields supplied, or one or more fields fail validation.
401
unauthorized
No API key supplied.
403
forbidden
API key is not associated with a user account.
404
not_found
The page does not exist, has expired, or is owned by a different account.
413
payload_too_large
html exceeds the 5 MB limit.
429
rate_limited
Hourly update bucket exhausted.

Delete a page#

DELETE/api/v1/pages/{slug}

Permanently removes a page owned by the calling account. After this call the page URL returns 404.

Authentication - Required. Authorization: Bearer sh_live_….

Path parameters#

FieldTypeDescription
slugstringThe slug to delete.

Request#

curl -X DELETE https://share-html.com/api/v1/pages/launch-notes \
  -H "Authorization: Bearer $API_KEY"
await fetch(`https://share-html.com/api/v1/pages/${slug}`, {
  method: "DELETE",
  headers: { Authorization: `Bearer ${apiKey}` },
});
import requests

requests.delete(
    f"https://share-html.com/api/v1/pages/{slug}",
    headers={"Authorization": f"Bearer {api_key}"},
)

Response204#

No body. The page URL returns 404 immediately after.

Errors#

HTTP
Code
Meaning
401
unauthorized
No API key supplied.
403
forbidden
API key is not associated with a user account.
404
not_found
The page does not exist or is owned by a different account.
429
rate_limited
Hourly delete bucket exhausted.

The endpoints below - GET /me/pages, GET /me/keys, POST /me/keys, and DELETE /me/keys/{id} - operate on the calling account and require an API key.

List your pages#

GET/api/v1/me/pages

Returns active pages owned by the calling account, newest first, with pagination.

Authentication - Required. Authorization: Bearer sh_live_….

Query parameters#

FieldTypeDescription
limitintegerPage size, 1 to 100. Defaults to 20.
offsetintegerNumber of items to skip. Defaults to 0.

Request#

curl "https://share-html.com/api/v1/me/pages?limit=20&offset=0" \
  -H "Authorization: Bearer $API_KEY"

Response200#

{
  "data": [
    {
      "slug": "launch-notes",
      "url": "https://pages.share-html.com/launch-notes",
      "title": "Launch notes",
      "owner": "user",
      "size_bytes": 4217,
      "has_password": false,
      "discoverable": false,
      "view_count": 132,
      "expires_at": null,
      "created_at": "2026-05-12T09:31:00.000Z",
      "updated_at": "2026-05-19T14:02:00.000Z"
    }
  ],
  "pagination": {
    "limit": 20,
    "offset": 0,
    "total": 47,
    "has_more": true,
    "next_offset": 20
  }
}

Each item is the same shape returned by GET /pages/{slug}. next_offset is present only when has_more is true.

Errors#

HTTP
Code
Meaning
401
unauthorized
No API key supplied.
403
forbidden
API key is not associated with a user account.
429
rate_limited
Hourly account-operations bucket exhausted.

List API keys#

GET/api/v1/me/keys

Returns every API key on the calling account. The full key value is never returned; only the public prefix is exposed.

Authentication - Required. Authorization: Bearer sh_live_….

Request#

curl https://share-html.com/api/v1/me/keys \
  -H "Authorization: Bearer $API_KEY"

Response200#

{
  "data": [
    {
      "id": "key_01HZ…",
      "name": "Production publisher",
      "prefix": "sh_live_a3f",
      "created_at": "2026-05-01T08:12:00.000Z",
      "last_used_at": "2026-05-19T13:58:00.000Z"
    }
  ]
}

Errors#

HTTP
Code
Meaning
401
unauthorized
No API key supplied.
403
forbidden
API key is not associated with a user account.
429
rate_limited
Hourly account-operations bucket exhausted.

Create an API key#

POST/api/v1/me/keys

Mints a new API key on the calling account. The full key value is returned in the key field and is shown once only - store it immediately. Subsequent requests against GET /me/keys return only the prefix.

Authentication - Required. Authorization: Bearer sh_live_….

Body parameters#

FieldTypeRequiredDescription
namestringyesHuman-readable label. 1 to 64 characters. Shown in the dashboard.

Request#

curl -X POST https://share-html.com/api/v1/me/keys \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "CI deploy bot"}'

Response201#

{
  "id": "key_01HZ…",
  "name": "CI deploy bot",
  "prefix": "sh_live_a3f",
  "key": "sh_live_…"
}

Errors#

HTTP
Code
Meaning
400
bad_request
Body is not valid JSON or name is missing or too long.
401
unauthorized
No API key supplied.
403
forbidden
API key is not associated with a user account.
429
rate_limited
Hourly account-operations bucket exhausted.

Revoke an API key#

DELETE/api/v1/me/keys/{id}

Revokes an API key on the calling account. The key cannot revoke itself - use a different key, or revoke through the dashboard.

Authentication - Required. Authorization: Bearer sh_live_….

Path parameters#

FieldTypeDescription
idstringKey ID from GET /me/keys.

Request#

curl -X DELETE https://share-html.com/api/v1/me/keys/key_01HZ… \
  -H "Authorization: Bearer $API_KEY"

Response204#

No body.

Errors#

HTTP
Code
Meaning
401
unauthorized
No API key supplied.
403
forbidden
API key is not associated with a user account.
404
not_found
Key does not exist on the calling account.
409
conflict
The key targeted for revocation is the same key used to authenticate the request.

OpenAPI#

The machine-readable schema is served at /api/openapi.json. It is generated from the same validation schemas the API enforces, so it cannot drift from behavior. Use it with SDK generators and tools such as Swagger UI, Redoc, and Stoplight.