Query Upload Progress
Query the progress of an ongoing file upload by sending a GET request to the specified endpoint with the file path as a query parameter. Alternatively, track progress in real-time using a WebSocket connection.
GET
/progress
Query upload progress via REST endpoint
WebSocket
/ws/progress
Track upload progress in real-time via WebSocket
Important Notes
- • Requires
informProgress=truein the upload request - • While uploading,
total_sizein the response JSON is -1 if you did not provide a Content Length header in your upload request - • All paths must start with
/(absolute paths only) - • Use your shard node URL:
https://YOUR-NODE-URLfor REST orwss://YOUR-NODE-URLfor WebSocket - • Server responds with
"completed"for any existing file also completed upload - • Account for natural network delays, especially with
overwrite=trueuploads
Headers
| Header | Type | Required | Description |
|---|---|---|---|
| x-user-api-key | string | Yes | Your authentication API key |
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| path | string | Yes | Absolute path of the file being uploaded (must start with /) |
| updatingRate | number (uint) | No | How many x "unitOfMeasurement" (see below) should be uploaded before an update is sent (keep in mind that more updates result in slower uploads) |
| unitOfMeasurement | "MB" | "GB" | No | The unit of measurement in which the upload progress is reported (MB | GB). Default is "MB" |
Example Request: REST Endpoint
type UploadProgress struct {
TotalSize int64 `json:"total_size"`
Uploaded int64 `json:"uploaded"`
Status string `json:"status"` // "uploading", "completed", "failed", "not_exist"
Error string `json:"error"`
}
func GetUploadProgress(path string) *UploadProgress {
url := fmt.Sprintf("%s/progress?path=%s", SHARD_NODE_URL, url.QueryEscape(path))
req, err := http.NewRequest("GET", url, nil)
if err != nil {
panic(fmt.Errorf("progress request creation failed: %w", err))
}
req.Header.Set("x-user-api-key", API_KEY)
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(fmt.Errorf("progress request failed: %w", err))
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
body, _ := io.ReadAll(resp.Body)
panic(fmt.Sprintf("Progress check failed: %d - %s", resp.StatusCode, body))
}
var progress UploadProgress
if err := json.NewDecoder(resp.Body).Decode(&progress); err != nil {
panic(fmt.Errorf("progress JSON decode failed: %w", err))
}
return &progress
}
func main() {
pathOnShard := "/files/large-audio-file"
ticker := time.NewTicker(50 * time.Millisecond)
defer ticker.Stop()
go func() {
for range ticker.C {
fmt.Println("Upload progress:", GetUploadProgress(pathOnShard))
}
}()
UploadFile("large-audio-file", pathOnShard)
}
interface UploadProgress {
total_size: number;
uploaded: number;
status: string;
error: string;
}
async function getProgress(path: string): Promise<UploadProgress> {
const resp = await fetch(`${SHARD_URL}/progress?path=${encodeURIComponent(path)}`, {
headers: { 'x-user-api-key': API_KEY }
});
return resp.ok ? await resp.json() : { status: "not_exist", uploaded: 0, total_size: 0 } as UploadProgress;
}
//example usage
function pollProgress(path: string, pollTimeMs: number = 200) {
let stopped = false;
const poll = async () => {
if (stopped) return;
const p = await getProgress(path);
if (p.total_size > 0) {
console.log(`${p.uploaded}/${p.total_size} bytes (${((p.uploaded / p.total_size) * 100).toFixed(1)}%) - ${p.status}`);
}
if (p.status !== 'completed' && p.status !== 'failed' && !stopped) {
setTimeout(poll, pollTimeMs);
}
};
poll();
return { stop: () => { stopped = true; } };
}
const pathOnShard = "/files/large-file.zip";
const poller = pollProgress(pathOnShard);
uploadFile("./large-file.zip", pathOnShard).then(() => poller.stop())
Example Request: WebSocket
package main
//uses github.com/gorilla/websocket library
import (
"fmt"
"net/http"
"net/url"
"strings"
"encoding/json"
"github.com/gorilla/websocket"
)
const API_KEY = "YOUR-API-KEY"
const SHARD_NODE_URL = "wss://YOUR-NODE-URL"
type UploadProgress struct {
TotalSize int64 `json:"total_size"`
Uploaded int64 `json:"uploaded"`
Status string `json:"status"` // "uploading", "completed", "failed", "not_exist"
Error string `json:"error"`
}
func GetUploadProgressWS(path string) {
const maxRetries = 40
retryCount := 0
for retryCount < maxRetries {
wsURL := fmt.Sprintf("%s/progress/ws?path=%s",
WS_SHARD_NODE_URL,
url.QueryEscape(path))
header := http.Header{}
header.Set("x-user-api-key", API_KEY)
dialer := websocket.Dialer{}
conn, _, err := dialer.Dial(wsURL, header)
if err != nil {
retryCount++
time.Sleep(50 * time.Millisecond)
continue
}
for {
_, message, err := conn.ReadMessage()
if err != nil {
if websocket.IsUnexpectedCloseError(err) {
fmt.Printf("WebSocket closed: %v\n", err)
}
conn.Close()
break
}
var progress UploadProgress
if err := json.Unmarshal(message, &progress); err != nil {
fmt.Printf("Progress decode error: %v\n", err)
continue
}
if progress.Status == "not_exist" {
fmt.Printf("Upload not started yet (retry %d/%d), reconnecting...\n", retryCount+1, maxRetries)
conn.Close()
retryCount++
time.Sleep(100 * time.Millisecond)
break
}
if progress.TotalSize > 0 {
fmt.Printf("Progress: %d/%d bytes (%.1f%%) - Status: %s\n",
progress.Uploaded,
progress.TotalSize,
float64(progress.Uploaded)/float64(progress.TotalSize)*100,
progress.Status)
} else {
fmt.Printf("Progress: %d bytes - Status: %s\n",
progress.Uploaded, progress.Status)
}
if progress.Status == "completed" || progress.Status == "failed" {
conn.Close()
return
}
}
}
fmt.Printf("Failed to track progress after %d attempts\n", maxRetries)
}
func main() {
go func() {
GetUploadProgressWS("/files/large-audio.mp3")
}()
UploadFile("large-audio.mp3", "/files/large-audio.mp3")
}
import WebSocket from 'ws'; // npm install ws
interface UploadProgress {
total_size: number;
uploaded: number;
status: string;
error: string;
}
function GetUploadProgressWS(path: string): void {
const maxRetries = 40;
let retryCount = 0;
const attemptConnection = () => {
if (retryCount >= maxRetries) {
console.log(`Failed to track progress after ${maxRetries} attempts`);
return;
}
const wsURL = `${WS_SHARD_NODE_URL}/progress/ws?path=${encodeURIComponent(path)}`;
const ws = new WebSocket(wsURL, {
headers: { 'x-user-api-key': API_KEY }
});
ws.on('message', (data: Buffer) => {
try {
const progress: UploadProgress = JSON.parse(data.toString());
if (progress.status === "not_exist") {
console.log(`Upload not started yet (retry ${retryCount + 1}/${maxRetries}), reconnecting...`);
ws.close();
retryCount++;
setTimeout(attemptConnection, 100);
return;
}
if (progress.total_size > 0) {
const pct = (progress.uploaded / progress.total_size) * 100;
console.log(`Progress: ${progress.uploaded}/${progress.total_size} bytes (${pct.toFixed(1)}%) - Status: ${progress.status}`);
} else {
console.log(`Progress: ${progress.uploaded} bytes - Status: ${progress.status}`);
}
if (progress.status === "completed" || progress.status === "failed") {
ws.close();
return;
}
} catch (err) {
console.log(`Progress decode error: ${(err as Error).message}`);
}
});
ws.on('close', () => {
console.log("WebSocket closed");
});
ws.on('error', (err) => {
retryCount++;
setTimeout(attemptConnection, 50);
});
};
attemptConnection();
}
const pathOnShard = "/files/large-file.zip";
GetUploadProgressWS(pathOnShard);
uploadFile("large-file.zip", pathOnShard);
Response Status Codes (REST)
| Code | Status | Description |
|---|---|---|
| 200 | Success | Upload progress retrieved successfully |
| 400 | Bad Request | Invalid request parameters |
| 401 | Unauthorized | Invalid or missing API key |
| 5xx | Server Error | Internal server error - check response body for details |