PageDrop API Documentation

PageDrop lets you deploy HTML pages instantly via a simple REST API. Send your HTML in a POST request and receive a live URL in the response. Sites are hosted forever by default, or you can set a custom TTL. No signup, no API keys, no authentication required.

Base URL: https://pagedrop.dev/api/v1

Authentication

No authentication is required. The API is completely free and open. Simply make HTTP requests to the endpoints below. Delete operations require the X-Delete-Token header, which is provided when a site is created.

POST /sites

POST /api/v1/sites

Create a new hosted site. Accepts JSON with raw HTML, Markdown, or a multipart form upload with a file (HTML, PDF, or ZIP).

Option 1: JSON Body

FieldTypeRequiredDescription
html string required* Raw HTML content to host. Max 15 MB. *Provide html or markdown, not both.
markdown string required* Markdown content — auto-rendered to a responsive, styled HTML page with dark/light mode support. Max 15 MB. *Provide html or markdown, not both.
title string optional Page title for markdown rendering. Auto-detected from the first # heading if omitted.
ttl string optional Time-to-live. Format: number + unit (h=hours, d=days, m=months). Examples: 1d, 12h, 30d. Default: never expires (permanent).
slug string optional Custom vanity slug for the URL. 3-64 characters, lowercase alphanumeric and hyphens. Example: "my-project"/s/my-project. Must be unique.
ogTitle string optional Custom Open Graph title for social media previews. Max 200 characters. Injected as <meta property="og:title"> when the page is served.
ogDescription string optional Custom Open Graph description for social media previews. Max 500 characters. Also sets <meta name="description">.
ogImage string optional URL to an Open Graph image (1200x630px recommended). Enables twitter:card=summary_large_image for rich Twitter/X previews.
password string optional Password-protect the page. Visitors must enter this password before viewing the content.
passwordExpiry number optional Password cookie expiry in hours (1–720). Default: 24. After this period, visitors must re-enter the password.

Option 2: Multipart Upload

FieldTypeRequiredDescription
file file required HTML file (.html), PDF file (.pdf), or ZIP archive (.zip) containing a project with an index.html at the root. PDF files are auto-wrapped in a viewer page. Max 10MB for PDF and ZIP.

Example Request (JSON)

cURL
curl -X POST "https://pagedrop.dev/api/v1/sites" \
  -H "Content-Type: application/json" \
  -d '{"html": "<h1>Hello World</h1><p>Deployed with PageDrop!</p>"}'

Example Request (Markdown)

cURL
curl -X POST "https://pagedrop.dev/api/v1/sites" \
  -H "Content-Type: application/json" \
  -d '{
    "markdown": "# Hello World\n\nThis is **bold** and this is `code`.\n\n## Features\n\n- Auto-styled HTML\n- Dark/light mode\n- Responsive design",
    "slug": "my-readme"
  }'

Example Request (JSON with OG Tags)

cURL
curl -X POST "https://pagedrop.dev/api/v1/sites" \
  -H "Content-Type: application/json" \
  -d '{
    "html": "<h1>My Demo</h1>",
    "ogTitle": "My Demo Page",
    "ogDescription": "Check out this awesome demo hosted on PageDrop",
    "ogImage": "https://example.com/preview.png"
  }'

Example Request (PDF Upload)

cURL
curl -X POST "https://pagedrop.dev/api/v1/sites" \
  -F "file=@document.pdf"

Example Request (ZIP Upload)

cURL
curl -X POST "https://pagedrop.dev/api/v1/sites" \
  -F "file=@my-project.zip"

Success Response

201 Created
{
  "status": "success",
  "data": {
    "siteId": "a1b2c3d4",
    "url": "https://pagedrop.dev/s/a1b2c3d4",
    "deleteToken": "dlt_e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8",
    "expiresAt": null,
    "ttlDays": null,
    "files": ["index.html"],
    "totalSizeBytes": 1234,
    "viewCount": 0,
    "createdAt": "2026-02-17T12:00:00.000Z",
    "passwordProtected": false
  }
}
Save the deleteToken — it is only returned once at creation time. You need it to update or delete the site later.

PUT /sites/:siteId

PUT /api/v1/sites/:siteId

Update an existing site's content without changing the URL. Requires the delete token from creation. Accepts the same content formats as POST.

Headers

HeaderRequiredDescription
X-Delete-Token required The delete token returned when the site was created.

Example Request

cURL
curl -X PUT "https://pagedrop.dev/api/v1/sites/my-project" \
  -H "Content-Type: application/json" \
  -H "X-Delete-Token: dlt_your_token_here" \
  -d '{"html": "<h1>Updated Content</h1>"}'

Success Response

200 OK
{
  "status": "success",
  "data": {
    "siteId": "my-project",
    "url": "https://pagedrop.dev/s/my-project",
    "expiresAt": null,
    "files": ["index.html"],
    "totalSizeBytes": 512,
    "updatedAt": "2026-02-27T12:00:00.000Z"
  }
}

GET /sites/:siteId

GET /api/v1/sites/:siteId

Retrieve metadata about a hosted site.

Path Parameters

ParamTypeDescription
siteId string The unique site identifier returned at creation

Example Response

200 OK
{
  "status": "success",
  "data": {
    "siteId": "a1b2c3d4",
    "url": "https://pagedrop.dev/s/a1b2c3d4",
    "createdAt": "2026-02-17T12:00:00.000Z",
    "expiresAt": null,
    "fileCount": 1,
    "totalSizeBytes": 1234,
    "viewCount": 42,
    "files": ["index.html"]
  }
}

DELETE /sites/:siteId

DELETE /api/v1/sites/:siteId

Delete a hosted site. Requires the X-Delete-Token header.

Headers

HeaderRequiredDescription
X-Delete-Token required The delete token returned when the site was created

Example Request

cURL
curl -X DELETE "https://pagedrop.dev/api/v1/sites/a1b2c3d4" \
  -H "X-Delete-Token: dlt_e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8"

Success Response

200 OK
{
  "status": "success",
  "message": "Site a1b2c3d4 has been deleted"
}

POST /sites/batch

POST /api/v1/sites/batch

Create multiple sites in a single request. Perfect for deploying AI-generated pages, student assignments, or multi-page projects. Supports HTML and Markdown content. Maximum 20 sites per request.

Request Body (JSON)

FieldTypeDescription
sites Array Array of site objects (max 20). Each object supports the same fields as POST /sites: html or markdown (required), plus optional title, slug, ttl, ogTitle, ogDescription, ogImage, password, passwordExpiry.

Example Request

cURL
curl -X POST "https://pagedrop.dev/api/v1/sites/batch" \
  -H "Content-Type: application/json" \
  -d '{
    "sites": [
      { "html": "<h1>Page One</h1>", "slug": "page-one", "ttl": "7d" },
      { "markdown": "# Page Two\nHello world", "title": "My Doc" },
      { "html": "<h1>Page Three</h1>" }
    ]
  }'

Success Response

201 Created
{
  "status": "success",
  "data": {
    "created": [
      {
        "siteId": "page-one",
        "url": "https://pagedrop.dev/s/page-one",
        "deleteToken": "dlt_abc123...",
        "expiresAt": "2026-03-20T12:00:00.000Z",
        "files": ["index.html"],
        "totalSizeBytes": 22
      },
      {
        "siteId": "a1b2c3d4",
        "url": "https://pagedrop.dev/s/a1b2c3d4",
        "deleteToken": "dlt_def456...",
        "expiresAt": null,
        "files": ["index.html"],
        "totalSizeBytes": 150
      },
      {
        "siteId": "e5f6g7h8",
        "url": "https://pagedrop.dev/s/e5f6g7h8",
        "deleteToken": "dlt_ghi789...",
        "expiresAt": null,
        "files": ["index.html"],
        "totalSizeBytes": 24
      }
    ],
    "failed": [],
    "totalCreated": 3,
    "totalFailed": 0
  }
}

POST /sites/batch/delete

POST /api/v1/sites/batch/delete

Delete multiple sites in a single request. Ideal for CI/CD cleanup pipelines and batch management. Maximum 50 sites per request.

Request Body (JSON)

FieldTypeDescription
sites Array Array of objects, each with siteId (string) and deleteToken (string)

Example Request

cURL
curl -X POST "https://pagedrop.dev/api/v1/sites/batch/delete" \
  -H "Content-Type: application/json" \
  -d '{
    "sites": [
      { "siteId": "abc123", "deleteToken": "dlt_token1..." },
      { "siteId": "def456", "deleteToken": "dlt_token2..." }
    ]
  }'

Success Response

200 OK
{
  "status": "success",
  "data": {
    "deleted": ["abc123"],
    "failed": [
      { "siteId": "def456", "error": "Invalid delete token" }
    ],
    "totalDeleted": 1,
    "totalFailed": 1
  }
}

POST /sites/:siteId/fork

POST /api/v1/sites/:siteId/fork

Fork (clone) a public site — creates an independent copy with new ownership. The original site is unchanged. The forked site gets its own delete token. Cannot fork password-protected sites.

Request Body (JSON, all optional)

FieldTypeDescription
slug string Custom vanity slug for the forked site
ttl string Time-to-live for the fork (e.g. "7d", "1h")
ogTitle string Override OG title (inherits from source if omitted)
ogDescription string Override OG description (inherits from source if omitted)
ogImage string Override OG image URL (inherits from source if omitted)

Example Request

cURL
curl -X POST "https://pagedrop.dev/api/v1/sites/abc123/fork" \
  -H "Content-Type: application/json" \
  -d '{ "slug": "my-remix" }'

Success Response

201 Created
{
  "status": "success",
  "data": {
    "siteId": "my-remix",
    "url": "https://pagedrop.dev/s/my-remix",
    "deleteToken": "dlt_...",
    "expiresAt": null,
    "files": ["index.html"],
    "totalSizeBytes": 42,
    "forkedFrom": "abc123",
    "createdAt": "2026-03-17T..."
  }
}

GET /sites/:siteId/analytics

GET /api/v1/sites/:siteId/analytics

Get detailed view analytics for a site. Returns daily view counts, top referrers, and the 20 most recent individual views. Requires the delete token — only the site owner can access analytics.

Headers

HeaderRequiredDescription
X-Delete-Token required The delete token returned when the site was created

Example Request

cURL
curl "https://pagedrop.dev/api/v1/sites/a1b2c3d4/analytics" \
  -H "X-Delete-Token: dlt_e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8"

Success Response

200 OK
{
  "status": "success",
  "data": {
    "siteId": "a1b2c3d4",
    "viewCount": 42,
    "recentViews": [
      { "timestamp": "2026-03-11T14:30:00.000Z", "referrer": "https://twitter.com/someone/status/123" },
      { "timestamp": "2026-03-11T14:25:00.000Z", "referrer": null },
      { "timestamp": "2026-03-11T13:10:00.000Z", "referrer": "https://google.com" }
    ],
    "dailyViews": {
      "2026-03-11": 15,
      "2026-03-10": 12,
      "2026-03-09": 15
    },
    "topReferrers": [
      { "referrer": "(direct)", "count": 20 },
      { "referrer": "https://twitter.com/someone/status/123", "count": 12 },
      { "referrer": "https://google.com", "count": 10 }
    ]
  }
}
Analytics data is based on the last 100 page views. The viewCount is the total lifetime count. Daily views and referrer breakdowns are computed from tracked recent views.

GET /sites/:siteId/files

GET /api/v1/sites/:siteId/files

List all files in a hosted site. Useful for ZIP-deployed sites with multiple assets.

Example Response

200 OK
{
  "status": "success",
  "data": {
    "siteId": "a1b2c3d4",
    "files": [
      { "path": "index.html", "contentType": "text/html; charset=utf-8" },
      { "path": "style.css", "contentType": "text/css" },
      { "path": "app.js", "contentType": "application/javascript" }
    ],
    "totalSizeBytes": 8192
  }
}

GET /s/:siteId

GET /s/:siteId

View the hosted site in a browser. This is the public URL users visit to see the deployed page. Returns the raw HTML content with appropriate Content-Type headers.

This endpoint is outside the /api/v1 namespace. It serves the hosted content directly, not JSON metadata.

GET /embed/:siteId

GET /embed/:siteId

Serve a hosted site in an iframe-friendly format. Uses Cross-Origin-Resource-Policy: cross-origin headers that allow embedding in blog posts, Notion pages, and other platforms. Password-protected sites return 403.

Use this endpoint when you need to embed a PageDrop site inside an <iframe>. Example: <iframe src="https://pagedrop.dev/embed/my-page" width="100%" height="500"></iframe>

Response Format

All API endpoints return JSON. Successful responses are wrapped in a standard envelope with status: "success" and a data object. Error responses include error and message fields.

Error Response

400 Bad Request
{
  "error": "Validation failed",
  "message": "Request body must include 'html' field or upload a file"
}

Error Codes

StatusMeaning
400Bad Request — Missing or invalid input (no HTML, invalid ZIP, exceeds size limit)
401Unauthorized — Invalid or missing X-Delete-Token for DELETE operations
403Forbidden — Cannot fork or embed a password-protected site
404Not Found — Site does not exist or has expired
413Payload Too Large — HTML exceeds 5 MB or ZIP exceeds 10 MB
429Too Many Requests — Rate limit exceeded
500Internal Server Error — Something went wrong on our end

Rate Limits

The API applies rate limiting to protect service quality:

LimitValue
Site creations per minute10 per IP address
Read requests per minute60 per IP address
Window60 seconds sliding window

When rate limited, the API returns a 429 status with a Retry-After header.

Rate limit headers are included in every response: RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset.

Size Limits

ResourceLimit
HTML body (JSON)5 MB
PDF upload10 MB
ZIP upload10 MB
Hosting durationForever (default), or set custom TTL up to 365 days via ttl parameter
Files per ZIP50 files max

cURL Examples

terminal
# Deploy raw HTML
curl -X POST "https://pagedrop.dev/api/v1/sites" \
  -H "Content-Type: application/json" \
  -d '{"html": "<h1>Hello!</h1>"}'

# Upload a PDF file
curl -X POST "https://pagedrop.dev/api/v1/sites" \
  -F "file=@document.pdf"

# Upload a ZIP project
curl -X POST "https://pagedrop.dev/api/v1/sites" \
  -F "file=@my-project.zip"

# Get site metadata
curl "https://pagedrop.dev/api/v1/sites/a1b2c3d4"

# List files in a site
curl "https://pagedrop.dev/api/v1/sites/a1b2c3d4/files"

# Delete a site
curl -X DELETE "https://pagedrop.dev/api/v1/sites/a1b2c3d4" \
  -H "X-Delete-Token: dlt_YOUR_TOKEN_HERE"

JavaScript Example

app.js
// Deploy HTML
const response = await fetch("https://pagedrop.dev/api/v1/sites", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    html: "<h1>Hello World</h1><p>Deployed with PageDrop</p>"
  })
});

const { data } = await response.json();
console.log(`Live at: ${data.url}`);
console.log(`Expires: ${data.expiresAt ?? "Never"}`);
console.log(`Delete token: ${data.deleteToken}`); // Save this!

// Get site metadata
const meta = await fetch(`https://pagedrop.dev/api/v1/sites/${data.siteId}`);
console.log(await meta.json());

// Upload a PDF file
const pdfForm = new FormData();
pdfForm.append("file", pdfBlob, "document.pdf");
const pdfUpload = await fetch("https://pagedrop.dev/api/v1/sites", {
  method: "POST",
  body: pdfForm
});
console.log(await pdfUpload.json());

// Upload a ZIP file
const formData = new FormData();
formData.append("file", zipFileBlob, "project.zip");
const upload = await fetch("https://pagedrop.dev/api/v1/sites", {
  method: "POST",
  body: formData
});
console.log(await upload.json());

// Delete a site
await fetch(`https://pagedrop.dev/api/v1/sites/${data.siteId}`, {
  method: "DELETE",
  headers: { "X-Delete-Token": data.deleteToken }
});

Python Example

main.py
import requests

# Deploy HTML
response = requests.post(
    "https://pagedrop.dev/api/v1/sites",
    json={"html": "<h1>Hello World</h1><p>Deployed with PageDrop</p>"}
)
result = response.json()
site = result["data"]
print(f"Live at: {site['url']}")
print(f"Expires: {site['expiresAt'] or 'Never'}")
print(f"Delete token: {site['deleteToken']}")  # Save this!

# Get site metadata
meta = requests.get(f"https://pagedrop.dev/api/v1/sites/{site['siteId']}")
print(meta.json())

# Upload a PDF file
with open("document.pdf", "rb") as f:
    pdf_upload = requests.post(
        "https://pagedrop.dev/api/v1/sites",
        files={"file": ("document.pdf", f, "application/pdf")}
    )
    print(pdf_upload.json())

# Upload a ZIP file
with open("my-project.zip", "rb") as f:
    upload = requests.post(
        "https://pagedrop.dev/api/v1/sites",
        files={"file": ("project.zip", f, "application/zip")}
    )
    print(upload.json())

# Delete a site
requests.delete(
    f"https://pagedrop.dev/api/v1/sites/{site['siteId']}",
    headers={"X-Delete-Token": site["deleteToken"]}
)

MCP Integration

PageDrop supports the Model Context Protocol (MCP), allowing AI assistants like Claude, VS Code Copilot, Cursor, and other MCP-compatible clients to deploy and manage HTML sites directly.

Installation

Add the following to your claude_desktop_config.json (or equivalent MCP client configuration):

claude_desktop_config.json
{
  "mcpServers": {
    "pagedrop": {
      "command": "npx",
      "args": ["-y", "pagedrop-mcp"]
    }
  }
}

Available Tools

ToolDescriptionParameters
deploy_html Deploy HTML or Markdown content and get a live URL instantly html or markdown (one required), title, ttl, slug, ogTitle, ogDescription, ogImage, password, passwordExpiry (all optional)
get_site_info Retrieve metadata about a hosted site siteId (required)
delete_site Delete a hosted site using its delete token siteId (required), deleteToken (required)
batch_delete Delete multiple sites at once (max 50) sites (required) — array of {siteId, deleteToken}

Environment Variables

VariableDefaultDescription
PAGEDROP_BASE_URL https://pagedrop.dev Override the base URL for API requests (useful for self-hosted instances)
Part of the SoftVoyagers Ecosystem