Shulker API v2.1.0
Programmatically control Shulker's high-performance gaming infrastructure. Manage servers, containers, teams, and files through a unified REST API with enterprise-grade features.
Introduction
https://shulker.in/api/v2/
Authentication
All API requests require authentication via API keys. Keys provide granular permissions and rate limiting.
?api_key=sk_1e14aecda1a06bc22062f8fcb8e90cbde309d29d1e7955378b1aeaf6c130cdc3
X-API-Key: sk_1e14aecda1a06bc22062f8fcb8e90cbde309d29d1e7955378b1aeaf6c130cdc3
🔐 Security Note — API keys grant access to your infrastructure. Treat them like passwords. Never commit API keys to version control. Use environment variables or secret management. Rotate keys regularly (every 90 days recommended).
Permissions System
Granular permission system with read and write categories. Each API key can have specific permissions assigned.
curl "https://shulker.in/api/v2/?req=permissions&api_key=sk_..."
Rate Limits
X-RateLimit-Limit: 60 X-RateLimit-Remaining: 45 X-RateLimit-Reset: 1697385645
Error Handling
| Status Code | Meaning |
|---|---|
| 200 OK | Request successful |
| 400 Bad Request | Invalid parameters |
| 401 Unauthorized | Invalid API key |
| 403 Forbidden | Insufficient permissions |
| 404 Not Found | Resource not found |
| 429 Too Many Requests | Rate limit exceeded |
| 500 Internal Error | Server error |
{
"error": "Authorization failed",
"message": "Insufficient permissions",
"code": 403,
"timestamp": "2026-01-07T16:28:03+05:30",
"request_id": "req_659a1b2c3d4e5f"
}
Minecraft Server Management
curl "https://shulker.in/api/v2/?req=minecraft&action=server_start&server_name=MyServer&node=de0&api_key=sk_..."
curl "https://shulker.in/api/v2/?req=minecraft&action=server_stop&server_name=MyServer&node=de0&api_key=sk_..."
curl "https://shulker.in/api/v2/?req=minecraft&action=server_restart&server_name=MyServer&node=de0&api_key=sk_..."
curl "https://shulker.in/api/v2/?req=minecraft&action=server_command&server_name=MyServer&node=de0&cmd=say%20Hello%20World&api_key=sk_..."
curl "https://shulker.in/api/v2/?req=minecraft&action=server_status&server_name=MyServer&node=de0&api_key=sk_..."
curl "https://shulker.in/api/v2/?req=minecraft&action=server_logs&server_name=MyServer&node=de0&lines=100&api_key=sk_..."
curl "https://shulker.in/api/v2/?req=servers&api_key=sk_..."
DevSpace Containers
curl "https://shulker.in/api/v2/?req=devspace&action=server_start&server_name=my-node-app&node=us1&api_key=sk_..."
curl "https://shulker.in/api/v2/?req=devspace&action=server_stop&server_name=my-node-app&node=us1&api_key=sk_..."
curl "https://shulker.in/api/v2/?req=devspace&action=server_command&server_name=my-node-app&node=us1&cmd=npm%20install&api_key=sk_..."
curl "https://shulker.in/api/v2/?req=devspace&action=server_logs&server_name=my-node-app&node=us1&api_key=sk_..."
File Operations
curl "https://shulker.in/api/v2/?req=minecraft&action=file_list&server_name=MyServer&node=de0&path=/&api_key=sk_..."
curl "https://shulker.in/api/v2/?req=minecraft&action=file_read&server_name=MyServer&node=de0&path=server.properties&api_key=sk_..."
curl -X POST https://shulker.in/api/v2/ \ -H "Content-Type: application/json" \ -d '{ "req": "minecraft", "action": "file_write", "server_name": "MyServer", "node": "de0", "path": "server.properties", "content": "max-players=50\\ndifficulty=hard\\nmotd=My Awesome Server", "api_key": "sk_..." }'
curl -X POST https://shulker.in/api/v2/ \ -F "req=minecraft" \ -F "action=file_upload" \ -F "server_name=MyServer" \ -F "node=de0" \ -F "path=plugins/" \ -F "api_key=sk_..." \ -F "[email protected]"
curl -X DELETE "https://shulker.in/api/v2/?req=minecraft&action=file_delete&server_name=MyServer&node=de0&path=oldfile.txt&api_key=sk_..."
User Management
curl "https://shulker.in/api/v2/?req=info&api_key=sk_..."
curl "https://shulker.in/api/v2/?req=wallet&api_key=sk_..."
Team Management
curl "https://shulker.in/api/v2/?req=teams&api_key=sk_..."
curl -X POST https://shulker.in/api/v2/ \ -H "Content-Type: application/json" \ -d '{ "req": "teams", "team_name": "My Development Team", "initial_funds": 100, "api_key": "sk_..." }'
curl -X POST https://shulker.in/api/v2/ \ -H "Content-Type: application/json" \ -d '{ "req": "teams", "action": "add_member", "team_id": 1, "user_email": "[email protected]", "api_key": "sk_..." }'
API Key Management
curl "https://shulker.in/api/v2/?req=api_keys&api_key=sk_..."
curl -X POST https://shulker.in/api/v2/ \ -H "Content-Type: application/json" \ -d '{ "req": "api_keys", "name": "Monitoring Bot", "description": "Server monitoring and alerting", "permissions": { "read": ["servers", "server_status", "server_logs", "server_console"], "write": [] }, "server_restrictions": ["prod-*", "monitoring-*"], "ip_restrictions": ["192.168.1.0/24", "10.0.0.0/8"], "expires_at": "2026-04-01", "rate_limit_per_minute": 30, "rate_limit_per_hour": 500, "rate_limit_per_day": 5000, "api_key": "sk_..." }'
curl -X PUT https://shulker.in/api/v2/ \ -H "Content-Type: application/json" \ -d '{ "req": "api_keys", "action": "update", "key_id": 3, "name": "Updated Bot Name", "permissions": { "read": ["servers", "server_status", "server_logs", "server_console", "server_stats"], "write": [] }, "api_key": "sk_..." }'
curl -X DELETE "https://shulker.in/api/v2/?req=api_keys&key_id=3&api_key=sk_..."
curl -X POST https://shulker.in/api/v2/ \ -H "Content-Type: application/json" \ -d '{ "req": "api_keys", "action": "rotate", "key_id": 3, "api_key": "sk_..." }'
Code Examples
import requests import json from typing import Optional, Dict, Any class ShulkerAPI: def __init__(self, api_key: str, base_url: str = "https://shulker.in/api/v2/"): self.api_key = api_key self.base_url = base_url self.session = requests.Session() def _request(self, params: Dict[str, Any], method: str = "GET") -> Dict[str, Any]: params['api_key'] = self.api_key url = self.base_url if method.upper() == "GET": response = self.session.get(url, params=params) else: response = self.session.post(url, json=params) response.raise_for_status() return response.json() def get_user_info(self) -> Dict[str, Any]: return self._request({'req': 'info'}) def list_servers(self) -> Dict[str, Any]: return self._request({'req': 'servers'}) def start_server(self, server_name: str, node: str) -> Dict[str, Any]: return self._request({ 'req': 'minecraft', 'action': 'server_start', 'server_name': server_name, 'node': node }) def stop_server(self, server_name: str, node: str) -> Dict[str, Any]: return self._request({ 'req': 'minecraft', 'action': 'server_stop', 'server_name': server_name, 'node': node }) def restart_server(self, server_name: str, node: str) -> Dict[str, Any]: return self._request({ 'req': 'minecraft', 'action': 'server_restart', 'server_name': server_name, 'node': node }) def execute_command(self, server_name: str, node: str, command: str) -> Dict[str, Any]: return self._request({ 'req': 'minecraft', 'action': 'server_command', 'server_name': server_name, 'node': node, 'cmd': command }) def get_server_status(self, server_name: str, node: str) -> Dict[str, Any]: return self._request({ 'req': 'minecraft', 'action': 'server_status', 'server_name': server_name, 'node': node }) def get_server_logs(self, server_name: str, node: str, lines: int = 100) -> Dict[str, Any]: return self._request({ 'req': 'minecraft', 'action': 'server_logs', 'server_name': server_name, 'node': node, 'lines': lines }) def list_files(self, server_name: str, node: str, path: str = "/") -> Dict[str, Any]: return self._request({ 'req': 'minecraft', 'action': 'file_list', 'server_name': server_name, 'node': node, 'path': path }) def read_file(self, server_name: str, node: str, file_path: str) -> Dict[str, Any]: return self._request({ 'req': 'minecraft', 'action': 'file_read', 'server_name': server_name, 'node': node, 'path': file_path }) def write_file(self, server_name: str, node: str, file_path: str, content: str) -> Dict[str, Any]: return self._request({ 'req': 'minecraft', 'action': 'file_write', 'server_name': server_name, 'node': node, 'path': file_path, 'content': content }, method="POST") def create_api_key(self, name: str, permissions: Dict, **kwargs) -> Dict[str, Any]: data = { 'req': 'api_keys', 'name': name, 'permissions': permissions, **kwargs } return self._request(data, method="POST") def delete_api_key(self, key_id: int) -> Dict[str, Any]: return self._request({ 'req': 'api_keys', 'key_id': key_id }, method="DELETE") # Usage Example if __name__ == "__main__": client = ShulkerAPI("sk_your_api_key_here") # Get user information user = client.get_user_info() print(f"Logged in as: {user['user']['email']}") # List all servers servers = client.list_servers() for server in servers['servers']: print(f"Server: {server['server_name']} - Status: {server['server_status']}") # Start a server result = client.start_server("MySurvivalServer", "de0") print(f"Start result: {result['message']}") # Execute a command cmd_result = client.execute_command("MySurvivalServer", "de0", "say Server started via API!") print(f"Command result: {cmd_result['success']}")
class ShulkerAPI { constructor(apiKey, baseUrl = 'https://shulker.in/api/v2/') { this.apiKey = apiKey; this.baseUrl = baseUrl; } async request(params, method = 'GET') { params.api_key = this.apiKey; const url = new URL(this.baseUrl); if (method === 'GET') { url.search = new URLSearchParams(params).toString(); const response = await fetch(url); if (!response.ok) throw new Error(await response.text()); return response.json(); } else { const response = await fetch(url, { method: method, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(params) }); if (!response.ok) throw new Error(await response.text()); return response.json(); } } async getUserInfo() { return this.request({ req: 'info' }); } async listServers() { return this.request({ req: 'servers' }); } async startServer(serverName, node) { return this.request({ req: 'minecraft', action: 'server_start', server_name: serverName, node: node }); } async stopServer(serverName, node) { return this.request({ req: 'minecraft', action: 'server_stop', server_name: serverName, node: node }); } async restartServer(serverName, node) { return this.request({ req: 'minecraft', action: 'server_restart', server_name: serverName, node: node }); } async executeCommand(serverName, node, command) { return this.request({ req: 'minecraft', action: 'server_command', server_name: serverName, node: node, cmd: command }); } async getServerStatus(serverName, node) { return this.request({ req: 'minecraft', action: 'server_status', server_name: serverName, node: node }); } async getServerLogs(serverName, node, lines = 100) { return this.request({ req: 'minecraft', action: 'server_logs', server_name: serverName, node: node, lines: lines }); } async listFiles(serverName, node, path = '/') { return this.request({ req: 'minecraft', action: 'file_list', server_name: serverName, node: node, path: path }); } async readFile(serverName, node, filePath) { return this.request({ req: 'minecraft', action: 'file_read', server_name: serverName, node: node, path: filePath }); } async createAPIKey(name, permissions, options = {}) { return this.request({ req: 'api_keys', name: name, permissions: permissions, ...options }, 'POST'); } async deleteAPIKey(keyId) { return this.request({ req: 'api_keys', key_id: keyId }, 'DELETE'); } } // Usage Example async function main() { const client = new ShulkerAPI('sk_your_api_key_here'); try { // Get user info const user = await client.getUserInfo(); console.log(`Logged in as: ${user.user.email}`); // List servers const servers = await client.listServers(); servers.servers.forEach(server => { console.log(`Server: ${server.server_name} - Status: ${server.server_status}`); }); // Start a server const result = await client.startServer('MySurvivalServer', 'de0'); console.log(`Start result: ${result.message}`); } catch (error) { console.error('Error:', error.message); } } main();
#!/bin/bash # Configuration API_KEY="sk_your_api_key_here" BASE_URL="https://shulker.in/api/v2/" SERVER_NAME="MySurvivalServer" NODE="de0" LOG_FILE="/var/log/shulker-api.log" # Logging function log_message() { echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOG_FILE" } # Make API request make_request() { local params="$1" local url="${BASE_URL}?${params}&api_key=${API_KEY}" curl -s -X GET "$url" } # Check server status check_status() { log_message "Checking status of server: $SERVER_NAME" local response=$(make_request "req=minecraft&action=server_status&server_name=${SERVER_NAME}&node=${NODE}") local status=$(echo "$response" | jq -r '.data.server_status // "unknown"' 2>/dev/null) if [[ "$status" == *"Running"* ]]; then log_message "Server is running" echo "✅ Server is running" return 0 else log_message "Server is not running: $status" echo "❌ Server is not running" return 1 fi } # Start server start_server() { log_message "Starting server: $SERVER_NAME" local response=$(make_request "req=minecraft&action=server_start&server_name=${SERVER_NAME}&node=${NODE}") local success=$(echo "$response" | jq -r '.success // false' 2>/dev/null) if [[ "$success" == "true" ]]; then log_message "Server started successfully" echo "✅ Server started successfully" else log_message "Failed to start server: $response" echo "❌ Failed to start server" fi } # Stop server stop_server() { log_message "Stopping server: $SERVER_NAME" local response=$(make_request "req=minecraft&action=server_stop&server_name=${SERVER_NAME}&node=${NODE}") local success=$(echo "$response" | jq -r '.success // false' 2>/dev/null) if [[ "$success" == "true" ]]; then log_message "Server stopped successfully" echo "✅ Server stopped successfully" else log_message "Failed to stop server: $response" echo "❌ Failed to stop server" fi } # Send command send_command() { local cmd="$1" log_message "Sending command: $cmd" local encoded_cmd=$(printf '%s' "$cmd" | jq -sRr @uri) local response=$(make_request "req=minecraft&action=server_command&server_name=${SERVER_NAME}&node=${NODE}&cmd=${encoded_cmd}") echo "$response" | jq '.' } # Main menu case "$1" in status) check_status ;; start) start_server ;; stop) stop_server ;; restart) stop_server sleep 5 start_server ;; command) if [[ -z "$2" ]]; then echo "Usage: $0 command \"\"" exit 1 fi send_command "$2" ;; *) echo "Usage: $0 {status|start|stop|restart|command}" echo " status - Check server status" echo " start - Start the server" echo " stop - Stop the server" echo " restart - Restart the server" echo " command - Send a command to the server" exit 1 ;; esac
Best Practices
🔐 Security — Use environment variables for API keys, never commit keys to version control, implement IP restrictions, rotate keys every 90 days, and always apply the principle of least privilege when assigning permissions.
⚡ Performance — Implement caching for frequently accessed data (server status, user info), batch operations when possible, use appropriate polling intervals for status checks, and consider webhooks for real-time updates instead of polling.
🔄 Error Handling — Implement exponential backoff with jitter for 5xx errors and rate limits (429), use circuit breaker pattern to prevent cascade failures, log all errors with request IDs for debugging, and set up alerting for persistent failures.
📝 Development — Build or use client libraries for your language, always test API changes in staging first, specify API version in requests, and keep internal API usage documentation updated.
Changelog
v2.1.0 (January 2025) — Added comprehensive API key management endpoints (create, list, update, delete, rotate). Enhanced permission system with granular read/write controls. Added server expiry checks for start/restart operations. Implemented IP and server restrictions for API keys. Added rate limit per server per day functionality. Enhanced error responses with detailed permission information. Improved request logging with Cloudflare integration. Added request ID tracking for better debugging.
v2.0.0 (December 2024) — Complete rewrite of API architecture. Unified endpoint structure for Minecraft and DevSpace. Added team management capabilities (create teams, add members, manage permissions). Implemented advanced permission system with read/write categories. Added comprehensive file operation endpoints (list, read, write, upload, delete). Optimized database queries for better performance. Enhanced CORS headers for better cross-origin support.
⚠️ Deprecation Notice — API v1.x will be sunset on June 30, 2026. Please migrate to v2.x before this date. All v1.x endpoints will continue to work until the sunset date, but new features will only be available in v2.x. View the migration guide at /docs/migration-guide