NebuliHost API
Manage your servers and their files from scripts, CI pipelines and other tools.
Getting started
All endpoints live under this base URL and return JSON, except file downloads.
https://hosting.thehsi.cloud/api/v1Create a key in Settings → API Keys, then check that it works:
curl https://hosting.thehsi.cloud/api/v1/me \
-H "Authorization: Bearer $NEBULI_API_KEY"Authentication
Send your key in the Authorization header of every request:
Authorization: Bearer nh_…Keys belong to your primary account. If you have linked accounts, the API works with the same servers you see in the dashboard. You can have up to 10 keys.
Permissions
Each key only gets the permissions you select for it. Give a key only what it needs. For example,
a deploy script needs files:write but not servers:write.
| Permission | Allows |
|---|---|
account:read | Read your account and plan |
servers:read | List servers and view their details |
servers:write | Create and delete servers |
files:read | List and download files |
files:write | Upload, create, rename and delete files |
Your plan's limits still apply: a key can't create more servers or upload more data than your plan allows,
and actions your plan doesn't include (for example uploading files) fail with missing_privilege.
The API only works with servers you own. Admin privileges don't apply to API keys.
Errors
Failed requests use a normal HTTP status code and a JSON body with a stable error code you can check in your code, plus a readable message:
{
"success": false,
"error": "server_not_found",
"message": "Server not found"
}| Status | Error codes | Meaning |
|---|---|---|
| 400 | invalid_json invalid_id invalid_path is_directory … | The request is malformed or a value is invalid. |
| 401 | missing_api_key invalid_api_key | No key was sent, or the key is wrong or revoked. |
| 403 | insufficient_scope missing_privilege forbidden server_limit_reached account_disabled | The key or your plan doesn't allow this, or the server isn't yours. |
| 404 | server_not_found not_found | The server, file or endpoint doesn't exist. |
| 409 | server_exists already_exists is_file | Something with that name already exists. |
| 411 | length_required | Raw uploads need a Content-Length header. |
| 413 | storage_limit_exceeded | The upload would exceed the server's storage limit. |
| 500 | io_error | Something went wrong on our side. Try again later. |
Account
Your account, plan and limits. max_servers is -1 when unlimited, and max_server_size (bytes) is null when unlimited.
{
"success": true,
"account": {
"id": 42,
"username": "jane.doe",
"email": "jane@example.com",
"provider": "github",
"plan": "premium",
"max_servers": 5,
"server_count": 2,
"max_server_size": 1073741824,
"linked_providers": ["google"]
}
}Servers
A server is one website, served at https://<id>.thehsi.cloud. Server objects look like this (size and max_size are in bytes, and max_size is null when unlimited):
{
"id": "my-site",
"url": "https://my-site.thehsi.cloud",
"status": "running",
"size": 48213,
"max_size": 1073741824,
"created_at": "2026-11-03T09:12:44+00:00"
}All your servers, as {"success": true, "servers": [ … ]}.
Creates a server with a default index.html. The id becomes the subdomain:
5 to 63 characters, lowercase letters, numbers and dashes, and it can't start or end with a dash.
Returns 201 with the new server.
curl -X POST https://hosting.thehsi.cloud/api/v1/servers \
-H "Authorization: Bearer $NEBULI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"id": "my-site"}'One server, as {"success": true, "server": { … }}.
Files
File paths are relative to the server root, for example index.html or assets/app.css.
URL-encode special characters in paths. Paths that point outside the server are rejected.
The full file tree. Folders are objects, and files map to their size in bytes.
{
"success": true,
"files": {
"assets": { "app.css": 2310, "logo.svg": 911 },
"index.html": 1204
}
}Downloads a file. If path is a folder, you get a zip of that folder.
curl -OJ https://hosting.thehsi.cloud/api/v1/servers/my-site/files/index.html \
-H "Authorization: Bearer $NEBULI_API_KEY"A zip of the whole server, useful for backups.
curl -o my-site.zip https://hosting.thehsi.cloud/api/v1/servers/my-site/archive \
-H "Authorization: Bearer $NEBULI_API_KEY"
Unpacks a zip into the server, as a multipart upload with a file field and a required
mode field. Use merge to add the zip's files and overwrite those with the same path,
or replace to delete everything else on the server (this also needs plan: manage_files).
The zip is checked and unpacked before the site changes, so a rejected zip leaves it untouched. If everything is
inside one folder that has an index.html, that folder's contents go to the server root, and the
response names it in stripped_folder. Password-protected zips and symbolic links are rejected.
curl -X PUT https://hosting.thehsi.cloud/api/v1/servers/my-site/archive \
-H "Authorization: Bearer $NEBULI_API_KEY" \
-F "file=@my-site.zip" \
-F "mode=replace"{
"success": true,
"mode": "replace",
"files": 42,
"stripped_folder": null
}
Creates or replaces a file. Missing parent folders are created. Send the file as the raw request body
(with Content-Length), or as a multipart form field named file.
The write is atomic: visitors never see a half-uploaded file.
# Raw body
curl -X PUT https://hosting.thehsi.cloud/api/v1/servers/my-site/files/index.html \
-H "Authorization: Bearer $NEBULI_API_KEY" \
-H "Content-Type: application/octet-stream" \
--data-binary @index.html
# Multipart
curl -X PUT https://hosting.thehsi.cloud/api/v1/servers/my-site/files/assets/logo.svg \
-H "Authorization: Bearer $NEBULI_API_KEY" \
-F "file=@logo.svg"Creates a folder, including any missing parent folders. Succeeds if the folder already exists.
Renames or moves a file or folder. Fails with already_exists if the destination is taken.
curl -X PATCH https://hosting.thehsi.cloud/api/v1/servers/my-site/files/old.html \
-H "Authorization: Bearer $NEBULI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"new_path": "archive/old.html"}'Deletes a file, or a folder with everything in it.
Deploy from CI
This script uploads everything in a build folder to a server. Create a key with only
files:write, and store it as the NEBULI_API_KEY secret in your CI.
#!/usr/bin/env bash
set -euo pipefail
API="https://hosting.thehsi.cloud/api/v1"
SERVER="my-site"
BUILD_DIR="dist"
cd "$BUILD_DIR"
find . -type f -print0 | while IFS= read -r -d '' file; do
path="${file#./}"
echo "Uploading $path"
curl --fail-with-body -sS -X PUT "$API/servers/$SERVER/files/$path" \
-H "Authorization: Bearer $NEBULI_API_KEY" \
-H "Content-Type: application/octet-stream" \
--data-binary "@$file" > /dev/null
done
Files that are no longer in your build stay on the server. To remove them, compare the tree from
GET /files with your build folder and delete the extras,
or zip the folder and upload it with mode=replace.