Skip to content

Public API

The Public API lets you manage projects, web services and deployments programmatically — from CI pipelines, CLIs or any HTTP client. It is the only externally supported HTTP surface of the platform.

The base URL is https://api.runsite.app.

Every request must include an API key in the Authorization header:

Terminal window
Authorization: Bearer ak_live_a1b2c3d4...

Create and manage keys from the dashboard — see API Keys. Requests without a valid, active key get 401 Unauthorized.

GET /api/v1/users/me/api-key

Returns the key’s id, name, key_prefix, scope and expiry — never the key itself. Use it to discover what a key is allowed to do before calling a write endpoint.


GET /api/v1/projects

Returns all projects owned by the authenticated user.

POST /api/v1/projects
Content-Type: application/json
{
"name": "my-app",
"description": "My application"
}
GET /api/v1/projects/{project_id}

GET /api/v1/web-services
POST /api/v1/web-services
Content-Type: application/json
{
"name": "backend-api",
"project_id": "uuid",
"git_repository_url": "https://github.com/user/repo",
"port": 3000,
"build_command": "npm run build",
"min_instances": 1,
"max_instances": 3
}
GET /api/v1/web-services/{web_service_id}
POST /api/v1/web-services/{web_service_id}/deploy
POST /api/v1/web-services/{web_service_id}/start
POST /api/v1/web-services/{web_service_id}/restart
POST /api/v1/web-services/{web_service_id}/stop

deploy and start are the same action — they start the existing image and do not rebuild. To build and release a new commit, create a deployment:

POST /api/v1/web-services/{web_service_id}/deployments

which returns the new deployment, including the id to poll with GET /api/v1/web-services/{web_service_id}/deployments/{deployment_id}.

restart re-rolls existing instances without rebuilding. stop releases compute (env vars and disks are preserved).

These four actions return the service’s lifecycle status — starting, stopping, restarting — and refuse a service blocked by the content policy or suspended for bandwidth.

GET /api/v1/web-services/{web_service_id}/logs?tail=100&since=1710000000
ParameterTypeDefaultDescription
tailint100Number of log lines (1–10000)
sinceintUnix timestamp to filter logs from
GET /api/v1/web-services/{web_service_id}/metrics

Returns CPU (millicores and percent of limit), memory (bytes and percent of limit) and the running instance count. Responds 404 when the service is not running and has no metrics to report.

GET /api/v1/web-services/{web_service_id}/metrics/history?period=3600
ParameterTypeDefaultDescription
periodint3600Window in seconds, from 60 to 604800 (7 days)

Per-service request rate and latency are not collected and are not available through this API.


GET /api/v1/web-services/{web_service_id}/deployments?limit=10&offset=0
ParameterTypeDefaultDescription
limitint10Results per page (1–100)
offsetint0Pagination offset
POST /api/v1/web-services/{web_service_id}/deployments
Content-Type: application/json
{
"branch": "main"
}

The branch field is optional — defaults to the service’s configured branch.

GET /api/v1/web-services/{web_service_id}/deployments/{deployment_id}
POST /api/v1/web-services/{web_service_id}/deployments/{deployment_id}/rollback

GET /api/v1/web-services/{web_service_id}/env

Values of secret variables are masked in the response.

POST /api/v1/web-services/{web_service_id}/env
Content-Type: application/json
{
"key": "DATABASE_URL",
"value": "postgres://...",
"is_secret": true
}
PUT /api/v1/web-services/{web_service_id}/env/bulk
Content-Type: application/json
{
"variables": [
{"key": "NODE_ENV", "value": "production"},
{"key": "PORT", "value": "3000", "is_secret": false}
]
}
DELETE /api/v1/web-services/{web_service_id}/env/{var_id}

Project secrets are scoped per environment (production, staging, development) and can be reused across services in the project.

GET /api/v1/projects/{project_id}/secrets

Returns secrets grouped by key.

POST /api/v1/projects/{project_id}/secrets
Content-Type: application/json
{
"key": "STRIPE_SECRET_KEY",
"value": "sk_live_...",
"scope": "production",
"is_secret": true
}
ScopeDescription
productionUsed in production deployments
stagingUsed in staging environments
developmentUsed in development environments
PUT /api/v1/projects/{project_id}/secrets/bulk
Content-Type: application/json
{
"secrets": [
{"key": "API_SECRET", "value": "value1", "scope": "production"},
{"key": "API_SECRET", "value": "value2", "scope": "staging"}
]
}
DELETE /api/v1/projects/{project_id}/secrets/{secret_id}

Read-only. Connection URLs and credentials are never returned.

GET /api/v1/databases?limit=50&offset=0

Returns Postgres and Valkey instances of the active workspace in one list, each carrying kind, status, plan name, configured limits and hostnames.

GET /api/v1/databases/{database_id}

Same fields plus current CPU and memory usage when the instance is running. Usage is collected for Postgres only; for Valkey those fields are null.

Both endpoints are scoped to the caller’s active workspace.


All errors follow a consistent format:

{
"detail": "Error message describing what went wrong"
}
StatusDescription
400Bad request — invalid input
401Unauthorized — invalid or missing credentials
403Forbidden — insufficient scope
404Resource not found
429Rate limit exceeded

A 403 raised by the scope check carries a structured detail rather than a string, so a client can tell the caller which scope the key is missing:

{
"detail": {
"code": "insufficient_scope",
"required": "write",
"actual": "read"
}
}

429 means the public API rate limit (30 requests per minute) was exceeded. The budget is per API key, so two keys never share one bucket.


Browse and call the API live in the API Explorer.

IP geolocation by DB-IP