Uploading files
One request for screenshots and short videos; parts for large files and unstable networks. A file has its link right after the upload.
In one request
POST /api/v1/uploads takes multipart/form-data with a file field and the optional title, tags, visibility, ttl, password and oneTime.
curl -X POST "https://lac.pics/api/v1/uploads" \
-H "Authorization: Bearer lac_api_YOUR_KEY" \
-H "Idempotency-Key: capture-2026-09-27-a7f3c1" \
-F "file=@capture.png" \
-F "visibility=unlisted"Images: PNG, JPEG, WebP and AVIF. Videos: MP4 and WebM. The type comes from the file’s first bytes, not its name; HEIC is not accepted. Sizes and simultaneous uploads are in Limits.
Retries without duplicates
To retry an upload safely after a network error, send Idempotency-Key: a random string of 16–128 Latin letters, digits, _ and -, one per file and its options.
- For at least 24 hours a retry with the same key returns the same file (201) without extending its expiry.
- 409 with
Retry-After: an earlier attempt is still running; wait and retry with the same key. - 409 without
Retry-After: the key was used for a different file or options. - 410: the file of that upload was deleted or expired.
Video processing
A video starts in PROCESSING. Poll mediaStatus with the batch media-status method: playback and the poster work from READY. FAILED comes with a reason in mediaError; processing can be retried.
The server never re-encodes a video: it accepts up to 30 minutes and up to 3840×2160 either way, H.264 + AAC in MP4 or VP9/AV1 + Opus in WebM, under a bitrate ceiling (limits.video in the account profile); anything else gets 415 VIDEO_NOT_SUITABLE, and the lacuna app or the site compresses it before sending. One version is kept: the one that plays and downloads. Before READY only the owner can download the uploaded file, with ?download=1.
In parts
For large files and unstable networks, a file is split into 2 MB parts (at most 50). Each part is sent on its own, and an interrupted upload continues where it stopped.
- Compute the SHA-256 of every part and the file’s fingerprint.
- Start the upload: POST /uploads/resumable with an
Idempotency-Key. - Send the parts: PUT …/parts/{index}, up to 3 at a time.
- Complete it: POST …/complete with a
completeKeyand the link settings; the response returns the file.
If the completion response is lost, read the upload’s state: content holds the file. An account keeps up to 10 unfinished uploads, each for 24 hours.
Video subtitles
A ready video can carry SRT or WebVTT subtitles or a plain transcript, one track per video. Everyone who can watch the video sees it. There is no speech recognition: you provide the text.
From your own program or script
Any program or script that can send a file in an HTTP request with a header will do: a screenshot tool with a “custom uploader”, a scheduled script, your own server.
- Create a key with the “Uploads only” preset in the console: API. It needs just the
content:writepermission. - Give the program the address, the header and the file field from the table below.
- The program takes the file’s link from the answer: the
content.urlfield.
| Setting | Value |
|---|---|
| Method | POST |
| Address | https://lac.pics/api/v1/uploads |
| Header | Authorization: Bearer lac_api_… — details |
| Body | multipart/form-data, the file in the file field |
| Link from the answer | content.url (JSON) |
A key opens your account: keep it like a password and never put it in the request address. Who can open the file is up to the visibility field: unlisted — anyone with the link, private — only you; without it your account setting applies.
Methods
- POST
/api/v1/uploadsUpload a file - GET
/api/v1/media-statusProcessing status - POST
/api/v1/content/{id}/retryRetry processing - POST
/api/v1/uploads/resumableStart a resumable upload - PUT
/api/v1/uploads/resumable/{id}/parts/{index}Upload a part - POST
/api/v1/uploads/resumable/{id}/completeComplete the upload - GET
/api/v1/uploads/resumable/{id}Upload state - GET
/api/v1/uploads/resumableResumable uploads - DELETE
/api/v1/uploads/resumable/{id}Cancel an upload - PUT
/api/v1/content/{id}/video-textUpload subtitles - GET
/api/v1/content/{id}/video-textVideo subtitles - DELETE
/api/v1/content/{id}/video-textDelete subtitles