Model Upload

Upload complete HuggingFace model directories to Xerotier.ai using archive or directory upload methods.

Overview

Xerotier.ai supports two methods for uploading complete model directories:

Method Best For Key Features
Archive Upload Standard model uploads Create a .tar.gz archive, upload in chunks, server extracts
Directory Upload Large models, incremental uploads Upload files individually, fine-grained control

Both methods use chunked uploads for reliability and support the same model formats. For basic chunked file uploads, see the Model Management API reference.

Archive Upload

Upload compressed archives containing HuggingFace model directories. Archives are extracted server-side, preserving the model's file structure for vLLM compatibility.

Best for: Uploading complete HuggingFace model directories. Supports .tar, .tar.gz, and .tar.bz2 formats.

Supported Formats

Format Extension Content-Type
tar .tar application/x-tar
tar.gz .tar.gz, .tgz application/gzip
tar.bz2 .tar.bz2, .tbz2 application/x-bzip2

Creating an Archive

Create a properly formatted archive from your model directory. Files must be at the archive root (not nested in subdirectories).

Basic Command
COPYFILE_DISABLE=1 tar -chzf model-name.tar.gz -C /path/to/model/directory .

Command Options

Option Description
-c Create a new archive
-h Follow symlinks (required for HuggingFace cache)
-z Compress with gzip (use -j for bzip2)
-f Specify the output filename
-C Change to directory before adding files

Important: The -h flag is required when archiving from HuggingFace cache, as it uses symlinks. Without this flag, your archive will contain broken links instead of actual model files.

HuggingFace Cache Example

# Find and archive a model from HuggingFace cache COPYFILE_DISABLE=1 tar -chzf Qwen3-0.6B.tar.gz -C ~/.cache/huggingface/hub/models--Qwen--Qwen3-0.6B/snapshots/abc123/ .

Verify Archive Structure

Before uploading, verify files are at the root level:

$ tar -tzf model-name.tar.gz | head -5 ./config.json ./model.safetensors ./tokenizer.json ./tokenizer_config.json ./generation_config.json

Initialize Archive Upload

POST /proj_ABC123/v1/uploads/archive

Request Body

Parameter Type Description
model_namerequired string Display name for the model
archive_sizerequired integer Total archive size in bytes
archive_formatrequired string Archive format: "tar", "tar.gz", or "tar.bz2"
curl
curl -X POST https://api.xerotier.ai/proj_ABC123/v1/uploads/archive \ -H "Authorization: Bearer xero_myproject_your_api_key" \ -H "Content-Type: application/json" \ -d '{ "model_name": "Qwen3-0.6B", "archive_size": 1234567890, "archive_format": "tar.gz" }'

Response

{ "session_id": "550e8400-e29b-41d4-a716-446655440000", "upload_url": "https://api.xerotier.ai/proj_ABC123/v1/uploads/550e8400.../chunks", "chunk_size": 104857600, "total_chunks": 12, "expires_at": "2026-01-02T00:00:00Z" }

Upload Archive Chunks

After initialization, upload chunks using the same chunked upload process as single-file uploads:

POST /proj_ABC123/v1/uploads/{sessionId}/parts?part_number={chunkIndex}

Complete Archive Upload

When all chunks are uploaded, complete the upload to trigger archive extraction:

POST /proj_ABC123/v1/uploads/{sessionId}/complete

Response

{ "model_id": "660e8400-e29b-41d4-a716-446655440001", "model_name": "Qwen3-0.6B", "file_count": 7, "total_size": 1234567890, "files": [ {"relative_path": "config.json", "size": 1234, "content_type": "application/json"}, {"relative_path": "model.safetensors", "size": 1234000000, "content_type": "application/octet-stream"}, {"relative_path": "tokenizer.json", "size": 5678, "content_type": "application/json"} ], "status": "completed" }

Archive Upload Example

Python
import os import hashlib import requests API_KEY = "xero_myproject_your_api_key" BASE_URL = "https://api.xerotier.ai/proj_ABC123/v1" def upload_archive(archive_path: str, model_name: str): archive_size = os.path.getsize(archive_path) # Initialize init = requests.post(f"{BASE_URL}/uploads/archive", headers={"Authorization": f"Bearer {API_KEY}"}, json={"model_name": model_name, "archive_size": archive_size, "archive_format": "tar.gz"} ).json() session_id = init["session_id"] chunk_size = init["chunk_size"] # Upload chunks with open(archive_path, "rb") as f: idx = 0 while chunk := f.read(chunk_size): checksum = hashlib.sha256(chunk).hexdigest() requests.post(f"{BASE_URL}/uploads/{session_id}/parts?part_number={idx}", headers={"Authorization": f"Bearer {API_KEY}", "X-Chunk-Checksum": checksum}, data=chunk) idx += 1 # Complete return requests.post(f"{BASE_URL}/uploads/{session_id}/complete", headers={"Authorization": f"Bearer {API_KEY}"}).json() upload_archive("./qwen3-0.6b.tar.gz", "Qwen3-0.6B")

Directory Upload

Upload individual files that mirror a local model directory structure. Useful when creating an archive is impractical or for incremental uploads.

Best for: Large models where archive creation is slow, or when you want fine-grained control over individual file uploads.

Initialize Directory Upload

POST /proj_ABC123/v1/uploads/directory

Provide a manifest of all files to upload:

Request Body

Parameter Type Description
model_namerequired string Display name for the model
filesrequired array List of files with relative_path and size
curl
curl -X POST https://api.xerotier.ai/proj_ABC123/v1/uploads/directory \ -H "Authorization: Bearer xero_myproject_your_api_key" \ -H "Content-Type: application/json" \ -d '{ "model_name": "Qwen3-0.6B", "files": [ {"relative_path": "config.json", "size": 1234}, {"relative_path": "model.safetensors", "size": 1234000000}, {"relative_path": "tokenizer.json", "size": 5678} ] }'

Response

{ "session_id": "550e8400-e29b-41d4-a716-446655440000", "upload_url": "https://api.xerotier.ai/proj_ABC123/v1/uploads/550e8400.../files", "files": [ {"relative_path": "config.json", "upload_path": ".../files/config.json", "size": 1234}, {"relative_path": "model.safetensors", "upload_path": ".../files/model.safetensors", "size": 1234000000} ], "expires_at": "2026-01-02T00:00:00Z" }

Upload Individual File

POST /proj_ABC123/v1/uploads/{sessionId}/files/{relativePath}

Headers

Header Description
X-File-Checksum SHA256 checksum of the file content (hex-encoded)
curl
CHECKSUM=$(sha256sum config.json | cut -d' ' -f1) curl -X POST "https://api.xerotier.ai/proj_ABC123/v1/uploads/$SESSION_ID/files/config.json" \ -H "Authorization: Bearer xero_myproject_your_api_key" \ -H "X-File-Checksum: $CHECKSUM" \ --data-binary @config.json

Response

{ "relative_path": "config.json", "size": 1234, "checksum": "a1b2c3d4e5f6...", "uploaded_file_count": 1, "expected_file_count": 7, "progress": 14.28 }

Directory Upload Example

Python
import os import hashlib import requests from pathlib import Path API_KEY = "xero_myproject_your_api_key" BASE_URL = "https://api.xerotier.ai/proj_ABC123/v1" def upload_directory(model_dir: str, model_name: str): model_path = Path(model_dir) # Build manifest files = [{"relative_path": str(f.relative_to(model_path)), "size": f.stat().st_size} for f in model_path.rglob("*") if f.is_file()] # Initialize init = requests.post(f"{BASE_URL}/uploads/directory", headers={"Authorization": f"Bearer {API_KEY}"}, json={"model_name": model_name, "files": files} ).json() session_id = init["session_id"] # Upload each file for file_info in files: file_path = model_path / file_info["relative_path"] with open(file_path, "rb") as f: data = f.read() checksum = hashlib.sha256(data).hexdigest() requests.post(f"{BASE_URL}/uploads/{session_id}/files/{file_info['relative_path']}", headers={"Authorization": f"Bearer {API_KEY}", "X-File-Checksum": checksum}, data=data) # Complete return requests.post(f"{BASE_URL}/uploads/{session_id}/complete", headers={"Authorization": f"Bearer {API_KEY}"}).json() upload_directory("./models--Qwen--Qwen3-0.6B", "Qwen3-0.6B")

Required Model Files

For vLLM compatibility, models must include:

File Required Description
config.json Yes Model configuration
*.safetensors or *.bin or *.exl2 Yes Model weights (at least one)
tokenizer.json No Tokenizer configuration
tokenizer_config.json No Tokenizer settings