Uploading With a Pre-Signed URL
When This Option Is Ideal
Use this when you have the video bytes already (on your server, in a browser upload, or on a device) and want the fastest, most reliable upload path.
The upload goes directly to object storage using a time-limited pre-signed URL. Your servers never proxy the file.
The Two-Step Process
- Request upload URL: Tell AntCDN how you want the video processed and get a pre-signed URL.
- Upload bytes:
PUTthe video file to the pre-signed URL.
Processing begins automatically after the upload finishes.
Step 1: Submit Video Config
POST https://api.antcdn.net/v1/videos/upload-url
Required Parameters
| Field | Type | Description |
|---|---|---|
fileName | string | The name of the file including its extension (e.g. demo.mp4) |
allowPublicAccess | boolean | Whether playback can be accessed without signing |
Optional Parameters
| Field | Type | Default | Description |
|---|---|---|---|
videoName | string | - | Display name for the video. Useful for finding the video in the dashboard or via API. |
videoQuality | string | standard | Quality preset: standard, high, ultra |
maxEdgeLength | integer | -1 | Max short-edge resolution (e.g. 720, 1080, 2160). -1 means “auto”. |
maxFrameRate | integer | -1 | Max frame rate. -1 means “auto”. |
encodingMode | string | fast | fast or slow |
audioCodec | string | aac | aac, aac-he, aac-he-v2, mp3, ac3, eac3 |
generateCaptions | boolean | false | Whether captions should be generated automatically (if enabled for your plan) |
Response body
{ "body": { "assetId": "vid_XXX", "signedURL": "https://<storage-provider>/<path>?<signature>", "bucketPath": "<internal-bucket-path>", "expiresAt": 1640995200 }}| Field | Type | Description |
|---|---|---|
signedURL | string | Pre-signed URL for direct upload (valid for 1 hour) |
expiresAt | integer | Unix timestamp when the upload URL expires |
assetId | string | Your new video (asset) ID |
Step 2: Upload To Pre-Signed URL
Open your video file and make a “PUT” request to the signedURL you got from step 1.
file, _ := os.Open(filePath)defer file.Close()putReq, _ := http.NewRequest("PUT", `<signedURL>`, file)client.Do(putReq)Code Examples
View:
const APIKEY = "YOUR_API_KEY"; // Created in the AntCDN dashboard
interface UploadURLRequestOptions { fileName: string; videoQuality: "standard" | "high" | "ultra"; allowPublicAccess: boolean; videoName?: string; maxEdgeLength?: 480 | 720 | 1080 | 1440 | 2160; maxFrameRate?: number; encodingMode?: "fast" | "slow"; audioCodec?: "aac" | "mp3" | "ac3" | "eac3";}
async function uploadVideo(file: File, options: UploadURLRequestOptions): Promise<string> { // Step 1: Request a pre-signed URL for direct upload const res = await fetch( `https://api.antcdn.net/v1/videos/upload-url`, { method: "POST", headers: { "X-Api-Key": APIKEY, "Content-Type": "application/json" }, body: JSON.stringify(options) } );
if (!res.ok) throw new Error(`upload-url failed: ${res.status}`); const { body } = (await res.json()) as { body: { assetId: string; signedURL: string } };
// Step 2: Upload bytes directly to object storage const putRes = await fetch(body.signedURL, { method: "PUT", // Many storage providers infer type; it's still a good idea to send it. headers: file.type ? { "Content-Type": file.type } : undefined, body: file });
if (!putRes.ok) throw new Error(`upload PUT failed: ${putRes.status}`);
// Processing starts automatically after upload completes. return body.assetId;}
// Minimal usage example (browser)const input = document.querySelector<HTMLInputElement>("#video")!;const file = input.files?.[0];if (!file) throw new Error("No file selected");
const assetId = await uploadVideo(file, { fileName: file.name, videoQuality: "high", allowPublicAccess: true});import requests
APIKEY = "YOUR_API_KEY" # Created in the AntCDN dashboard
def upload_video(file_path: str, file_name: str) -> str: # Step 1: Request a pre-signed URL for direct upload upload_response = requests.post( 'https://api.antcdn.net/v1/videos/upload-url', headers={ 'X-Api-Key': APIKEY, 'Content-Type': 'application/json' }, json={ 'fileName': file_name, 'videoQuality': 'high', 'allowPublicAccess': True } )
upload_response.raise_for_status() body = upload_response.json()['body']
# Step 2: Upload bytes directly to object storage with open(file_path, 'rb') as f: put_res = requests.put(body['signedURL'], data=f) put_res.raise_for_status()
# Processing starts automatically after upload completes. return body['assetId']package main
import ( "bytes" "encoding/json" "fmt" "io" "net/http" "os")
const APIKEY = "YOUR_API_KEY" // Created in the AntCDN dashboard
type UploadURLRequest struct { FileName string `json:"fileName"` VideoQuality string `json:"videoQuality"` AllowPublicAccess bool `json:"allowPublicAccess"`}
type UploadURLResponse struct { Body struct { SignedURL string `json:"signedURL"` AssetId string `json:"assetId"` } `json:"body"`}
func uploadVideo(filePath, fileName string) (string, error) { // Step 1: Request a pre-signed URL for direct upload reqBody, _ := json.Marshal(UploadURLRequest{ FileName: fileName, VideoQuality: "high", AllowPublicAccess: true, })
req, _ := http.NewRequest("POST", "https://api.antcdn.net/v1/videos/upload-url", bytes.NewBuffer(reqBody)) req.Header.Set("X-Api-Key", APIKEY) req.Header.Set("Content-Type", "application/json")
client := &http.Client{} resp, err := client.Do(req) if err != nil { return "", err } defer resp.Body.Close()
var uploadResp UploadURLResponse if err := json.NewDecoder(resp.Body).Decode(&uploadResp); err != nil { return "", err }
// Step 2: Upload bytes directly to object storage file, err := os.Open(filePath) if err != nil { return "", err } defer file.Close()
putReq, _ := http.NewRequest("PUT", uploadResp.Body.SignedURL, file) putResp, err := client.Do(putReq) if err != nil { return "", err } defer putResp.Body.Close() io.Copy(io.Discard, putResp.Body) if putResp.StatusCode < 200 || putResp.StatusCode >= 300 { return "", fmt.Errorf("upload PUT failed: %s", putResp.Status) }
// Processing starts automatically after upload completes. return uploadResp.Body.AssetId, nil}const APIKEY = "YOUR_API_KEY"; // Created in the AntCDN dashboard
interface UploadVideoOptions { // Common options fileName: string; allowPublicAccess: boolean; videoQuality?: "standard" | "high" | "ultra";
// Optional videoName?: string; maxEdgeLength?: 480 | 720 | 1080 | 1440 | 2160; maxFrameRate?: number; encodingMode?: "fast" | "slow"; audioCodec?: "aac" | "aac-he" | "aac-he-v2" | "mp3" | "ac3" | "eac3";}
async function uploadVideo(file: File, options: UploadVideoOptions): Promise<string> { // Step 1: Request a pre-signed URL for direct upload const uploadResponse = await fetch( `https://api.antcdn.net/v1/videos/upload-url`, { method: "POST", headers: { "X-Api-Key": APIKEY, "Content-Type": "application/json" }, body: JSON.stringify(options) } );
if (!uploadResponse.ok) throw new Error(`upload-url failed: ${uploadResponse.status}`); const { body } = (await uploadResponse.json()) as { body: { assetId: string; signedURL: string; bucketPath: string; expiresAt: number }; };
// Step 2: Upload bytes directly to object storage const putRes = await fetch(body.signedURL, { method: "PUT", headers: file.type ? { "Content-Type": file.type } : undefined, body: file });
if (!putRes.ok) throw new Error(`upload PUT failed: ${putRes.status}`);
// Processing starts automatically after upload completes. return body.assetId;}
// Example usage with all optionsconst file = new File([/* bytes */], "video.mp4", { type: "video/mp4" });const assetId = await uploadVideo(file, { fileName: file.name, allowPublicAccess: true, videoQuality: "ultra", videoName: "My Firat Video", maxEdgeLength: 1080, maxFrameRate: 60, encodingMode: "slow", audioCodec: "aac"});import requestsfrom typing import Optional, Literal
APIKEY = "YOUR_API_KEY" # Created in the AntCDN dashboard
def upload_video( file_path: str, file_name: str, allow_public_access: bool, video_quality: Literal["standard", "high", "ultra"] = "standard", # Optional parameters video_name: Optional[str] = None, max_edge_length: Optional[int] = None, max_frame_rate: Optional[int] = None, encoding_mode: Optional[Literal["fast", "slow"]] = None, audio_codec: Optional[Literal["aac", "aac-he", "aac-he-v2", "mp3", "ac3", "eac3"]] = None) -> str: # Step 1: Build request body with all options request_body = { 'fileName': file_name, 'allowPublicAccess': allow_public_access, 'videoQuality': video_quality, }
# Add optional parameters if provided if video_name: request_body['videoName'] = video_name if max_edge_length: request_body['maxEdgeLength'] = max_edge_length if max_frame_rate: request_body['maxFrameRate'] = max_frame_rate if encoding_mode: request_body['encodingMode'] = encoding_mode if audio_codec: request_body['audioCodec'] = audio_codec
# Step 2: Request a pre-signed URL for direct upload upload_response = requests.post( 'https://api.antcdn.net/v1/videos/upload-url', headers={ 'X-Api-Key': APIKEY, 'Content-Type': 'application/json' }, json=request_body )
upload_response.raise_for_status() body = upload_response.json()['body']
# Step 3: Upload bytes directly to object storage with open(file_path, 'rb') as f: put_res = requests.put(body['signedURL'], data=f) put_res.raise_for_status()
# Processing starts automatically after upload completes. return body['assetId']
# Example usage with all optionsvideo_id = upload_video( file_path="/path/to/video.mp4", file_name="video.mp4", allow_public_access=False, video_quality="ultra", video_name="Product Demo 2024", max_edge_length=1080, max_frame_rate=60, encoding_mode="slow", audio_codec="aac")package main
import ( "bytes" "encoding/json" "fmt" "io" "net/http" "os")
const APIKEY = "YOUR_API_KEY" // Created in the AntCDN dashboard
type UploadURLRequest struct { FileName string `json:"fileName"` AllowPublicAccess bool `json:"allowPublicAccess"` VideoQuality string `json:"videoQuality,omitempty"`
// Optional VideoName *string `json:"videoName,omitempty"` MaxEdgeLength *int `json:"maxEdgeLength,omitempty"` MaxFrameRate *int `json:"maxFrameRate,omitempty"` EncodingMode *string `json:"encodingMode,omitempty"` AudioCodec *string `json:"audioCodec,omitempty"`}
type UploadURLResponse struct { Body struct { SignedURL string `json:"signedURL"` AssetId string `json:"assetId"` } `json:"body"`}
func uploadVideo(filePath string, req UploadURLRequest) (string, error) { // Step 1: Request a pre-signed URL for direct upload reqBody, _ := json.Marshal(req)
httpReq, _ := http.NewRequest("POST", "https://api.antcdn.net/v1/videos/upload-url", bytes.NewBuffer(reqBody)) httpReq.Header.Set("X-Api-Key", APIKEY) httpReq.Header.Set("Content-Type", "application/json")
client := &http.Client{} resp, err := client.Do(httpReq) if err != nil { return "", err } defer resp.Body.Close()
var uploadResp UploadURLResponse json.NewDecoder(resp.Body).Decode(&uploadResp)
// Step 2: Upload bytes directly to object storage file, _ := os.Open(filePath) defer file.Close()
putReq, _ := http.NewRequest("PUT", uploadResp.Body.SignedURL, file) putResp, err := client.Do(putReq) if err != nil { return "", err } defer putResp.Body.Close() io.Copy(io.Discard, putResp.Body) if putResp.StatusCode < 200 || putResp.StatusCode >= 300 { return "", fmt.Errorf("upload PUT failed: %s", putResp.Status) }
// Processing starts automatically after upload completes. return uploadResp.Body.AssetId, nil}
// Example usage with all optionsfunc main() { videoName := "Product Demo 2024" maxEdge := 1080 frameRate := 60 encoding := "slow" codec := "aac"
assetId, _ := uploadVideo("/path/to/video.mp4", UploadURLRequest{ FileName: "video.mp4", AllowPublicAccess: false, VideoQuality: "ultra", VideoName: &videoName, MaxEdgeLength: &maxEdge, MaxFrameRate: &frameRate, EncodingMode: &encoding, AudioCodec: &codec, })
fmt.Println("Video ID:", assetId)}How To Play The Video You Just Uploaded
After you upload your video, it will begin processing. Once it’s ready, create (or use) an Edge Key and embed it:
<!-- Replace {edgeKey} with an Edge Key --><iframe src="https://player.antcdn.net/v1/{edgeKey}/master.m3u8?autoplay=true&muted=true" frameborder="0" allowfullscreen></iframe>Check out our “Video Player” docs for more information.
Bonus: Upload Progress Tracking
For large files, you may want to track upload progress:
const uploadWithProgress = async (file, signedURL) => { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest();
xhr.upload.addEventListener('progress', (event) => { if (event.lengthComputable) { const percentComplete = (event.loaded / event.total) * 100; console.log(`Upload progress: ${percentComplete.toFixed(2)}%`); } });
xhr.addEventListener('load', () => { if (xhr.status === 200) { resolve('Upload complete'); } else { reject(new Error('Upload failed')); } });
xhr.open('PUT', signedURL); xhr.setRequestHeader('Content-Type', file.type); xhr.send(file); });};