Pterodactyl Client API Reference
Comprehensive developer reference for the Pterodactyl Panel 1.x Client API on VisiHost, covering authentication, server controls, file management, backups, and automation.
Welcome to the VisiHost Pterodactyl Client API Reference. This documentation provides a complete technical guide for interacting programmatically with your game servers, bot containers, audio nodes, and databases hosted on the VisiHost platform.
The Client API enables developers to automate server management workflows, build custom Discord bot status integrations, orchestrate CI/CD game updates, automate backups, and query real-time server telemetry.
1. Getting Started & Core Concepts#
1.1 Base URL & Environment#
All Client API endpoints operate under the /api/client path prefix:
- Official VisiHost Panel:
https://paid.visihost.in - Client API Base URL:
https://paid.visihost.in/api/client - Documentation Standard Placeholder:
https://panel.example.com
The v1 in official documentation URLs (e.g., docs.pterodactyl.io/v1/api) designates the documentation revision, NOT an API request path segment.
1.2 Client API vs. Application API#
- Client API (
/api/client): Designed for account owners and subusers. It grants permission-bounded access to individual server containers, power states, files, schedules, and databases. - Application API (
/api/application): An administrative API used by infrastructure operators to manage bare-metal nodes, system nests, and global user provisioning. The Application API is reserved for infrastructure administrators and is not covered here.
2. Authentication & Security#
All requests to the Client API require a user-generated Client API Key transmitted via the HTTP Authorization header as a Bearer token.
2.1 Generating a Client API Key#
- Sign in to your control panel at paid.visihost.in.
- Click your user avatar / username in the upper-right corner and select Account Settings (or navigate directly to
/account/api). - Click the API Credentials tab.
- Enter a descriptive name in the Description field (e.g.,
Discord Bot ManagerorGitHub CI Deployer). - In the Allowed IPs field, optionally specify CIDR IP ranges or specific server IP addresses permitted to authenticate with this token. Leave blank to allow requests from any source IP.
- Click Create.
- Copy your secret key immediately. Pterodactyl Client keys begin with the prefix
ptlc_and are displayed only once.
2.2 Standard Request Headers#
Include these headers with every HTTP request:
| Header Name | Required Value | Description |
|---|---|---|
Authorization | Bearer ptlc_YOUR_API_KEY | Bearer token authentication with your Client API key. |
Accept | Application/vnd.pterodactyl.v1+json | Informs the panel serializer to output v1 Fractal JSON structures. |
Content-Type | application/json | Required on all POST, PUT, and PATCH requests carrying JSON payloads. |
curl -X GET "https://paid.visihost.in/api/client/account" \
-H "Authorization: Bearer ptlc_YOUR_API_KEY" \
-H "Accept: Application/vnd.pterodactyl.v1+json"3. Response Conventions, Pagination & Error Handling#
3.1 JSON Response Envelopes#
Pterodactyl formats all responses through the Fractal transformation layer:
Single Resource Item
{
"object": "server",
"attributes": {
"identifier": "a1b2c3d4",
"name": "Production Bot Node",
"node": "in-del-01"
}
}Paginated Resource Collection
{
"object": "list",
"data": [
{
"object": "server",
"attributes": {
"identifier": "a1b2c3d4",
"name": "Production Bot Node"
}
}
],
"meta": {
"pagination": {
"total": 1,
"count": 1,
"per_page": 50,
"current_page": 1,
"total_pages": 1,
"links": {}
}
}
}3.2 Error Response Format#
When a request fails, the panel returns an HTTP 4xx or 5xx status code with an errors collection:
{
"errors": [
{
"code": "HttpForbiddenException",
"status": "403",
"detail": "You do not have permission to perform this action."
}
]
}Validation Error Schema (422 Unprocessable Entity)
{
"errors": [
{
"code": "ValidationException",
"status": "422",
"detail": "The signal field is required.",
"source": {
"field": "signal"
}
}
]
}3.3 HTTP Status Codes Reference#
| Status Code | Reason | Cause & Resolution |
|---|---|---|
200 OK | Success | Request succeeded and returned a response body. |
201 Created | Created | Resource (API key, subuser, task) created successfully. |
202 Accepted | Accepted | Action queued asynchronously (e.g., server reinstall). |
204 No Content | Success | Action succeeded without returning body content (e.g., power action, deletion). |
400 Bad Request | Invalid State | Attempting to delete a locked backup, exceed feature limits, or pass invalid arguments. |
401 Unauthorized | Invalid Key | API key is missing, expired, or lacked the Bearer header prefix. |
403 Forbidden | Access Denied | The API key or user account lacks the specific permission node required. |
404 Not Found | Resource Missing | Invalid 8-character server identifier, file path, database ID, or subuser UUID. |
405 Method Not Allowed | Wrong HTTP Verb | Using GET instead of POST, or POST instead of PUT. |
422 Unprocessable Entity | Validation Error | Payload failed validation (e.g., invalid cron expression or bad variable rules). |
429 Too Many Requests | Rate Limited | Too many requests sent within a short window. Back off and retry. |
502 Bad Gateway | Server Offline / Wings Down | Wings daemon is unreachable, or server must be online to execute command. |
504 Gateway Timeout | Timeout | Node operation took longer than timeout limit (e.g., large archive decompression). |
4. Complete Client API Endpoint Reference#
4.1 Account & API Credentials (/api/client/account)#
Get Account Profile
- Method:
GET - Path:
/api/client/account - Permissions: Authenticated Account Token
- Success Status:
200 OK - Response:
{
"object": "user",
"attributes": {
"id": 1,
"admin": false,
"username": "developer",
"email": "user@example.com",
"first_name": "Alex",
"last_name": "Dev",
"language": "en"
}
}Update Email Address
- Method:
PUT - Path:
/api/client/account/email - Request Body:
{
"email": "newemail@example.com",
"password": "CurrentPassword123"
}- Success Status:
204 No Content - Rate Limit: Maximum of 3 email updates per 24 hours.
Update Password
- Method:
PUT - Path:
/api/client/account/password - Request Body:
{
"current_password": "OldPassword123",
"password": "NewStrongPassword456!",
"password_confirmation": "NewStrongPassword456!"
}- Success Status:
204 No Content
List API Keys
- Method:
GET - Path:
/api/client/account/api-keys - Success Status:
200 OK - Response: Returns a
listcollection ofapi_keyobjects (identifier,description,allowed_ips,last_used_at,created_at).
Create API Key
- Method:
POST - Path:
/api/client/account/api-keys - Request Body:
{
"description": "Discord Bot CI Runner",
"allowed_ips": ["198.51.100.25"]
}- Success Status:
201 Created - Response: Returns the
api_keyobject withmeta.secret_tokencontaining the fullptlc_...key. - Account Limit: Maximum of 25 active API keys per account.
Delete API Key
- Method:
DELETE - Path:
/api/client/account/api-keys/{identifier} - Path Parameter:
identifier(string, e.g.,AB12CD34) - Success Status:
204 No Content
Manage SSH Keys
GET /api/client/account/ssh-keys: List registered SSH public keys.POST /api/client/account/ssh-keys: Register an SSH key (name,public_key). Returns201 Created.POST /api/client/account/ssh-keys/remove: Delete key with body{"fingerprint": "..."}. Returns204 No Content.
4.2 Server Information & Live Metrics#
List Accessible Servers
- Method:
GET - Path:
/api/client - Query Parameters:
page(integer, optional): Page number (default:1).per_page(integer, optional): Items per page (default:50, max:100).type(string, optional): Passownerto filter only servers owned by the user.filter[name](string, optional): Filter by server name.include(string, optional): Relationships to eager load (e.g.,allocations,variables).
- Success Status:
200 OK - Response Attributes:
identifier: 8-character short string (e.g.,a1b2c3d4) used in all server-specific paths.uuid: Full 36-character UUID string.limits: Container limits (memory,swap,disk,io,cpu).feature_limits: Allowed resources (databases,allocations,backups).
Get Server Details
- Method:
GET - Path:
/api/client/servers/{server} - Path Parameter:
server(8-character short ID) - Success Status:
200 OK
Get Live Resource Utilization
- Method:
GET - Path:
/api/client/servers/{server}/resources - Description: Returns live runtime metrics directly from the Wings daemon.
- Success Status:
200 OK - Response:
{
"object": "stats",
"attributes": {
"current_state": "running",
"is_suspended": false,
"resources": {
"memory_bytes": 268435456,
"cpu_absolute": 18.25,
"disk_bytes": 1073741824,
"network_rx_bytes": 145000,
"network_tx_bytes": 290000,
"uptime": 86400000
}
}
}- Caching Note: Results are cached internally by the panel for up to 20 seconds.
Get WebSocket Credentials
- Method:
GET - Path:
/api/client/servers/{server}/websocket - Permissions:
websocket.connect - Success Status:
200 OK - Response: Returns a one-time JWT token and
wss://socket address valid for 10 minutes.
4.3 Power Actions & Console Commands#
Send Power Action
- Method:
POST - Path:
/api/client/servers/{server}/power - Permissions:
control.start(forstart)control.stop(forstop)control.restart(forrestart)control.stop(forkill)
- Request Body:
{
"signal": "restart"
}- Signals:
"start": Boots the container if offline."stop": Sends the graceful shutdown command or signal."restart": Gracefully stops the container and reboots it."kill": Sends an immediateSIGKILLto force-stop an unresponsive process.
- Success Status:
204 No Content
Send Console Command
- Method:
POST - Path:
/api/client/servers/{server}/command - Permissions:
control.console - Request Body:
{
"command": "say Broadcast message to all players"
}- Success Status:
204 No Content - Error Behavior: Returns
502 Bad Gatewaywith message"Server must be online in order to send commands."if the container is offline.
4.4 File Management#
All file paths are relative to the server container root /.
| Operation | Method | Path | Query / Body | Success Status |
|---|---|---|---|---|
| List Files | GET | .../files/list | ?directory=/plugins | 200 OK (list of file_object) |
| Read Contents | GET | .../files/contents | ?file=/config.json | 200 OK (text/plain) |
| Write File | POST | .../files/write | ?file=/config.json (raw text body) | 204 No Content |
| Download URL | GET | .../files/download | ?file=/data.db | 200 OK (signed_url) |
| Upload URL | GET | .../files/upload | None | 200 OK (signed_url) |
| Create Folder | POST | .../files/create-folder | {"root":"/","name":"logs"} | 204 No Content |
| Rename / Move | PUT | .../files/rename | {"root":"/","files":[{"from":"a.txt","to":"b.txt"}]} | 204 No Content |
| Copy File | POST | .../files/copy | {"location":"server.properties"} | 204 No Content |
| Compress Archive | POST | .../files/compress | {"root":"/","files":["src/","package.json"]} | 200 OK (returns archive file_object) |
| Decompress | POST | .../files/decompress | {"root":"/","file":"archive.tar.gz"} | 204 No Content |
| Delete Files | POST | .../files/delete | {"root":"/","files":["old.log","temp/"]} | 204 No Content |
| Chmod Files | POST | .../files/chmod | {"root":"/","files":[{"file":"run.sh","mode":"0755"}]} | 204 No Content |
| Remote Pull | POST | .../files/pull | {"url":"https://...","directory":"/"} | 204 No Content |
4.5 Backups#
List Backups
- Method:
GET - Path:
/api/client/servers/{server}/backups - Permissions:
backup.read - Success Status:
200 OK - Response: List of
backupobjects with attributes:uuid,name,is_successful,is_locked,bytes,checksum,created_at,completed_at.
Create Backup
- Method:
POST - Path:
/api/client/servers/{server}/backups - Permissions:
backup.create - Request Body:
{
"name": "Pre-Update Backup",
"ignored": "*.log\ntmp/*",
"is_locked": false
}- Success Status:
200 OK(returns newly initiatedbackupobject;completed_atisnullwhile asynchronous compression runs). - Restrictions: Returns
400 Bad Requestif the server has reached its plan'sfeature_limits.backupsquota.
Toggle Backup Lock
- Method:
POST - Path:
/api/client/servers/{server}/backups/{backup}/lock - Permissions:
backup.delete - Description: Toggles
is_locked. Locked backups cannot be deleted until unlocked. - Success Status:
200 OK
Restore Backup
- Method:
POST - Path:
/api/client/servers/{server}/backups/{backup}/restore - Permissions:
backup.restore - Request Body:
{
"truncate_dir": true
}- Success Status:
204 No Content
Delete Backup
- Method:
DELETE - Path:
/api/client/servers/{server}/backups/{backup} - Permissions:
backup.delete - Success Status:
204 No Content - Restrictions: Cannot delete a locked backup (
400 Bad Request). Unlock first.
4.6 Schedules & Tasks#
List Schedules
- Method:
GET - Path:
/api/client/servers/{server}/schedules - Permissions:
schedule.read - Success Status:
200 OK
Create Schedule
- Method:
POST - Path:
/api/client/servers/{server}/schedules - Permissions:
schedule.create - Request Body:
{
"name": "Auto-Restart Every 12 Hours",
"is_active": true,
"minute": "0",
"hour": "*/12",
"day_of_month": "*",
"month": "*",
"day_of_week": "*",
"only_when_online": true
}- Success Status:
201 Created
Execute Schedule Immediately
- Method:
POST - Path:
/api/client/servers/{server}/schedules/{schedule}/execute - Permissions:
schedule.update - Success Status:
204 No Content
Create Scheduled Task
- Method:
POST - Path:
/api/client/servers/{server}/schedules/{schedule}/tasks - Permissions:
schedule.update - Request Body:
{
"action": "command",
"payload": "broadcast Scheduled maintenance rebooting now",
"time_offset": 30,
"continue_on_failure": false
}- Allowed
actionvalues:"command": Issues a console command frompayload."power": Sends a power signal (start,stop,restart,kill)."backup": Initiates a backup (payloadholds ignored files). Fails with403if server backup limit is 0.
- Success Status:
201 Created
4.7 Database Management#
List Databases
- Method:
GET - Path:
/api/client/servers/{server}/databases - Permissions:
database.read - Success Status:
200 OK - Response: List of database objects containing database name, username, and connection host.
Create Database
- Method:
POST - Path:
/api/client/servers/{server}/databases - Permissions:
database.create - Request Body:
{
"database": "bot_production",
"remote": "%"
}- Success Status:
200 OK - Response: Returns the database object including
relationships.password.attributes.password. - Restrictions: Cannot exceed the plan's
feature_limits.databases.
Rotate Database Password
- Method:
POST - Path:
/api/client/servers/{server}/databases/{database}/rotate-password - Permissions:
database.update - Success Status:
200 OK(returns newly generated password in response).
Delete Database
- Method:
DELETE - Path:
/api/client/servers/{server}/databases/{database} - Permissions:
database.delete - Success Status:
204 No Content
4.8 Network Allocations#
List Allocations
- Method:
GET - Path:
/api/client/servers/{server}/network/allocations - Permissions:
allocation.read - Success Status:
200 OK
Auto-Assign Allocation
- Method:
POST - Path:
/api/client/servers/{server}/network/allocations - Permissions:
allocation.create - Description: Assigns the next available port from the node pool to the server.
- Success Status:
200 OK - Restrictions: Throws
400 Bad Requestiffeature_limits.allocationsis reached.
Set Primary Allocation
- Method:
POST - Path:
/api/client/servers/{server}/network/allocations/{allocation}/primary - Permissions:
allocation.update - Success Status:
200 OK
Update Allocation Notes
- Method:
POST - Path:
/api/client/servers/{server}/network/allocations/{allocation} - Permissions:
allocation.update - Request Body:
{
"notes": "Voice query port"
}- Success Status:
200 OK
Delete Allocation
- Method:
DELETE - Path:
/api/client/servers/{server}/network/allocations/{allocation} - Permissions:
allocation.delete - Success Status:
204 No Content - Restrictions: You cannot delete the server's primary allocation.
4.9 Startup Configuration & Egg Variables#
Get Startup Configuration
- Method:
GET - Path:
/api/client/servers/{server}/startup - Permissions:
startup.read - Success Status:
200 OK - Response: Returns all environment variables with their defaults, rules, and
is_editablestatus, plusmeta.startup_commandandmeta.docker_images.
Update Startup Variable
- Method:
PUT - Path:
/api/client/servers/{server}/startup/variable - Permissions:
startup.update - Request Body:
{
"key": "JS_FILE",
"value": "bot.js"
}- Success Status:
200 OK - Restrictions: Read-only variables locked by administrators return
400 Bad Request.
4.10 Subusers & Access Control#
List Subusers
- Method:
GET - Path:
/api/client/servers/{server}/users - Permissions:
user.read - Success Status:
200 OK
Invite Subuser
- Method:
POST - Path:
/api/client/servers/{server}/users - Permissions:
user.create - Request Body:
{
"email": "assistant@example.com",
"permissions": [
"control.console",
"control.start",
"control.restart",
"file.read",
"file.read-content"
]
}- Success Status:
200 OK - Security Guardrail: A user can only grant permission nodes they themselves possess. Server owners cannot be added as subusers.
Update Subuser Permissions
- Method:
POST - Path:
/api/client/servers/{server}/users/{user} - Path Parameter:
user(subuser UUID) - Permissions:
user.update - Request Body:
{
"permissions": [
"control.console",
"file.read"
]
}- Success Status:
200 OK
Delete Subuser
- Method:
DELETE - Path:
/api/client/servers/{server}/users/{user} - Permissions:
user.delete - Success Status:
204 No Content
4.11 Server Settings#
Rename Server
- Method:
POST - Path:
/api/client/servers/{server}/settings/rename - Permissions:
settings.rename - Request Body:
{
"name": "Production Bot - Asia Cluster",
"description": "Primary community moderation bot"
}- Success Status:
204 No Content
Reinstall Server
- Method:
POST - Path:
/api/client/servers/{server}/settings/reinstall - Permissions:
settings.reinstall - Description: Re-runs the initial egg installation script on the container.
- Success Status:
202 Accepted - Caution: Overwrites server executable files. Back up your files before reinstalling.
Update Docker Image
- Method:
PUT - Path:
/api/client/servers/{server}/settings/docker-image - Permissions:
startup.docker-image - Request Body:
{
"docker_image": "ghcr.io/pterodactyl/yoloks:nodejs_22"
}- Success Status:
204 No Content - Restrictions: Must match one of the allowed images defined in the server's egg configuration.
5. Multi-Language Code Implementations#
5.1 Node.js (18+) / Native fetch#
// server-manager.mjs
const PANEL_URL = 'https://paid.visihost.in';
const API_KEY = process.env.VISIHOST_CLIENT_API_KEY;
const headers = {
'Authorization': `Bearer ${API_KEY}`,
'Accept': 'Application/vnd.pterodactyl.v1+json',
'Content-Type': 'application/json'
};
// 1. List accessible servers
async function listServers() {
const res = await fetch(`${PANEL_URL}/api/client`, { headers });
if (!res.ok) throw new Error(`HTTP ${res.status}: ${res.statusText}`);
const { data } = await res.json();
return data;
}
// 2. Poll live telemetry
async function getServerStats(serverId) {
const res = await fetch(`${PANEL_URL}/api/client/servers/${serverId}/resources`, { headers });
const { attributes } = await res.json();
const ramMB = (attributes.resources.memory_bytes / 1024 / 1024).toFixed(1);
return {
state: attributes.current_state,
cpu: attributes.resources.cpu_absolute,
ramMB
};
}
// 3. Send power action
async function restartServer(serverId) {
const res = await fetch(`${PANEL_URL}/api/client/servers/${serverId}/power`, {
method: 'POST',
headers,
body: JSON.stringify({ signal: 'restart' })
});
if (res.status === 204) {
console.log(`Server ${serverId} restart signal dispatched.`);
}
}
// Execution
async function main() {
const servers = await listServers();
for (const s of servers) {
const id = s.attributes.identifier;
const stats = await getServerStats(id);
console.log(`[${id}] ${s.attributes.name} | ${stats.state} | CPU: ${stats.cpu}% | RAM: ${stats.ramMB} MB`);
}
}
main().catch(console.error);5.2 Python 3 (requests)#
# server_manager.py
import os
import requests
PANEL_URL = "https://paid.visihost.in"
API_KEY = os.getenv("VISIHOST_CLIENT_API_KEY")
headers = {
"Authorization": f"Bearer {API_KEY}",
"Accept": "Application/vnd.pterodactyl.v1+json",
"Content-Type": "application/json"
}
def get_servers():
url = f"{PANEL_URL}/api/client"
r = requests.get(url, headers=headers)
r.raise_for_status()
return r.json().get("data", [])
def create_backup(server_id: str, name: str):
url = f"{PANEL_URL}/api/client/servers/{server_id}/backups"
payload = {
"name": name,
"ignored": "*.log\ntmp/*",
"is_locked": False
}
r = requests.post(url, headers=headers, json=payload)
r.raise_for_status()
print("Backup UUID:", r.json()["attributes"]["uuid"])
def send_command(server_id: str, command: str):
url = f"{PANEL_URL}/api/client/servers/{server_id}/command"
r = requests.post(url, headers=headers, json={"command": command})
if r.status_code == 204:
print("Command sent successfully.")
elif r.status_code == 502:
print("Cannot send command: server is offline.")
else:
print(f"Error {r.status_code}: {r.text}")
if __name__ == "__main__":
servers = get_servers()
for s in servers:
print(f"Server ID: {s['attributes']['identifier']} - {s['attributes']['name']}")6. API Security Checklist#
- Protect Environment Secrets: Store Client API keys inside server environment variables (e.g.,
VISIHOST_CLIENT_API_KEY). Never commit API keys or.envfiles to git repositories. - Never Call the API from Client-Side Code: Client API keys grant full power and file manipulation capabilities. Never invoke the Client API from browser JavaScript, React frontend components, or public mobile clients. Route user actions through your own authenticated backend server.
- Restrict IP Whitelists: Configure your production bot server's static IP in the Allowed IPs list when creating your API credential at
https://paid.visihost.in/account/api. - Enforce Least Privilege: Use subuser accounts with granular permission scopes (e.g., granting only
control.startandcontrol.stop) when integrating with third-party automation tools. - Always Enforce HTTPS: Plaintext HTTP connections are rejected by VisiHost infrastructure. Always connect using
https://. - Redact Tokens in Support Channels: Never paste API tokens or private server UUIDs into Discord support channels or public tickets.
