Skip to content

Public API

The Public API lets you manage projects, web services, deployments and managed databases 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)
sinceintnoneUnix 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 and 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}

GET /api/v1/plans?service_type=postgresql
ParameterTypeDescription
service_typestringRequired. web_service, postgresql or redis

Returns the active plans for that service type with id, slug, monthly price in cents and resource limits. Pass a plan’s id as plan_id when creating a database.


Connection URLs and credentials are never returned by any of these endpoints. To give a web service access to a database, create a connection: the URL is injected into the service as a secret environment variable on the server side.

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.

Every database endpoint is scoped to the caller’s active workspace.

Needs a write key. Billed from the account balance.

POST /api/v1/databases
Content-Type: application/json
{
"name": "main-db",
"plan_id": "uuid",
"project_id": "uuid",
"postgres_version": "16"
}
FieldRequiredDescription
nameyesDisplay name, 1–100 characters
plan_idyesA postgresql plan from List plans
project_idnoNeeded to connect the database to a service of that project
postgres_versionno14, 15, 16 (default) or 17
storage_gbnoExtra GB on top of the plan’s storage, 0–1000, billed per GB

The database name, user and password are generated on the server. The response is the new database with status creating; provisioning takes a minute or two, so poll Get a database until it is running.

Needs a write key. Billed from the account balance.

POST /api/v1/redis
Content-Type: application/json
{
"name": "cache",
"plan_id": "uuid",
"project_id": "uuid"
}

Takes the same fields as the Postgres endpoint, except that the version field is redis_version (7.2 by default, or 8.0). The ACL user and password are generated on the server.

Both create endpoints allow 5 requests per minute per key.

Needs a write key.

POST /api/v1/databases/{database_id}/start
POST /api/v1/databases/{database_id}/stop

Works for both engines; the id decides which one. Stopping keeps the data and the plan, but services connected to the database lose their connection until it is started again. The response carries the database id, kind and new status.

Needs an admin key.

DELETE /api/v1/databases/{database_id}

Deletes a Postgres or Valkey instance together with all of its data, and returns 204. There is no undo, so back up anything you need first.

Needs a write key.

POST /api/v1/web-services/{web_service_id}/connections
Content-Type: application/json
{
"target_type": "database",
"target_id": "uuid"
}

target_type is database for Postgres and redis for Valkey. The service and the database must belong to the same project. Runsite sets DATABASE_URL or REDIS_URL on the service as a secret environment variable; the response names the variable in env_var_key but never includes its value. Deploy or restart the service so the running process picks it up.


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