Assets and images

Every image and file in a Content Delivery API response has an absolute, permanent URL that browsers and CDNs may cache for a year. Images can be resized and converted on request with w, h, fit, fmt and q. This page is for developers who show Spatibase images and files on a website or in an app.

What does an asset URL look like?

An asset URL has the form https://api.spatibase.com/assets/<assetId>/<filename>. Assets appear in images and files fields (Field values) as objects like this:

json
{"id": "b8251bf8-2fab-4001-a3a1-49465ebcabfb", "url": "https://api.spatibase.com/assets/b8251bf8-2fab-4001-a3a1-49465ebcabfb/redwood-national-park-1.jpg", "filename": "redwood-national-park-1.jpg", "mimeType": "image/jpeg", "size": 31475, "width": 1600, "height": 1000, "alt": "Redwood National Park, trailhead view"}
  • The file route is outside /delivery/v1 and not scoped to a project. The asset is found by its id; the filename part is cosmetic, so /assets/<id> or any other name returns the same file.
  • url is always absolute. Links to the project’s files inside markdown fields are rewritten to the same absolute form.
  • A file’s content never changes under its id. Editing the alt text or the filename changes the JSON, not the bytes, and old URLs with the previous filename keep working. Deleting an asset makes its URL return 404 and removes it from fields. A deleted asset stays in the trash for 30 days: restoring it brings its URL back, and once it is deleted for good the URL stays 404.

The asset URL and its parameters are also described in both OpenAPI files (getAssetFile).

Are asset URLs public?

Yes. Asset URLs work without an API key, also in private projects, because an <img> tag cannot send one. Anyone with the URL can download the file, whether or not an entry using it is published; an uploaded file that is used nowhere is still served. Asset ids are long random UUIDs, but Spatibase does not sign file URLs, so do not upload anything private.

Which files count as images?

Spatibase treats a file as an image when its content is JPEG, PNG, GIF, WebP, AVIF or SVG, whatever its extension. Only images accept the resize and format parameters.

Images Other files
Detected by file content: JPEG, PNG, GIF, WebP, AVIF, SVG everything else, including TIFF and HEIC
mimeType image/jpeg, image/png, image/gif, image/webp, image/avif, image/svg+xml from the upload, else from the extension, else application/octet-stream
width, height displayed size in pixels (EXIF rotation applied; first frame for animations) null
Resize parameters applied ignored, even invalid ones: trail-map.pdf?w=200&fmt=webp returns the PDF

Each uploaded file can be at most 25 MB (Limits).

How do I resize or convert an image?

Add query parameters to the image URL. This returns an 800 px wide WebP version:

text
https://api.spatibase.com/assets/b8251bf8-2fab-4001-a3a1-49465ebcabfb/redwood-national-park-1.jpg?w=800&fmt=webp
Parameter Values Default Meaning
w 1 to 4000 none target width in pixels, rounded up to the size ladder
h 1 to 4000 none target height in pixels, rounded up to the size ladder
fit cover, contain, inside cover with both w and h, otherwise inside cover: crop to fill the box; contain: fit inside and pad to the box; inside: fit inside, no padding
fmt webp, jpeg (or jpg), png, avif the source format output format
q 1 to 100 80 quality for webp, jpeg and avif, rounded to the nearest 10 (at least 10); ignored for png and gif

Values are case-insensitive (fmt=WEBP works). Each parameter may appear once. Out-of-range or unknown values on an image are a 400:

json
{"error":{"code":"bad_request","message":"\"fmt\" must be one of webp, jpeg, png, avif (got \"gif\")"}}

Size ladder

w and h snap up to the next of these sizes: 16, 32, 48, 64, 96, 128, 160, 200, 256, 320, 400, 480, 512, 640, 800, 960, 1024, 1280, 1600, 1920, 2048, 2560, 3200, 3840. Values above 3840 become 3840. Then:

  • Raster images are never enlarged: the result is at most the original size.
  • With only w or only h, the other side follows the aspect ratio.
  • With both, and cover or contain, the box keeps its shape and shrinks until it fits the original.
  • w and h snap independently, so w=400&h=300 becomes a 400×320 box.

Results for a 1600×1000 JPEG:

Query Output
?w=400 400×250
?w=401 480×300
?h=100 205×128
?w=3000 1600×1000 (not enlarged)
?w=400&h=300 400×320, cropped (cover)
?w=400&h=300&fit=contain 400×320, padded
?w=400&h=300&fit=inside 400×250
?w=500&h=500 512×512, cropped
?w=1000&h=1000&fit=inside 1024×640
?w=4000&h=4000 1000×1000 (a 3840×3840 box shrunk to fit)
?fmt=webp 1600×1000 WebP

Formats

  • Without fmt, the output keeps the source format. Animated GIF and WebP stay animated when the output is GIF or WebP; fmt=png, jpeg or avif keeps only the first frame.
  • fmt=jpeg flattens transparency onto white. contain padding is white for JPEG and transparent for PNG, WebP and AVIF.
  • SVG is served unchanged unless fmt is given: logo.svg?w=400 returns the original SVG. With fmt, it is rasterised at the requested size and may be larger than its nominal size: logo.svg?w=400&fmt=png of a 64×32 SVG gives 400×200.
  • No new image is made when nothing would change: fit alone, or a PNG or GIF requested in its own format or with only q, returns the original. A JPEG, WebP or AVIF with only q, or with its own fmt, is re-encoded at that quality.
  • Because q is rounded, q=55 and q=64 produce the same file; q=66 makes a new one.

How do I serve responsive images?

Use srcset with widths taken from the size ladder, so every URL maps to one cached variant, and the asset’s width, height and alt for layout and accessibility:

html
<img
  src="https://api.spatibase.com/assets/b8251bf8-2fab-4001-a3a1-49465ebcabfb/redwood-national-park-1.jpg?w=640&fmt=webp"
  srcset="https://api.spatibase.com/assets/b8251bf8-2fab-4001-a3a1-49465ebcabfb/redwood-national-park-1.jpg?w=320&fmt=webp 320w,
          https://api.spatibase.com/assets/b8251bf8-2fab-4001-a3a1-49465ebcabfb/redwood-national-park-1.jpg?w=640&fmt=webp 640w,
          https://api.spatibase.com/assets/b8251bf8-2fab-4001-a3a1-49465ebcabfb/redwood-national-park-1.jpg?w=1280&fmt=webp 1280w"
  sizes="(max-width: 700px) 100vw, 640px"
  width="1600" height="1000"
  alt="Redwood National Park, trailhead view">

Above the original width (here 1600 px) every request maps to the same 1600 px variant. A JavaScript version that builds this from an images field is in Recipes.

Which caching headers do assets send?

Originals and resized variants are cached for a year as immutable, because the content never changes under an asset id.

Header Value
Cache-Control public, max-age=31536000, immutable on originals and variants
ETag, Last-Modified ETag always, Last-Modified on originals and cached variants; If-None-Match returns 304
X-Variant-Cache MISS (rendered for this request) or HIT (served from the variant cache); absent for originals
Content-Type the asset’s mimeType, or the output format
Content-Disposition inline with the filename, its extension changed to the output format (redwood-national-park-1.webp; .jpg for JPEG)
Access-Control-Allow-Origin *; OPTIONS allows Range and If-None-Match
X-Content-Type-Options nosniff
Content-Security-Policy default-src 'none'; img-src data:; style-src 'unsafe-inline'; sandbox for SVG, HTML, XML and JavaScript files, so an uploaded file cannot run script when opened directly
  • Originals support Range requests (206 Partial Content), useful for large downloads and video.
  • The first request for a new variant renders it, which is slower; later requests are served from the variant cache. The variant cache has a size limit: when it is full, the oldest variants are removed and rendered again when next requested.
  • The first response for a new variant can have a different ETag from later responses for the same URL, so one revalidation may return 200 instead of 304. With year-long immutable caching this rarely matters.
  • Files are served through Spatibase’s CDN; year-long caching is safe because content never changes under an id. A deleted file can still be served from the CDN until its cached copy expires.

Asset errors

Asset errors use the same JSON format as the Content Delivery API, with Cache-Control: no-store:

Status code When
400 bad_request invalid w, h, q, fit or fmt on an image, or a parameter given twice
404 not_found unknown or malformed asset id (Asset "<id>" not found), the file is missing, or the path is not /assets/<id> or /assets/<id>/<filename>
405 method_not_allowed any method other than GET, HEAD or OPTIONS

Updated

Docs support

How can we help?

A person on our team reads every message. We include the page you are on, so you don't have to explain where.

Or write to hello@spatibase.com. For outages, check System status.

Talk to us

Not sure it’s a fit? Let’s find out

Tell us about your use case and what you need, from data model to security and deployment. In 30 minutes we’ll look at it together and tell you honestly if we can help.

Or write to hello@spatibase.com.