Public API

One HTTP call to publish AI-generated content as a public, shareable web page. Anonymous, rate-limited, CORS-permissive.

Endpoint

POST https://quicky.page/api/v1/publish

Send Content-Type: application/json. No API key. No signup. Rate-limited at 30 publishes / 5 minutes / IP. Permissive CORS — call it from curl, a server, a browser, an extension, or an MCP server.

Markdown shape (recommended for agents)

Pass an optional title and a markdown content body. The server converts to qp.v1 blocks for you.

curl -X POST https://quicky.page/api/v1/publish \
  -H 'content-type: application/json' \
  -d '{
    "title": "My AI Summary",
    "content": "# Hello\n\nA paragraph with **bold** and a [link](https://example.com).\n\n- one\n- two"
  }'

Supported markdown subset:

  • # / ## / ### headings (deeper collapses to h3)
  • Paragraphs separated by blank lines
  • Unordered (-, *, +) and ordered (1.) lists
  • **bold**, *italic*, _underline_, `inline code`
  • [label](url) links
  • Standalone ![alt](url) image lines
  • Fenced code blocks (``` or ~~~, optional language hint) → code block
  • Horizontal rules (---, ___, ***) → divider block
  • Blockquotes (> line) → quote block
  • GitHub-style callouts (> [!NOTE|TIP|IMPORTANT|WARNING|CAUTION]) → calloutblock. The 5 GitHub tones collapse to qp's 3-tone palette: NOTE → info, TIP → success, IMPORTANT / WARNING / CAUTION → warning.

Tables, footnotes, and inline HTML fall through as plain text — qp.v1's renderer doesn't display them.

Structured shape

Pass an explicit blocks array when you need control over block types (e.g., embeds).

curl -X POST https://quicky.page/api/v1/publish \
  -H 'content-type: application/json' \
  -d '{
    "title": "My AI Summary",
    "blocks": [
      { "type": "richtext", "html": "<h2>Subhead</h2><p>Paragraph.</p>" },
      { "type": "image", "url": "https://example.com/diagram.png", "alt": "Diagram" }
    ]
  }'

Each block is one of richtext, image, embed, html, callout, quote, divider, timeline, comparison, code, or button. All block content is sanitized server-side; unsafe HTML, scripts, and bad URLs are dropped before storage.

qp.v1 schema

The full block discriminated union and the publish payload envelope:

// qp.v1 — block schema for the publish API.
// Pages are a flat, ordered array of these discriminated-union members.

type Block =
  | { type: "richtext"; html: string }
  | { type: "image"; url: string; alt?: string }
  | {
      type: "embed";
      embedType: "youtube" | "twitter" | "figma" | "codepen" | "iframe_sandbox";
      url: string;
      title?: string;
    }
  | { type: "html"; html: string; safetyMode: "strict_sanitized" }
  | {
      type: "callout";
      tone: "info" | "warning" | "success";
      html: string;
    }
  | { type: "quote"; html: string; attribution?: string }
  | { type: "divider" }
  | {
      type: "timeline";
      items: Array<{ label: string; body: string }>;
    }
  | {
      type: "comparison";
      left: string;
      right: string;
      leftLabel?: string;
      rightLabel?: string;
    }
  | {
      type: "code";
      code: string;
      language?: "plaintext" | "ts" | "tsx" | "js" | "jsx" | "json" | "html"
                | "css" | "py" | "rb" | "go" | "rust" | "java" | "c" | "cpp"
                | "csharp" | "swift" | "kotlin" | "php" | "sh" | "sql"
                | "yaml" | "toml" | "xml" | "md" | "diff";
      filename?: string;
    }
  | {
      type: "button";
      url: string;
      label: string;
      description?: string;
      variant: "filled" | "outline" | "soft";
      shape: "rounded" | "pill" | "square";
      accent: "theme" | "slate" | "blue" | "green" | "amber"
              | "red" | "purple" | "pink";
      icon?: string;
    };

// Top-level payload when posting blocks directly.
type PageOutput = {
  version: "qp.v1";
  blocks: Block[];
};

Response

{
  "id": "abc123",
  "url": "https://quicky.page/abc123",
  "editUrl": "https://quicky.page/?id=abc123#edit=...",  // private edit link; treat as a secret
  "editKey": "..."  // 24-char raw secret; same key in another form, for programmatic updates
}
  • url is the public link. Anyone with it can read the page. Safe to share.
  • editUrl is the private edit link in the exact format <base>/?id=<id>#edit=<editKey> — id in the querystring, editKey in the URL fragment (the fragment is never sent to the server, so the secret never appears in access logs or in the Referer header on outbound clicks). Opens the editor in one click. Anyone with this URL can edit the page — treat as a secret. Give this to the user; do not reconstruct it from editKey yourself.
  • editKey is the same secret in raw form. Use it as a request-body argument for programmatic updates (POST /api/v1/publish with id + editKey) or for delete/rename (PATCH/DELETE /api/v1/pages/<id>). There is no recovery if you lose it — just publish a new page.

Updating a page

POST again with id and editKey. The same URL stays live; the new content fully replaces the old. There is no diff, no history, no rollback — Quicky.Page is intentionally not a CMS.

curl -X POST https://quicky.page/api/v1/publish \
  -H 'content-type: application/json' \
  -d '{
    "id": "abc123",
    "editKey": "...",
    "content": "# New title\n\nUpdated body."
  }'

Other languages

JavaScript (fetch)

const res = await fetch("https://quicky.page/api/v1/publish", {
  method: "POST",
  headers: { "content-type": "application/json" },
  body: JSON.stringify({
    title: "My AI Summary",
    content: "# Hello\n\nA paragraph.",
  }),
});
const { id, url, editUrl, editKey } = await res.json();
console.log(url);     // -> https://quicky.page/abc123  (public, share this)
console.log(editUrl); // -> https://quicky.page/?id=abc123#edit=...  (private, edit link)

Python (requests)

import requests

r = requests.post(
    "https://quicky.page/api/v1/publish",
    json={
        "title": "My AI Summary",
        "content": "# Hello\n\nA paragraph.",
    },
)
r.raise_for_status()
data = r.json()
print(data["url"])      # -> https://quicky.page/abc123
print(data["editUrl"])  # -> https://quicky.page/?id=abc123#edit=...

Errors

StatusMeaning
400Input couldn't be parsed or no usable content was provided. Body: { "error": "..." }.
403editKey missing or wrong on an update.
404Unknown id on an update.
429Rate limit exceeded. Response includes Retry-After and X-RateLimit-* headers.
500Server error. Retry with backoff.

Title injection

When you supply title, it becomes a leading <h1>on the page — but only if the body doesn't already start with one. Sending both a title and a markdown content that opens with #won't stack two headings; the explicit one wins.

Reading a page back

GET https://quicky.page/api/v1/pages/<id>

Returns the page's content as JSON. Read-only; no editKey required.


Want to publish from inside an AI client? See the MCP server (Claude Desktop, Cursor) or the ChatGPT Custom GPT. Want a one-click publish button in your browser? See the browser extension.