Skip to content
Latchkey

curl -T / -F: Upload a File

There are two upload styles: PUT a file with -T, or multipart form with -F.

Uploading a build artifact or posting a form file are different transfers. Knowing which flag matches the API saves a lot of 400s.

What it does

-T / --upload-file <file> uploads the file using PUT by default, sending the raw bytes; you can change the method with -X. -F / --form sends a multipart/form-data POST, where each -F adds a field. For a file field, use -F 'field=@path' and optionally set the type with ;type=. Use -F when the API expects a form upload, -T when it expects a raw PUT body.

Common usage

Terminal
curl -T build.tar.gz https://artifacts.example.com/uploads/build.tar.gz
curl -F 'file=@report.pdf' https://api.example.com/upload
curl -F 'file=@data.json;type=application/json' -F 'name=ci' https://api.example.com/upload
curl -X POST -T build.zip https://artifacts.example.com/x   # PUT body via POST

Flags

FlagWhat it does
-T / --upload-file <f>Upload the file (PUT by default), raw bytes
-F / --form 'k=@f'Multipart form field reading a file
-F 'k=v'Multipart form field with a literal value
-F 'k=@f;type=...'Set the part content type
--form-stringForm field where @ and < are not special

In CI

Match the flag to the API: a presigned S3-style PUT URL wants -T, while a typical upload endpoint wants -F. Pair uploads with -f so a rejected upload (413 too large, 401 unauthorized) fails the step rather than passing silently with an error body.

Common errors in CI

curl: (26) Failed to open/read local data from file/application means the @path in -F or the -T file is missing or unreadable. A 411 Length Required or 400 often means the API wanted multipart (-F) but got a raw PUT (-T), or vice versa.

Related guides

Run this faster and cheaper on Latchkey managed runners. Start free →