Upload big files with the tus protocol (version 1.0.0). A request that fails costs one part, and an upload that was cut (the connection, the program, the computer) is picked up where it stopped, also hours later. Any tus client works. This page tells how to get the address of the upload from the API and how to have the file finished by the last request.
| Parameters | Description | Example | Format | Required |
|---|---|---|---|---|
| key | API-Key | 1ltghrilhllgrx2b2 | String | yes |
| srv_id | The srv_id of an upload that is not finished: you are given the same file server again, as long as it takes uploads. Leave it out for a new file |
12 | Number | no |
{
"msg": "OK",
"status": 200,
"tus_url": "https://u7134.send.now/cgi-bin/tus.cgi",
"srv_id": 12,
"upload_token": "1791160000.3fa9c1d3e5...",
"upload_limit": "1791123456.2147483648.9c1b7e0d44...",
"sess_id": "ynfejiddf0grvip0",
"api_url": "https://u7134.send.now/cgi-bin/api.cgi?u=api",
"chunk_url": "https://u7134.send.now/cgi-bin/put_chunk.cgi?u=api",
"server_time": "2024-08-26 12:19:28"
}
| Field | Description |
|---|---|
| tus_url | Where to create the upload. Not in the reply when the site does not offer tus: use Chunked upload then |
| srv_id | The file server of tus_url. Keep it with the upload and send it back in step 1 when you ask again for an upload that is not finished |
| upload_token | Send it as the header X-Upload-Token with every request to the file server, when it is not empty. Valid for 12 hours |
| upload_limit | Send it as the header X-Upload-Limit when creating the upload, when it is not empty: it carries the largest file your account may upload, and the file server refuses a bigger file at once (413) instead of after the last byte. Empty when your account has no limit |
| sess_id | It says whose account gets the file. Valid for about an hour: ask for it again just before the last request of a long upload (step 4) |
| api_url, chunk_url | For Chunked upload, and for finishing an upload without X-Import (see below) |
The answer is the one the file server's import would give after all the bytes: the file is too big for your account, its storage is used up, the cap per day is reached, the type of file is not allowed. Nothing is stored. fld_id (a folder of yours) and srv_id are optional.
{ "status": 200, "msg": "OK", "allowed": 1, "checked": 1 }
{ "status": 200, "msg": "file is too big", "allowed": 0, "error": "file is too big" }
The call is only a help: the site decides again when the file is finished. "checked": 0 means that nobody was asked (the site has the question off) and the file may be sent. A refused file has status 200 with "allowed": 0 and the reason in msg.
| Header | Description | Example | Required |
|---|---|---|---|
| Tus-Resumable | 1.0.0, on every request | 1.0.0 | yes |
| Upload-Length | The size of the file in bytes | 2400000000 | yes |
| Upload-Metadata | filename and filetype as tus wants them: a name, a space, the value in base64, pairs separated by commas | filename Ymlnemlw | no |
| X-Upload-Token | upload_token of step 1 | 1791160000.3fa9... | when not empty |
| X-Upload-Limit | upload_limit of step 1 | 1791123456.2147... | when not empty |
The reply is 201 with the header Location: the address of this upload, a path on the file server (/cgi-bin/tus.cgi/0123456789abcdef0123456789abcdef). Keep it with srv_id, the size and the file: it is all you need to go on after a stop.
| Header | Description | Example | Required |
|---|---|---|---|
| Tus-Resumable | 1.0.0 | 1.0.0 | yes |
| Upload-Offset | How many bytes the file server has: 0 for the first part, then the Upload-Offset of the last answer | 52428800 | yes |
| Content-Type | application/offset+octet-stream | yes | |
| X-Upload-Token | upload_token of step 1 | 1791160000.3fa9... | when not empty |
| X-Import | On the request that sends the last byte: asks for the file to be finished with it. A query string, see below | sess_id=ynfejiddf0grvip0&fname=big.zip&file_public=0&u=api | on the last request |
The body is the next part of the file, as big as you like (50 MB is a good size). The reply is 204 with the new Upload-Offset. Send the parts one after the other.
The fields of X-Import are written like the parameters of a URL (percent-encoded; at most 4 KB):
| Field | Description | Example | Required |
|---|---|---|---|
| sess_id | sess_id of step 1 | ynfejiddf0grvip0 | yes |
| fname | Name of the file (the filename of the metadata if you leave it out) | big.zip | no |
| file_public | 1 = public, 0 = private. Send it, do not rely on the default | 0 | no |
| file_descr | Description | Backup | no |
| to_folder | Id of one of your folders (default: the top level) | 58 | no |
| link_pass | Password for the file | 123password | no |
| u | api | api | no |
The reply to the request that carried X-Import comes when the file is saved, and says how it went in headers (the status stays 204, the bytes are all there):
X-Import-Status: OK
X-File-Code: byul9fp3ctyr
X-Delete-Link: https://send.now/byul9fp3ctyr?killcode=77b1an60zs
X-Import-Status: ERROR
X-Import-Error: file is too big
The link of the file is https://send.now/ followed by X-File-Code. An ERROR is final: the site refused this file, the upload is gone, send the file again only when the reason is gone.
Tus-Resumable: 1.0.0 and X-Upload-TokenThe reply has Upload-Offset, the bytes the file server has. Go on with step 4 from there. Do this after any request that failed or whose answer you did not get, and when the program starts again with an upload that is not finished (ask for step 1 again first, with the srv_id you kept: you get a new token and session for the same file server). A file that is already finished answers Upload-Offset equal to its length with the same X-Import-Status and X-File-Code headers: the answer to the last request is never lost. 404 or 410 means the upload is gone (an upload that nobody touches for 24 hours is removed): start again with step 3.
A client that does not send X-Import (or a file server that does not know it: the reply of the last request has no X-Import-Status) finishes the upload as in Chunked upload, step 3: POST api_url with op=import_file, sid = the last part of the address of the upload (32 letters and digits), sess_id and fname.
# 1. address, token, session
curl 'https://send.now/api/upload/chunk?key=1ltghrilhllgrx2b2'
# 3. create the upload: the size in bytes, the name in base64
curl -i -X POST 'TUS_URL' -H 'Tus-Resumable: 1.0.0' -H 'X-Upload-Token: UPLOAD_TOKEN' -H 'X-Upload-Limit: UPLOAD_LIMIT' \
-H 'Upload-Length: 10485760' -H "Upload-Metadata: filename $(printf 'big.zip' | base64)"
# 201 Created Location: /cgi-bin/tus.cgi/0123456789abcdef0123456789abcdef
# 4. send the file (here in one request, which is also the last one)
curl -i -X PATCH 'https://u7134.send.now/cgi-bin/tus.cgi/0123456789abcdef0123456789abcdef' \
-H 'Tus-Resumable: 1.0.0' -H 'X-Upload-Token: UPLOAD_TOKEN' -H 'Upload-Offset: 0' \
-H 'Content-Type: application/offset+octet-stream' \
-H 'X-Import: sess_id=SESS_ID&fname=big.zip&file_public=0&u=api' --data-binary @big.zip
# 204 No Content X-Import-Status: OK X-File-Code: byul9fp3ctyr
# 5. where was I? (after a stop)
curl -I 'https://u7134.send.now/cgi-bin/tus.cgi/0123456789abcdef0123456789abcdef' -H 'Tus-Resumable: 1.0.0' -H 'X-Upload-Token: UPLOAD_TOKEN'
Use the addresses of step 1 for all the steps: the bytes are kept on that file server. upload_token works for 12 hours, sess_id for about an hour, and an upload that is not touched for 24 hours is removed. |
|
An upload that takes longer than an hour: ask step 1 again (with srv_id) just before the request that sends the last byte, and use the new sess_id in X-Import. |
|
A file server answers GET tus_url with a few lines of text: tus 1.0.0, and the words upload-limit and import when it takes X-Upload-Limit and X-Import. Use it to find out whether the file server can do what you want before you ask for it. |
|
The size and storage limits of your account are checked when the file is finished. X-Upload-Limit and the precheck (step 2) only tell you earlier. |
|
A part that is sent at a place the file server is not at (it has more or fewer bytes than the Upload-Offset says) is answered with 409 and the offset it has; a very big part may instead get a dropped connection. Both ways, ask HEAD and go on from its offset. |
| 201 / 204 | Created / the part is saved |
| 400 / 412 / 415 | Bad request: a header is missing or wrong (Tus-Resumable, Upload-Length, Upload-Offset, Content-Type) |
| 403 | The token is missing, wrong or has run out: ask step 1 again |
| 404 / 410 | No such upload, or it has expired: start again with step 3 |
| 409 | The file server is at another offset (Upload-Offset of the answer): go on from there |
| 413 | The file is too big for your account (X-Upload-Limit), or for the file server. The answer says how big a file may be |
| 423 | The upload is in use by a request that is still running: wait a moment and ask HEAD |