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=true in the upload request
  • • While uploading, total_size in 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-URL for REST or wss://YOUR-NODE-URL for WebSocket
  • • Server responds with "completed" for any existing file also completed upload
  • • Account for natural network delays, especially with overwrite=true uploads

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