deed.page is free to start: no signup, no email, no card. The Free plan has limits, and an agent that knows them can plan around them instead of failing halfway through a job. Here is each limit, the exact response at that limit, and what to do next.
Every response below was captured against production on October 20, 2026. The test site used X-Ttl: 900 and was deleted afterwards.
The numbers
| Free | Hobby ($3/mo) | Pro ($15/mo) | |
|---|---|---|---|
| Sites per token | 25 | 250 | 2,500 |
| Size per site | 25 MB | 100 MB | 500 MB |
| Publishes per hour | 60 | 300 | 1,200 |
| Retained versions | 10 | 50 | unlimited |
| Custom domains | 1 | 3 | 20 |
| Webhooks | 1 | 5 | 20 |
| Site password | no | yes | yes |
Two limits apply on every plan. A single request body is capped at 4 MB, and there are per-address caps on how fast new tokens and new sites are created (more on those below).
Check where you stand first
GET /v1/whoami returns your plan and current usage. Call it at startup and before a large batch:
curl -sS https://deed.page/v1/whoami \
-H "Authorization: Bearer $DEEDPAGE_TOKEN"
{"ok":true,"authenticated":true,"principal":"prin_…",
"plan":{"id":"free","name":"Free","priceUsd":0,"maxSites":25,
"maxBytesPerSite":26214400,"maxPublishesPerHour":60,
"versionRetention":10,"maxDomains":1,"password":false,"maxWebhooks":1},
"sites":1,"site_limit":25,"domains":0,"domain_limit":1, ...}Without a token you get "authenticated":false and the Free plan object, so an agent can read the limits before its first publish. Over MCP, the whoami tool returns the same plan and counts:
curl -sS https://deed.page/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "Authorization: Bearer $DEEDPAGE_TOKEN" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"whoami","arguments":{}}}'sites against site_limit is the number to watch.
413 request_too_large: the body is over 4 MB
This is a transport limit, not a plan limit. A 4.4 MB body is refused before anything is stored:
{"code":"request_too_large",
"message":"Request body is 4400000 bytes; the per-request cap is 4194304. Split the site and send the rest with X-Merge: 1.",
"retry_after":null,"docs_url":"https://deed.page/docs#errors"}Both ways around it keep the same permanent URL: send the site in parts with X-Merge: 1 (how), or use a manifest upload so the whole site lands as one version (how).
413 quota_bytes: the site is over 25 MB
The plan size limit counts the whole site, including files kept by a merge. The manifest upload checks it before you send any bytes, which makes it a cheap way to test a large site. Here a planned 30 MB file was refused at the planning step:
curl -sS -X POST https://deed.page/v1/uploads \
-H "Authorization: Bearer $DEEDPAGE_TOKEN" \
-H "Content-Type: application/json" \
-d '{"slug":"acme-report-7f3k","files":[{"path":"video.mp4","sha256":"<64 hex>","size":31457280}]}'
{"code":"quota_bytes",
"message":"File video.mp4 is 31457280 bytes; Free allows 26214400 per site. See /pricing.",
"retry_after":null,"docs_url":"https://deed.page/docs#errors"}Two 15 MB files get the same code ("Site would be 31457280 bytes; …"). Shrink the assets, split the content across two sites, or upgrade.
403 quota_sites: the token already owns 25 sites
A publish to a new slug is refused once the token owns as many live sites as the plan allows. The message is "Free allows 25 sites. See /pricing." Updates to slugs you already own still work. A site past its X-Ttl stops counting: the next publish, list, or whoami drops it from the owner index, and the daily sweep does the same. Deleting a site you no longer need frees a slot right away:
curl -sS -X DELETE https://deed.page/v1/sites/acme-old-draft \
-H "Authorization: Bearer $DEEDPAGE_TOKEN"
{"ok":true,"deleted":"acme-old-draft"}After that delete, whoami showed "sites":0 for our test token. sites is the number that counts toward site_limit. Expired sites are not in it.
429 rate_limited: wait for the window
Hourly limits use clock-hour windows. A 429 includes retry_after (seconds until the window ends) in the body and a Retry-After header. The hourly caps are:
- publishes per token: 60 on Free, 300 on Hobby, 1,200 on Pro
- new tokens: 10 per address and 30 per network per hour
- new sites: 30 per address per hour
The new-token cap is the easiest to hit by accident: an agent that publishes without its token mints a new one every time. Store the first token and reuse it (token hygiene).
This shell function retries only on 429 and gives up on any other error:
publish() {
for attempt in 1 2 3; do
code=$(curl -sS -o resp.json -w '%{http_code}' -X PUT --data-binary @index.html \
-H "Content-Type: text/html" \
-H "Authorization: Bearer $DEEDPAGE_TOKEN" \
"https://deed.page/v1/sites/$1")
case "$code" in
2??) jq -c '{url, version, unchanged}' resp.json; return 0 ;;
429) wait=$(jq -r '.retry_after // 60' resp.json)
echo "rate limited; retrying in ${wait}s" >&2
sleep "$wait" ;;
*) jq -c '{code, message}' resp.json >&2; return 1 ;;
esac
done
return 1
}Because the window is a clock hour, retry_after can be close to 3,600 seconds. A cron job may do better to skip the run than to sleep that long.
Upgrading from inside the agent
When a limit is a real constraint, the upgrade is one call and keeps the same token and sites. POST /api/v1/billing/checkout with {"plan":"hobby"} or {"plan":"pro"} and the Bearer token returns a checkout_url. The MCP tool get_upgrade_link does the same. Neither call charges anything. A person pays on the Stripe Checkout page, and the plan on that token changes once Stripe confirms. Without a token, both return unauthorized. Plans are listed at deed.page/pricing.
If the same agent also needs an email inbox, for example to attach the optional recovery address to its token, Cooper Email gives agents inboxes and comes from the same team.