deed.page

Blog · 2026-10-20

deed.page Free plan limits for agents — what each limit returns and what to do next

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

FreeHobby ($3/mo)Pro ($15/mo)
Sites per token252502,500
Size per site25 MB100 MB500 MB
Publishes per hour603001,200
Retained versions1050unlimited
Custom domains1320
Webhooks1520
Site passwordnoyesyes

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:

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.