Skip to content

HTTP API

Everything under /_/api/v1, authenticated with a bearer token. A token is not a cookie: a cross-site request cannot borrow one, so these routes carry no CSRF protection and the browser UI's cookie does not work here. Two doors, one lock each.

Authorization: Bearer cdn_<key id>_<secret>

Objects

List

GET /_/api/v1/zones/{zone}/objects
parameter default
prefix only paths starting with it
delimiter / folds the listing into folders
cursor continue from the last path of the previous page
limit 100 at most 1000

Paging is by path rather than by offset. An object inserted while a client pages through would shift every later row under an offset, so a sync would silently skip one; a path cursor cannot.

With delimiter=/ the answer is a folder view: objects directly under the prefix, plus the names of the folders below it. Needs the read scope.

Upload or replace

PUT /_/api/v1/zones/{zone}/objects/{path}

The body is the bytes. 201 when the object is new, 200 when it replaced one.

header
Content-Type stored and returned; guessed from the path when absent
X-CDN-Cache-Control overrides the zone's caching header for this object

Refused with 413 past CDN_MAX_UPLOAD_BYTES and 507 past a storage quota, in both cases before the body is transferred when the client declares a length. Needs write, and the zone must be a storage zone.

Describe

GET /_/api/v1/zones/{zone}/objects/{path}

Size, etag, content type, timestamps and the public URL. Needs read.

Delete

DELETE /_/api/v1/zones/{zone}/objects/{path}

204, and 204 again for a path that is not there: a delete has to be safe to retry, and a client that timed out cannot know whether the first attempt landed. Needs delete.

Zones

Sign a URL

POST /_/api/v1/zones/{zone}/sign/{path}

{"expires_in": 3600} — seconds. Returns a URL carrying an expiry and a signature. Needs read. See Private links.

Purge

POST /_/api/v1/zones/{zone}/purge

One of three bodies:

{"paths": ["img/logo.png", "img/icon.png"]}
{"prefix": "img/"}
{"all": true}

Answers {"purged": n}. Nothing is deleted — a storage zone's objects stay in S3 and a pull zone's stay at the origin. Needs purge.

For a token with a path prefix, {"all": true} means everything that token can reach.

Statistics

GET /_/api/v1/zones/{zone}/stats?days=30

Totals, a daily series, the busiest paths and a breakdown by outcome. Needs stats.

Status codes

400 a request the client can fix; the message says how
401 no credential, or one that is not ours
403 a credential without the scope, prefix or zone for this
404 no such zone or object — also what another tenant's zone answers, because whether it exists is not something to learn from the difference between two codes
413 past CDN_MAX_UPLOAD_BYTES
429 writing faster than the token's allowance; carries Retry-After
507 past a storage quota

Health

GET /_/health          → {"status": "ok", "database": "ok"}
GET /_/health?deep=1   → also checks the object store

Unauthenticated. See Running it.