Base URL
Examples on these pages use the Cloud host. Self-hosted, swap the origin and every request is otherwise identical. Delivery is the one path that differs: on Cloud it carries a bucket prefix,
/b/{bucketId}/t/..., see your base URL.
Authentication
On Cloud, create keys in the dashboard at app.openinary.dev under Settings → API keys. A key is shown once and is pinned to a single bucket, which is why no endpoint takes a bucket ID. Either header works,Authorization: Bearer or x-api-key.
Self-hosted, create them from the dashboard at /api-keys or through the API Keys API. In API-only mode the first key is printed to the server logs at startup. Every API-key endpoint also accepts a dashboard session cookie, which is how the dashboard calls storage and upload without minting a key for itself.
These endpoints work on both Cloud and self-hosted, with the same request and response shapes. The pages that do not are marked Self-hosted only at the top and use https://media.example.com in their examples. For the full list of what Cloud does not have, see what differs on Cloud.
Endpoints
Rate limiting
Public routes are limited by IP: 100 requests per 60 seconds, tunable withPUBLIC_RATE_LIMIT_MAX and PUBLIC_RATE_LIMIT_WINDOW_MS. Every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset.
Most API-key endpoints sit outside that limiter, but POST /upload and GET /health/database do not, so a burst of uploads can be throttled even with a valid key.
Those numbers are self-hosted. Cloud limits delivery only, 300 requests per 60 seconds per IP and 1,000 per account, and leaves upload, signing and storage calls unlimited. See Cloud rate limits.
Errors
JSON errors carry a shorterror label and a message:
Two endpoints add fields on top of that pair: delete returns a
details object, and invalidate returns an errors array. Neither is a general envelope.