Limits
What the API enforces, and what happens when you reach it.
Per plan
| Free | Pro | |
|---|---|---|
| Projects | 1 | 1 per slot, plus one free-tier project |
| Assets per project | 50 | No published limit |
| Members per project | 2 | No published limit |
| Storage | 250 MB | 2 GB per project slot, pooled |
| Max file size | 5 MB | 25 MB |
| API requests | 5,000 / month | Unmetered |
“No published limit” means exactly that: there is no cap you will meet in normal use. A high backstop exists to stop runaway automation, and you will never see it unless something is wrong.
How storage is counted
Paid storage is pooled across your account. Each project slot grants 2 GB, and every paid project draws from the combined total. Three slots is one 6 GB pool, not three separate 2 GB allowances. A free-tier project keeps its own allowance. The bonus free-tier project that comes with a paid plan has its own 250 MB and is excluded from the account pool entirely — filling it does not touch your paid storage, and vice versa. It follows the free tier's 5 MB file limit too, whoever owns it. You are metered on what is stored, not what you upload. Images are converted before they are measured, so a 9 MB PNG that lands as a 1.4 MB WebP costs you 1.4 MB. The file-size limit applies to the converted result. Delivery is never blocked by storage. Passing your quota stops new uploads. Content already published keeps serving.
Per asset
| Text length | Up to 5,000 characters. Configurable per asset between 5 and 5,000. |
| HTML length | 100,000 characters — roughly 100 KB, and about three times a 5,000-word article, so long-form content belongs in an HTML asset. You can declare a lower ceiling per asset to stop an editor pasting something enormous. |
| JSON length | 50,000 characters |
| Version history — images | 10 versions retained |
| Version history — text, HTML, JSON | 50 versions retained |
Older versions are trimmed automatically once the limit is passed. Images retain fewer because each version keeps its own stored files.
Image variants
Every image asset returns an object of URLs. The sizes are maximum widths — aspect ratio is always preserved, so the height follows your source image.
| Variant | Max width |
|---|---|
| original | As uploaded, after conversion |
| large | 1200 px |
| medium | 800 px |
| thumb | 400 px |
Not every variant always exists.
Variants are never upscaled, so a 500 px upload has no large. GIF and SVG return original only, to keep animation and vector data intact. Fall back to original when the variant you want is absent.
Rate limits
Exceeding a limit returns 429 with a message naming which one you hit. Rate-limited requests are not counted against your quota.
The monthly request limit applies to free and trial plans only. Paid plans are unmetered — there is no monthly ceiling and no overage charge. If you see Monthly API request limit reached for this project on a paid plan, that is a bug worth reporting.
Caching and freshness
Publishing takes effect immediately. The cache is invalidated on publish, so the next request returns the new content. You never wait out a timer to see a change go live. Otherwise responses are cached for 5 minutes. That is the ceiling on how stale a response can be when nothing has been published. Check X-Duggie-App-Cacheon the response to see whether you were served from cache. Preview-token requests bypass the cache entirely, so a draft is always current.Cached responses still count against your monthly requests. The cache protects response times, not your allowance. If you are fetching per page view on the free tier, cache on your own side too — 5,000 a month is roughly 160 views a day.
What the API does and doesn't do
The public API has two endpoints: read your content, and write it in bulk. If you think we should support others, please submit a features request through the contacts page.
No publish webhooks yet
Duggie assumes you fetch content at request time, so there is nothing to notify. If you build statically — Next.js SSG, Astro, Hugo — there is currently no way to trigger a rebuild when a client publishes; you would rebuild on a schedule instead. Worth knowing before you choose an architecture, and worth telling us if it is blocking you.
Batch writes
| Assets per request | 1 to 100 |
| Tag length | Up to 200 characters |
| Types accepted | Text, JSON, HTML. Images are uploaded separately. |
One item with bad content does not fail the batch. Content that breaks a rule is skipped and the rest are applied: invalid JSON, HTML or text over its length limit, or a value that breaks one of the asset's own field rules. The response is 200with a per-item result, so check the body rather than the status code to know what landed.A malformed request is rejected whole. An item missing its tagorcontentis a bad request rather than a bad row, so the call answers400and nothing is written. Fix the payload and send it again.Creation stops at your asset limit. A batch that would take you past it creates what fits and skips the rest, rather than rejecting the whole call. Updates to existing assets are unaffected. Asset type is fixed at creation. Sending a different typefor an asset that already exists does not change it; the existing type wins.