SDX API Storage Data eXchange v1.0
Advanced object storage technology enabling blazing-fast uploads to public nodes (buckets) distributed globally. Upload files up to 1TB using chunked uploads with resume support.
Introduction
📦 Storage Data eXchange (SDX) — Shulker's advanced object storage technology. With SDX, you can upload files of up to 1TB using chunked uploads, create folders for organization, and manage your objects through a comprehensive RESTful API.
Authentication
The SDX API uses simple key-based authentication. Every request must include your unique key as a URL query parameter.
?key=your_sdx_key_here
🔐 Security — Your SDX key is tied to a specific node (e.g., in0). The API verifies that your key matches the node you're accessing. Find your API endpoint URL in your SDX Management Panel.
Endpoint URL
The API endpoint URL varies based on your assigned node. Find your specific endpoint in your SDX Management Panel.
https://node-in0-bucket.shulker.in/
⚠️ Important — Always use your actual endpoint URL from your management panel. The example above is for illustration only.
Initialize Upload
POSTInitialize a chunked file upload. Validates storage space and prepares the system for receiving file chunks.
/?key=YOUR_KEY&action=upload-init&path=/
| Parameter | Type | Description |
|---|---|---|
| fileName | String | Name of the file to upload |
| fileSize | Integer | Total file size in bytes (max 1TB) |
| totalChunks | Integer | Number of chunks (5MB each, last may be smaller) |
| mimeType | String | MIME type of the file (optional) |
async function initializeUpload(file, sdxKey, path = '/') { const totalChunks = Math.ceil(file.size / (5 * 1024 * 1024)); const formData = new FormData(); formData.append('fileName', file.name); formData.append('fileSize', file.size); formData.append('totalChunks', totalChunks); formData.append('mimeType', file.type); const response = await fetch( `https://node-in0-bucket.shulker.in/?key=${sdxKey}&action=upload-init&path=${path}`, { method: 'POST', body: formData } ); return await response.json(); }
import requests, math, os def initialize_upload(file_path, sdx_key, path='/'): file_size = os.path.getsize(file_path) file_name = os.path.basename(file_path) total_chunks = math.ceil(file_size / (5 * 1024 * 1024)) data = { 'fileName': file_name, 'fileSize': file_size, 'totalChunks': total_chunks, 'mimeType': 'application/octet-stream' } response = requests.post( f'https://node-in0-bucket.shulker.in/?key={sdx_key}&action=upload-init&path={path}', data=data ) return response.json()
curl -X POST "https://node-in0-bucket.shulker.in/?key=YOUR_KEY&action=upload-init&path=/" \ -F "fileName=example.mp4" \ -F "fileSize=104857600" \ -F "totalChunks=20" \ -F "mimeType=video/mp4"
{
"success": true,
"upload_id": "sdx_67890abcdef",
"chunk_size": 5242880,
"message": "Upload initialized successfully"
}
Upload Chunk
POSTUpload individual file chunks. Each chunk should be 5MB (except the last chunk which may be smaller).
/?key=YOUR_KEY&action=upload-chunk
| Header | Description |
|---|---|
| X-Upload-ID | Upload ID from init response |
| X-Chunk-Index | Zero-based chunk index |
async function uploadChunk(file, uploadId, chunkIndex, sdxKey) { const CHUNK_SIZE = 5 * 1024 * 1024; const start = chunkIndex * CHUNK_SIZE; const end = Math.min(start + CHUNK_SIZE, file.size); const chunk = file.slice(start, end); const response = await fetch( `https://node-in0-bucket.shulker.in/?key=${sdxKey}&action=upload-chunk`, { method: 'POST', headers: { 'X-Upload-ID': uploadId, 'X-Chunk-Index': chunkIndex.toString() }, body: chunk } ); return await response.json(); }
def upload_chunk(file_path, upload_id, chunk_index, sdx_key): CHUNK_SIZE = 5 * 1024 * 1024 with open(file_path, 'rb') as f: f.seek(chunk_index * CHUNK_SIZE) chunk_data = f.read(CHUNK_SIZE) headers = { 'X-Upload-ID': upload_id, 'X-Chunk-Index': str(chunk_index) } response = requests.post( f'https://node-in0-bucket.shulker.in/?key={sdx_key}&action=upload-chunk', headers=headers, data=chunk_data ) return response.json()
curl -X POST "https://node-in0-bucket.shulker.in/?key=YOUR_KEY&action=upload-chunk" \ -H "X-Upload-ID: sdx_67890abcdef" \ -H "X-Chunk-Index: 0" \ --data-binary "@chunk_0.bin"
{
"success": true,
"chunk_index": 0,
"uploaded_chunks": 1,
"total_chunks": 20,
"progress_percent": 5.0,
"bytes_uploaded": 5242880,
"bytes_remaining": 99614720
}
Complete Upload
POSTFinalize the upload by merging all chunks into the final file. Validates chunk integrity and updates storage usage.
/?key=YOUR_KEY&action=upload-complete
async function completeUpload(uploadId, sdxKey) { const formData = new FormData(); formData.append('uploadId', uploadId); const response = await fetch( `https://node-in0-bucket.shulker.in/?key=${sdxKey}&action=upload-complete`, { method: 'POST', body: formData } ); return await response.json(); }
def complete_upload(upload_id, sdx_key): data = {'uploadId': upload_id} response = requests.post( f'https://node-in0-bucket.shulker.in/?key={sdx_key}&action=upload-complete', data=data ) return response.json()
curl -X POST "https://node-in0-bucket.shulker.in/?key=YOUR_KEY&action=upload-complete" \ -F "uploadId=sdx_67890abcdef"
{
"success": true,
"message": "Upload completed successfully",
"file_name": "example.mp4",
"file_size": 104857600,
"file_size_formatted": "100 MB",
"path": "example.mp4",
"full_path": "/your_key/example.mp4",
"mime_type": "video/mp4",
"checksum": "d41d8cd98f00b204e9800998ecf8427e",
"upload_time_seconds": 45.32,
"merge_time_seconds": 2.15,
"average_speed_mbps": 2.21,
"chunks_uploaded": 20,
"storage_used_gb": 0.0977,
"storage_limit_gb": 100
}
Resume Upload
POSTCheck if an incomplete upload exists and get the list of already uploaded chunks to resume from where you left off.
/?key=YOUR_KEY&action=upload-resume&path=/
async function resumeUpload(fileName, sdxKey, path = '/') { const formData = new FormData(); formData.append('fileName', fileName); const response = await fetch( `https://node-in0-bucket.shulker.in/?key=${sdxKey}&action=upload-resume&path=${path}`, { method: 'POST', body: formData } ); return await response.json(); }
def resume_upload(file_name, sdx_key, path='/'): data = {'fileName': file_name} response = requests.post( f'https://node-in0-bucket.shulker.in/?key={sdx_key}&action=upload-resume&path={path}', data=data ) return response.json()
curl -X POST "https://node-in0-bucket.shulker.in/?key=YOUR_KEY&action=upload-resume&path=/" \ -F "fileName=example.mp4"
{
"success": true,
"can_resume": true,
"upload_id": "sdx_67890abcdef",
"uploaded_chunks": [0, 1, 2, 3, 4],
"total_chunks": 20,
"progress_percent": 25.0,
"bytes_uploaded": 26214400,
"bytes_remaining": 78643200
}
List Contents
GETList all files and folders in a specified directory path.
/?key=YOUR_KEY&action=list-contents&path=/
async function listContents(sdxKey, path = '/') { const response = await fetch( `https://node-in0-bucket.shulker.in/?key=${sdxKey}&action=list-contents&path=${path}` ); return await response.json(); }
def list_contents(sdx_key, path='/'): response = requests.get( f'https://node-in0-bucket.shulker.in/?key={sdx_key}&action=list-contents&path={path}' ) return response.json()
curl "https://node-in0-bucket.shulker.in/?key=YOUR_KEY&action=list-contents&path=/"
{
"success": true,
"path": "",
"full_path": "/your_key/",
"items": [
{
"name": "videos",
"path": "videos",
"type": "folder",
"size": 0,
"size_formatted": "-",
"modified": 1697385600,
"modified_formatted": "2025-10-15 12:00:00"
},
{
"name": "example.mp4",
"path": "example.mp4",
"type": "file",
"size": 104857600,
"size_formatted": "100 MB",
"modified": 1697385645,
"modified_formatted": "2025-10-15 12:00:45",
"mime_type": "video/mp4",
"can_read": false
}
],
"total_items": 2,
"folders": 1,
"files": 1
}
Download File
GETDownload a file from your SDX storage. Returns the file as a binary stream.
/?key=YOUR_KEY&action=download&path=/file.ext
async function downloadFile(sdxKey, filePath) { const response = await fetch( `https://node-in0-bucket.shulker.in/?key=${sdxKey}&action=download&path=${filePath}` ); const blob = await response.blob(); const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = filePath.split('/').pop(); a.click(); window.URL.revokeObjectURL(url); }
def download_file(sdx_key, file_path, save_path): response = requests.get( f'https://node-in0-bucket.shulker.in/?key={sdx_key}&action=download&path={file_path}', stream=True ) with open(save_path, 'wb') as f: for chunk in response.iter_content(chunk_size=8192): f.write(chunk) return save_path
curl "https://node-in0-bucket.shulker.in/?key=YOUR_KEY&action=download&path=/example.mp4" \ -o example.mp4
Read File Content
GETRead the contents of a text-based file (max 100MB). Only works with readable file types.
/?key=YOUR_KEY&action=read-file&path=/file.txt
async function readFile(sdxKey, filePath) { const response = await fetch( `https://node-in0-bucket.shulker.in/?key=${sdxKey}&action=read-file&path=${filePath}` ); return await response.json(); }
def read_file(sdx_key, file_path): response = requests.get( f'https://node-in0-bucket.shulker.in/?key={sdx_key}&action=read-file&path={file_path}' ) return response.json()
curl "https://node-in0-bucket.shulker.in/?key=YOUR_KEY&action=read-file&path=/config.json"
{
"success": true,
"file_name": "config.json",
"file_size": 1024,
"mime_type": "application/json",
"content": "{\"setting\": \"value\"}",
"encoding": "UTF-8"
}
Delete File/Folder
DELETEDelete a file or folder (including all contents). This action is irreversible.
/?key=YOUR_KEY&action=delete&path=/file.ext
async function deleteItem(sdxKey, itemPath) { const response = await fetch( `https://node-in0-bucket.shulker.in/?key=${sdxKey}&action=delete&path=${itemPath}`, { method: 'DELETE' } ); return await response.json(); }
def delete_item(sdx_key, item_path): response = requests.delete( f'https://node-in0-bucket.shulker.in/?key={sdx_key}&action=delete&path={item_path}' ) return response.json()
curl -X DELETE "https://node-in0-bucket.shulker.in/?key=YOUR_KEY&action=delete&path=/example.mp4"
{
"success": true,
"message": "Deleted successfully",
"path": "example.mp4",
"size_freed": 104857600,
"size_freed_formatted": "100 MB"
}
Rename File/Folder
POSTRename a file or folder to a new name within the same directory.
/?key=YOUR_KEY&action=rename&path=/oldname.ext
async function renameItem(sdxKey, oldPath, newName) { const formData = new FormData(); formData.append('newName', newName); const response = await fetch( `https://node-in0-bucket.shulker.in/?key=${sdxKey}&action=rename&path=${oldPath}`, { method: 'POST', body: formData } ); return await response.json(); }
def rename_item(sdx_key, old_path, new_name): data = {'newName': new_name} response = requests.post( f'https://node-in0-bucket.shulker.in/?key={sdx_key}&action=rename&path={old_path}', data=data ) return response.json()
curl -X POST "https://node-in0-bucket.shulker.in/?key=YOUR_KEY&action=rename&path=/old.mp4" \ -F "newName=new.mp4"
{
"success": true,
"message": "Renamed successfully",
"old_name": "old.mp4",
"new_name": "new.mp4",
"new_path": "new.mp4"
}
Create Folder
POSTCreate a new folder (sub-bucket) to organize your files better.
/?key=YOUR_KEY&action=create-folder&path=/
async function createFolder(sdxKey, folderName, path = '/') { const formData = new FormData(); formData.append('folderName', folderName); const response = await fetch( `https://node-in0-bucket.shulker.in/?key=${sdxKey}&action=create-folder&path=${path}`, { method: 'POST', body: formData } ); return await response.json(); }
def create_folder(sdx_key, folder_name, path='/'): data = {'folderName': folder_name} response = requests.post( f'https://node-in0-bucket.shulker.in/?key={sdx_key}&action=create-folder&path={path}', data=data ) return response.json()
curl -X POST "https://node-in0-bucket.shulker.in/?key=YOUR_KEY&action=create-folder&path=/" \ -F "folderName=videos"
{
"success": true,
"message": "Folder created successfully",
"folder_name": "videos",
"path": "videos"
}
Get File/Folder Info
GETGet detailed information about a specific file or folder including size, type, and modification date.
/?key=YOUR_KEY&action=info&path=/file.ext
async function getInfo(sdxKey, itemPath) { const response = await fetch( `https://node-in0-bucket.shulker.in/?key=${sdxKey}&action=info&path=${itemPath}` ); return await response.json(); }
def get_info(sdx_key, item_path): response = requests.get( f'https://node-in0-bucket.shulker.in/?key={sdx_key}&action=info&path={item_path}' ) return response.json()
curl "https://node-in0-bucket.shulker.in/?key=YOUR_KEY&action=info&path=/example.mp4"
{
"success": true,
"name": "example.mp4",
"path": "example.mp4",
"type": "file",
"modified": 1697385645,
"modified_formatted": "2025-10-15 12:00:45",
"size": 104857600,
"size_formatted": "100 MB",
"mime_type": "video/mp4",
"checksum": "d41d8cd98f00b204e9800998ecf8427e",
"can_read": false
}
Get Account Info
GETRetrieve information about your SDX account including storage usage and limits.
/?key=YOUR_KEY&action=account-info
async function getAccountInfo(sdxKey) { const response = await fetch( `https://node-in0-bucket.shulker.in/?key=${sdxKey}&action=account-info` ); return await response.json(); }
def get_account_info(sdx_key): response = requests.get( f'https://node-in0-bucket.shulker.in/?key={sdx_key}&action=account-info' ) return response.json()
curl "https://node-in0-bucket.shulker.in/?key=YOUR_KEY&action=account-info"
{
"success": true,
"account": {
"sdx_key": "your_key_here",
"owner": "[email protected]",
"storage_used_gb": 10.5,
"storage_limit_gb": 100,
"storage_used_percent": 10.5,
"storage_available_gb": 89.5,
"billing_amount": "5.00",
"billing_period": "monthly",
"next_billing": "2025-11-15",
"created_at": "2025-01-01 00:00:00"
}
}
Error Handling
| Status Code | Meaning | Common Causes |
|---|---|---|
| 400 | Bad Request | Invalid SDX key, missing parameters, or data validation failed |
| 401 | Unauthorized | Billing overdue or inactive account |
| 403 | Forbidden | Node mismatch - key doesn't match the node being accessed |
| 404 | Not Found | File or folder doesn't exist |
| 500 | Internal Server Error | Database connection failed or server-side issue |
{
"success": false,
"error": "Insufficient storage space. Used: 95.5GB, Limit: 100GB, Required: 5GB",
"timestamp": 1697385645
}
SDX API Tester
🚀 Try the Interactive API Tester — Want to test the SDX API without writing code? Use our interactive API tester tool to explore all endpoints, upload files, manage folders, and see real-time responses.