HTML Pub

Build and publish pages programmatically

Sign up to get your API key and start creating pages from your code.

Sign up free
Pro Plan Required

API Documentation

Complete reference for the htmlpub REST API

Quick Start

Get started with the htmlpub API in seconds

1. Get Your API Key

Sign in and upgrade to Pro to access API keys in your dashboard.

2. Make Your First Request

curl -X PUT https://htmlpub.com/api/pages/my-page \
  -H "Authorization: Bearer hp_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"html": "<h1>Hello World</h1>"}'

Authentication

All API requests require authentication using a Bearer token in the Authorization header.

Format

Authorization: Bearer hp_live_YOUR_API_KEY

Keep Your API Key Safe

Never expose your API key in client-side code or public repositories. Use environment variables and keep keys server-side only.

Endpoints

PUT
/api/pages/{slug}

Create or update a page with a custom slug

Request Body

{
  "html": "<h1>Your HTML content</h1>"
}

Success Response (201 Created / 200 OK)

{
  "id": "cm1a2b3c4d5e6f7g8h9i0",
  "slug": "my-page",
  "url": "https://htmlpub.com/my-page"
}

Slug Requirements

  • 3-100 characters long
  • Lowercase letters, numbers, and hyphens only
  • Must start and end with alphanumeric character

Examples

cURL

curl -X PUT https://htmlpub.com/api/pages/my-page \
  -H "Authorization: Bearer hp_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"html": "<h1>Hello World</h1><p>My content</p>"}'

JavaScript (fetch)

const response = await fetch('https://htmlpub.com/api/pages/my-page', {
  method: 'PUT',
  headers: {
    'Authorization': 'Bearer hp_live_YOUR_API_KEY',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    html: '<h1>Hello World</h1><p>My content</p>'
  })
});
const data = await response.json();
console.log(data.url); // https://htmlpub.com/my-page

Python (requests)

import requests

response = requests.put(
    'https://htmlpub.com/api/pages/my-page',
    headers={
        'Authorization': 'Bearer hp_live_YOUR_API_KEY',
        'Content-Type': 'application/json'
    },
    json={'html': '<h1>Hello World</h1><p>My content</p>'}
)
data = response.json()
print(data['url'])  # https://htmlpub.com/my-page

PATCH
/api/pages/{slug}

Update an existing page's content or title

Request Body

{
  "html": "<h1>Updated content</h1>"
}
// OR
{
  "title": "New Page Title"
}

Success Response (200 OK)

{
  "id": "cm1a2b3c4d5e6f7g8h9i0",
  "slug": "my-page",
  "title": "New Page Title",
  "url": "https://htmlpub.com/my-page"
}

Example

curl -X PATCH https://htmlpub.com/api/pages/my-page \
  -H "Authorization: Bearer hp_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"html": "<h1>Updated content</h1>"}'

GET
/api/pages

List all pages for your account

Success Response (200 OK)

{
  "pages": [
    {
      "id": "cm1a2b3c4d5e6f7g8h9i0",
      "slug": "my-page",
      "title": "My Page",
      "url": "https://htmlpub.com/my-page",
      "siteId": null,
      "createdAt": "2024-01-15T10:30:00.000Z"
    },
    {
      "id": "cm2b3c4d5e6f7g8h9i0j1",
      "slug": "another-page",
      "title": "Another Page",
      "url": "https://htmlpub.com/another-page",
      "siteId": "site_abc123",
      "createdAt": "2024-01-14T09:20:00.000Z"
    }
  ]
}

Examples

cURL

curl https://htmlpub.com/api/pages \
  -H "Authorization: Bearer hp_live_YOUR_API_KEY"

JavaScript (fetch)

const response = await fetch('https://htmlpub.com/api/pages', {
  headers: {
    'Authorization': 'Bearer hp_live_YOUR_API_KEY'
  }
});
const data = await response.json();
console.log(`You have ${data.pages.length} pages`);

Python (requests)

import requests

response = requests.get(
    'https://htmlpub.com/api/pages',
    headers={'Authorization': 'Bearer hp_live_YOUR_API_KEY'}
)
data = response.json()
print(f'You have {len(data["pages"])} pages')

DELETE
/api/pages/{slug}

Delete a page permanently

Success Response (200 OK)

{
  "success": true
}

Examples

cURL

curl -X DELETE https://htmlpub.com/api/pages/my-page \
  -H "Authorization: Bearer hp_live_YOUR_API_KEY"

JavaScript (fetch)

const response = await fetch('https://htmlpub.com/api/pages/my-page', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer hp_live_YOUR_API_KEY'
  }
});
const result = await response.json();
console.log('Deleted:', result.success);

Python (requests)

import requests

response = requests.delete(
    'https://htmlpub.com/api/pages/my-page',
    headers={'Authorization': 'Bearer hp_live_YOUR_API_KEY'}
)
result = response.json()
print('Deleted:', result['success'])

GET
/api/pages/{slug}/raw

Get the raw HTML content of a page (no authentication required)

Response

Returns the raw HTML content with Content-Type: text/html

Example

curl https://htmlpub.com/api/pages/my-page/raw

Assets

Upload images, CSS, JavaScript, and fonts to your pages. Accepted types: png, jpg, gif, svg, webp, css, js, woff, woff2, ttf.

POST
/api/pages/{slug}/assets

Upload a file to a page

Request Body (multipart/form-data)

Send a file using the file form field.

Success Response (201 Created)

{
  "asset": {
    "id": "asset_abc123",
    "filename": "logo.png",
    "contentType": "image/png",
    "size": 45678
  },
  "url": "https://htmlpub.com/api/pages/my-page/assets/asset_abc123"
}

Example

curl -X POST https://htmlpub.com/api/pages/my-page/assets \
  -H "Authorization: Bearer hp_live_YOUR_API_KEY" \
  -F "[email protected]"

Plan Limits

  • Free: 2MB per file, 10MB total, 10 assets per page
  • Starter: 5MB per file, 100MB total, 50 assets per page
  • Pro: 10MB per file, 500MB total, unlimited assets per page

GET
/api/pages/{slug}/assets

List all assets for a page

Success Response (200 OK)

{
  "assets": [
    {
      "id": "asset_abc123",
      "filename": "logo.png",
      "contentType": "image/png",
      "size": 45678,
      "createdAt": "2025-01-15T10:30:00.000Z"
    }
  ]
}

GET
/api/pages/{slug}/assets/{assetId}

Serve an asset file (no authentication required)

Returns the file with appropriate Content-Type. Cached with immutable headers (1 year).

DELETE
/api/pages/{slug}/assets/{assetId}

Delete a specific asset

Success Response (200 OK)

{ "success": true }

Sites

Group related pages into sites with their own URL namespace.

POST
/api/sites

Create a new site

Request Body

{
  "slug": "my-blog",
  "name": "My Blog"
}

Success Response (201 Created)

{
  "id": "site_abc123",
  "slug": "my-blog",
  "name": "My Blog",
  "url": "https://htmlpub.com/s/my-blog",
  "createdAt": "2025-01-15T10:30:00.000Z"
}

GET
/api/sites

List all sites with their pages

Success Response (200 OK)

{
  "sites": [
    {
      "id": "site_abc123",
      "slug": "my-blog",
      "name": "My Blog",
      "url": "https://htmlpub.com/s/my-blog",
      "defaultPageId": null,
      "pageCount": 3,
      "pages": [...],
      "createdAt": "2025-01-15T10:30:00.000Z"
    }
  ]
}

PATCH
/api/sites/{siteId}

Update a site's name, slug, or default page

Request Body (all fields optional)

{
  "name": "Updated Name",
  "slug": "new-slug",
  "defaultPageId": "page-id-or-null"
}

DELETE
/api/sites/{siteId}

Delete a site and all its pages (cascading delete)

Cascading Delete

Deleting a site also permanently deletes all pages in that site.

Custom Domains
Pro Plan

Connect your own domain to a page. Requires Pro plan.

POST
/api/custom-domains

Add a custom domain

Request Body

{
  "domain": "www.example.com",
  "pageId": "cm1a2b3c4d5e6f7g8h9i0"
}

Success Response (201 Created)

{
  "domain": {
    "domain": "www.example.com",
    "status": "PENDING",
    "pageId": "cm1a2b3c4d5e6f7g8h9i0"
  },
  "instructions": {
    "target": "customers.htmlpub.com",
    "message": "Add a CNAME record..."
  }
}

GET
/api/custom-domains

List all custom domains

Success Response (200 OK)

{
  "domains": [
    {
      "domain": "www.example.com",
      "status": "ACTIVE",
      "pageId": "cm1a2b3c4d5e6f7g8h9i0",
      "verifiedAt": "2025-01-16T12:00:00.000Z",
      "createdAt": "2025-01-15T10:30:00.000Z"
    }
  ]
}

PATCH
/api/custom-domains/{domain}

Change which page a domain points to

Request Body

{ "pageId": "new-page-id" }

DELETE
/api/custom-domains/{domain}

Remove a custom domain

Success Response (200 OK)

{ "success": true }

POST
/api/custom-domains/{domain}/verify

Verify DNS configuration for a domain

Success Response (200 OK)

{
  "domain": { ... },
  "verified": true,
  "cfStatus": "active"
}

Rate Limits

Rate limits are applied per IP address in a 60-second rolling window.

EndpointLimit
/api/pages (except /raw)20 requests per 60 seconds
/api/pages/.../raw60 requests per 60 seconds
/api/api-keys10 requests per 60 seconds

Rate Limit Headers

All responses include rate limit information in headers:

X-RateLimit-Limit: 20
X-RateLimit-Remaining: 15

When rate limited, you'll receive a 429 Too Many Requests response with a Retry-After: 60 header.

Error Codes

CodeDescription
400
Bad Request
Invalid request body, missing required fields, or malformed data
401
Unauthorized
Missing or invalid API key
403
Forbidden
Insufficient permissions (e.g., page limit reached, Pro plan required)
404
Not Found
Page does not exist or has expired
405
Method Not Allowed
Wrong authentication method for this endpoint
409
Conflict
Slug already exists for a different page
413
Payload Too Large
HTML content exceeds 5MB limit
429
Too Many Requests
Rate limit exceeded. Check Retry-After header
500
Internal Server Error
Unexpected server error

Error Response Format

{
  "error": "Error message description"
}

Plan Limits

API access requires the Pro plan. Here's what each plan includes:

FeatureFreeStarter ($10/mo)Pro ($29/mo)
Max Pages525100
Page Expiry7 daysNeverNever
Custom Slugs
API Access
Max API Keys005
Custom Domains
Analytics
Max Asset Size2MB5MB10MB
Total Storage50MB100MB500MB
Assets per Page1550Unlimited