Large files
POSThttps://api.kennmarq.com/v1/uploads
A request to the API carries at most 4 MB. For a larger file, up to 25 MB (a clip to mark, up to 1 GB), upload it first, straight to our storage in Zurich, then name the upload instead of sending the file. The Python SDK does this by itself.
How it works
- Ask for an upload with the file's size:
{"bytes": 12345678}. PUTthe file to theurlyou get back, with itsheaders, within 15 minutes.- Name the upload in
uploadinstead offileon /v1/mark, /v1/check or /v1/verify, or ininput_uploadinstead ofinputon /v1/mark.
# 1. The upload, for the file's size
curl https://api.kennmarq.com/v1/uploads \
-H "Authorization: Bearer $KENNMARQ_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"bytes\": $(wc -c < render.png)}"
# → {"object":"upload","id":"up_…","url":"https://…","method":"PUT","headers":{"Content-Length":"…"},…}
# 2. The file, to the url (no Authorization header: the URL is the permission)
curl -X PUT --upload-file render.png "$UPLOAD_URL"
# 3. The call, naming the upload
curl https://api.kennmarq.com/v1/mark \
-H "Authorization: Bearer $KENNMARQ_API_KEY" \
-F upload=up_… -F source=created \
-o render.marked.pngRequest
application/json with one field, bytes: the file's size, from 1 byte to 1 GB (1,073,741,824 bytes). Each call holds the files it reads to its own limit: 25 MB (26,214,400 bytes), and 1 GB for a clip to mark; a call naming a larger upload answers 413 payload_too_large. Needs a key with the mark or check scope. Asking for an upload counts toward the organisation's calls a minute, never toward its marks; a key may have 100 uploads waiting at once.
Response
201, application/json:
| Field | Value |
|---|---|
object | "upload" |
id | The upload's ID (up_…): name it in the call's upload field. |
bytes | The size you declared; the file must be exactly this long. |
url | Where to PUT the file: our storage in Zurich. The URL itself is the permission, so send no Authorization header. |
method | "PUT" |
headers | Headers the PUT must carry: Content-Length, which the URL is signed for. |
url_expires_at | The URL works for 15 minutes. |
expires_at | The upload can be used for an hour; then it's deleted unused. |
The rules
- An upload works once, within the hour, with the key that made it. Another key, a second call, or a later one gets
404 upload_not_found. - The PUT must send exactly the declared size; anything else is refused. A call naming an upload whose file hasn't arrived yet gets
400 invalid_request. - A call that fails for a reason of its own (a typo in an option, our service down for a moment) leaves the upload for the retry. A call that used it, or refused what it holds (not a file it takes, or blocked by the safety check), uses it up. A clip's job keeps its upload until the job is done; after a failure on our side it works again for its hour.
- On
/v1/verify, which needs no key for a file sent in the request, naming an upload takes the key that made it.
Errors
| Status | Code | When |
|---|---|---|
| 400 | invalid_request | bytes is missing, not a whole number, or under 1. |
| 400 | unknown_field | A field other than bytes. |
| 401 | missing_api_key, invalid_api_key, api_key_revoked | See authentication. |
| 403 | insufficient_scope, ip_not_allowed | The key has neither the mark nor the check scope, or the address isn't allowlisted. |
| 413 | payload_too_large | bytes is over 1 GB. |
| 415 | unsupported_media_type | The body isn't sent as application/json. |
| 429 | rate_limited | Too many calls this minute, or 100 uploads already waiting for this key; retry after Retry-After seconds. |
| 503 | storage_unavailable | Uploads are down for a moment. Retry. |
Error bodies follow the common format.