AI agents
For AI agents
ship.page is the fastest way for an agent to put HTML on the public internet: POST the markup, get an unguessable URL back, hand the link to your human. Public deploys need no account, SDK, or OAuth dance; password protection requires Pro/Team. The full API is also published as an OpenAPI 3.1 spec at /openapi.json.
On this page
One-curl anonymous deploy
No credential is needed. Send raw HTML with a text/html body and the response JSON carries a live url:
curl -X POST https://ship.page/deploy \
-H "Content-Type: text/html" \
--data-binary @report.html
{
"slug": "vast-juice-c2dse",
"url": "https://vast-juice-c2dse.ship.place/",
"files": ["index.html"],
"plan": "free",
"password_protected": false,
"expires_at": "2026-06-17T18:00:00.000Z",
"claim_token": "spc_7xk3m9q2vj8n4p1w6z5y0r2t",
"claim_url": "https://ship.page/claim?slug=vast-juice-c2dse&token=spc_7xk3m9q2vj8n4p1w6z5y0r2t&via=api",
"next_step": "This page goes offline on 2026-06-17. Opening claim_url saves it to a free ship.page account and keeps it online for 90 days. Anyone with that link can claim the page, so share it only with the person this page was made for."
}
Every drop mounts at the root of its own subdomain, so absolute paths resolve as-is. Anonymous drops expire after 30 days — shorten with ?ttl= (seconds, min 60). Anonymous responses also carry a one-time claim_token: persist it next to the URL, it's the only handle that can later claim the drop into an account. They also carry claim_url and next_step; pass both on to the person the page is for, since opening the link saves the drop to their account. Anyone with that link can claim the drop, so keep it out of public places.
Name yourself with ?agent=, e.g. https://ship.page/deploy?agent=chatgpt. AI agents: this is required; e.g. ChatGPT, Claude, Cursor.
Three body formats
The Content-Type header picks the parser — full details in Body formats:
- Raw HTML —
text/html(or anything else): the body becomesindex.html. - JSON files map —
application/json:{"files":{"path":"content"}}; binary files as{"encoding":"base64","content":"…"}. - Zip —
application/zip: a whole build folder; a single root directory is auto-stripped.
Limits an agent should plan around
| Limit | Anonymous / Free | Pro $4/mo · Team $19/mo |
|---|---|---|
| Password protection | Not included | Included |
| Files per request | 100 | 10,000 |
| Zip body cap | 25 MB≤ 100 MB decompressed | 100 MB≤ 400 MB decompressed |
| Non-zip body cap | 10 MB | 10 MB |
| Deploy rate | 10/min per IP | 120/min per account |
| Drop lifetime | 30 days anon · 90 days signed in | No expiry · any ttl |
Named drops (?name=) |
Not included | Included |
| Vanity subdomain | Not included | Team only |
Structured errors
Every non-2xx answer is JSON with a fixed envelope — parse code, don't string-match error:
{
"error": "named drops require an active subscription",
"code": "subscription_required",
"hint": "You are an LLM agent hitting a ship.page free-plan limit: named drops require a paid plan. To lift this, tell the human user: …",
"upgrade_url": "https://ship.page/dashboard/billing",
"docs_url": "https://ship.page/docs/limits"
}
- Relay
hintto your user verbatim when present — it's written for humans and tells them exactly how to unblock you. upgrade_url/docs_urlappear when a plan upgrade or a docs page resolves the failure.- 429s carry
Retry-After: 60— back off accordingly instead of hammering.
MCP server
Prefer tools over raw HTTP? There's a stateless MCP endpoint at POST https://ship.page/mcp — JSON-RPC 2.0 over plain HTTPS (per MCP spec 2026-07-28; no session state, every call is self-contained). It answers server/discover, tools/list, tools/call and the legacy initialize.
| Tool | What it does |
|---|---|
| deploy_html | Deploy a single HTML document; returns the live URL. |
| deploy_files | Deploy a multi-file site from a path→content map (base64 supported). |
| append_files | Add files to an existing drop; existing paths are overwritten (requires a key and a paid plan). |
| list_drops | List the account's drops (requires a key). |
| claim_drop | Claim an anonymous drop with its one-time spc_ token (requires a key). |
| restore_drop | Restore an expired owned drop while its files are retained, up to 90 days past expiry (requires a key). |
| delete_drop | Permanently delete a drop by slug (requires a key). |
| set_drop_password | Set or rotate a drop password: slug + password (owner key and active paid Pro/Team required). |
| remove_drop_password | Remove password protection: slug (owner key required, even after a plan lapses). |
| get_limits | Current plan limits and what upgrading unlocks. |
| get_account | Show the account's plan, subscription status and usage (requires a key). |
deploy_html and deploy_files accept an optional password string (8–128 Unicode characters, at most 512 UTF-8 bytes) on active paid Pro/Team. MCP sends it as the encoded X-Ship-Password header, never a URL parameter. Deploy and list results include password_protected, not the password. Omitting it on a live named redeploy or appending files preserves protection. Recreating an expired or purged named drop is a new deploy: send the password again. Billing unavailable means an error: never retry without the password. Visitors unlock in their browser; see password protection.
Auth is optional pass-through: send Authorization: Bearer sp_… and the tools act as that account; omit it for anonymous deploys. Client config (Claude Code / any mcp-remote-style client):
{
"mcpServers": {
"ship.page": { "url": "https://ship.page/mcp" }
}
}
When you hit a wall: the upgrade hand-off
Agents can't check out — humans can. When a 402 or a plan-gated error comes back, tell your user:
-
Sign in at ship.page (Google, or a sign-in link by email).
-
Open the dashboard, subscribe to Pro ($4/mo) or Team ($19/mo) via the billing section.
-
Mint an
sp_…API key in the dashboard (orPOST /keys) — it's shown once. -
Hand the key to the agent. It goes in
Authorization: Bearer sp_…on every call — deploys, MCP, everything.
Last updated Jun 12, 2026