Skip to main content

Upload Limits

Splat3D caps the size of a single source file (video or photo zip) at 5 GB, the same on every plan. The cap is enforced server-side via an R2 HEAD check at POST /v1/scenes/{id}/process. Only a COLMAP model zip is refused for its image count (more than 800 registered images); other uploads over 800 frames or photos are thinned to 800. A photo zip needs at least 5 photos (see Minimum input).

The cap

The cap is the largest file storage accepts in one upload: 4.995 GiB (5,363,466,240 bytes), which the API’s messages show as 5.0 GB. A file close enough to round the same is shown in exact bytes instead. Storage refuses a larger upload to the presigned URL outright. On the website, photos you select one by one are zipped in your browser, which holds the whole zip in memory, so they must total under 2 GB. Upload a bigger set as a single .zip. Larger captures need to be trimmed or split; see the tips below.

What counts

The cap is on the bytes of the file you upload to the presigned URL — the raw video, or the zipped photo archive. It does not cap:
  • the number of frames sampled from a video or photos in a zip (the pipeline keeps at most 800 and thins bigger sets evenly, MAX_FRAMES in pipeline/training/frame_extract.py; see Pipeline Architecture),
  • the resolution of individual photos.
If you have many high-resolution photos but the zipped archive stays under the cap, you’re fine.
One exception: a zip carrying a pre-built COLMAP model (see SfM Backends) is capped at 800 registered images, and is rejected rather than subsampled — dropping an image would orphan its pose. Ordinary photo zips are subsampled silently.

How the cap is enforced

There are two checks in the API:
  1. Advisory check at scene creation. POST /v1/scenes reads the file_size you declared in the request body and rejects with 400 invalid_input if it exceeds the cap. This is advisory — clients can lie about or omit file_size.
  2. Authoritative check at process time. POST /v1/scenes/{id}/process issues an R2 HEAD against the actual uploaded object and compares the real byte count against the cap before any credits are deducted (api/src/lib/scenes.ts processScene). Uploads from the website go through the same check, and POST /v1/scenes/{id}/retrain runs it on the source before it creates a version.
If the second check fails, the API returns:
with HTTP 413 Payload Too Large. The check runs before any credits are charged, so a scene that 413s costs nothing.

Minimum input

POST /v1/scenes/{id}/process also refuses a file that can’t become a scene, before charging anything:
  • an empty file (0 bytes),
  • a video under 30 seconds, read from its MP4 or MOV header. A video whose length can’t be read that way launches (Video),
  • a photo upload that isn’t a zip,
  • a photo zip with fewer than 5 JPEG or PNG photos. HEIC, HEIF, WebP and other formats are skipped, so export them as JPEG first. A zip holding one wide.mov is let through: it is a SplatKing video export, processed as a video (SplatKing export).
The API returns 422 source_unusable; the message says what it found and details holds the rule, e.g. {"field": "photo_count", "value": 3, "allowed": {"min": 5}}. The scene stays uploading: upload a usable file to the same upload URL (it lasts 24 hours from create) and call /process again.

Multipart upload (roadmap)

Single-PUT to R2 tops out at 5 GB, so the cap can’t go higher without multipart upload. Multipart is planned as the second step, after pipeline work for large sources. Until then, captures over 5 GB should be:
  • compressed at a lower bitrate before upload,
  • split into multiple scenes covering separate sub-rooms,
  • or sent as a curated photo zip instead of a long raw video.