Public API

Public API overview

The REST API's address, authentication, path layout, and the rules every endpoint shares.

Beta API

The /v1 API is in Beta. It may change without compatibility aliases for old paths; when it does, update your calling code and any saved configuration together.

Everything the CLI does is also available through the REST API. Use it to build your own automation or connect other systems.

Your first request

The API lives at https://api.nitroship.co, and requests need an access token. Get one with ntro token; an ntro_ API token sent as-is is rejected. API authentication has the details.

export NITROSHIP_ACCESS_TOKEN="$(ntro token)"
curl -H "Authorization: Bearer $NITROSHIP_ACCESS_TOKEN" \
  https://api.nitroship.co/v1/me

Some endpoints, such as pricing lookups, work without logging in.

Path layout

Every request goes to the same address (https://api.nitroship.co). Paths are grouped by area:

AreaPathsHandles
Teams and tokens/v1/identity/*Teams, members, invitations, default team, API tokens (/v1/identity/tokens)
Payments/v1/payments/*Team billing overview (/v1/payments/teams/{teamId}), billing profile, checkout and payment methods, plan changes, invoices
Apps and usageEverything elseApps, deployments, domains, usage, pricing (/v1/pricing, /v1/billing/currencies, /v1/billing/plans, /v1/billing/metrics, /v1/teams/{teamId}/usage..., /v1/teams/{teamId}/billing/spend, /v1/teams/{teamId}/billing/cdn)

Request and response shapes for apps and usage paths are in the API reference.

Public pricing

GET /v1/pricing?currency=KRW needs no authentication. 1 credit = 1 USD. A single date-versioned, currency-neutral pricing catalog defines usage rates, included quantities, monthly plan credits, seat prices, and Flat Rate CDN tier fees. A separate date-versioned currency price defines the operator's fixed fiat price of one credit and directly entered monthly platform fees. Prices do not follow live exchange rates.

The response intentionally retains the outer price_book JSON key. It contains resolved prices in the requested currency and credit-denominated fields; the key does not represent a separate per-currency catalog.

Field inside the response objectMeaning
currency, currency_minor_digitsRequested currency and its minor-unit scale (USD: 2, KRW: 0).
effective_fromThe later of the selected catalog's and currency price's effective dates, in RFC3339. Each is the latest version effective at the lookup time.
credit_priceFiat price of one credit in the requested currency, as a decimal string.
rates[].unit_price_credits, rates[].unit_priceUnit rate in credits and its exact fiat product with credit_price, both decimal strings.
plans[].monthly_credits, plans[].usage_creditMonthly plan credits and their fiat value, both decimal strings. These credits offset usage only and do not roll over.
plans[].platform_feeDirectly entered monthly platform fee in fiat, as a decimal string; not converted from credits.
plans[].seat_priceCatalog seat price converted to fiat, as a decimal string.
plans[].includedIncluded usage quantities by metric.
flat_tiers[].fee_credits, flat_tiers[].feeFlat Rate CDN tier fee in credits and its fiat value, both decimal strings.

Pricing decimals use plain decimal strings, without exponents and with trailing fractional zeros trimmed. 400 means an invalid currency; 404 means there is no effective catalog or currency price. A currency is available for billing only when it is registered and enabled and both effective versions exist. Billing periods use the versions effective at the period start. Statement lines, invoice amounts, and budget_minor stay in fiat.

Operator pricing management uses GET/POST /v1/operator/pricing-catalogs and GET/POST /v1/operator/currency-prices (or the corresponding /admin paths with an admin token). The former operator pricing routes are removed; there are no compatibility aliases. A team usage response that cannot resolve pricing uses the reason no_effective_pricing.

Operator-granted team credits are currency-neutral and reduce invoice totals across currencies, oldest grant first (FIFO). Billing's operator grant API accepts amountCredits, not a fiat minor-unit amount or currency; balances use the decimal-string creditBalance, and history uses amountCredits and remainingCredits. These are separate from monthly plan credits. See Usage and billing.

Payment receipts are emailed to the team's billing email when a positive-total invoice becomes paid. They are not a product webhook event. See Payment receipt email.

Common rules

IDs

  • For apps, teams, users, deployments, domains, and so on, use the public ID the API returned, unchanged. Malformed IDs get 400 invalid_id.
  • A deployment's shortId (the short ID in revision URLs) is a separate value used by the CLI. {deploymentId} in a path must be the public ID.

Pagination

  • Lists of apps, deployments, domains, purges, and activity use limit (1–100, default 50) and cursor.
  • Pass the response's nextCursor unchanged as cursor in the next request. A nextCursor of null means the last page.
  • Only deployment event lists differ: they use after/nextAfter and limit (1–1000, default 100).
  • Team, token, and invoice lists do not follow these rules.

Request bodies

  • Send JSON with Content-Type: application/json.
  • Unknown fields are rejected, so watch for typos.
  • Bodies are limited to 64 KiB in general; some endpoints have their own limits.

Errors

Error responses look like this:

{"error": {"code": "email_verification_required", "message": "..."}}
StatusUsual cause
401Missing or expired access token
403 email_verification_requiredA change was requested before the email was verified
403 or 404A resource in a team you cannot access. 404 may be returned to hide whether it exists.
429Too many requests

Rate limits

The apps and usage API limits requests per IP and returns 429 when you exceed it. If the response has X-Retry-After, wait that many seconds before retrying. Signup and login have separate limits.

Request IDs

When reporting a problem, include the response's X-Request-Id. Run ntro --verbose to see the IDs of requests the CLI sends.

App domain lists

GET /v1/apps/{appId}/domains returns only the app's production address and custom domains by default, leaving out per-deployment revision and branch URLs. Add includeRevisions=true to include them. Results are sorted by registration (oldest first), so the ntro.run address created with the app comes first.

Compute settings API

RequestWhat it does
GET /v1/compute/optionsAvailable regions (id, label, maxTier) and tiers (name, label, cpuMillicores, memoryMiB). Only users with a verified email can call it.
GET /v1/apps/{appId}/compute-settingsRead the app's compute defaults. Needs app read permission.
PUT /v1/apps/{appId}/compute-settingsReplace the app's compute defaults entirely. Needs app edit permission.
POST /v1/apps/{appId}/deployments/{deploymentId}/redeployRedeploy. With applyComputeSettings: true it matches Apply now in the console.
  • cpuMillicores is in thousandths of a vCPU. 1000 is one vCPU; values are multiples of 1000 between 1000 and 64000.
  • compute-settings fields are regions, tier, minInstances, maxInstances, and port. A null or missing field means "not set". Responses also include updatedAt (last change) and productionSources (where current production's values came from); either may be null.
  • A deployment's compute details include the effective port and the value sources in sources.
  • applyComputeSettings: true redeploys production without rebuilding. Non-null app settings replace the previous deployment's values, including those from nitroship.json or CLI flags, and get source app. Fields not set in app settings retain the previous deployment's values. Old deployments without source records get 409; rebuild them first. See Managing deployments.

You can always edit compute settings through the console or API, even when production uses values from nitroship.json or CLI flags. Saving does not change the running deployment; use Apply now (or applyComputeSettings: true) to apply the console/API values. On every normal deploy, nitroship.json again overrides console/API settings for the fields it declares (regions, tier, minInstances, maxInstances, and port); supported CLI flags still take precedence over the file.

Cron jobs API

RequestWhat it does
GET /v1/apps/{appId}/cronsRead the app's effective cron jobs. Needs app read permission.
PUT /v1/apps/{appId}/cronsReplace the entire console cron set. Needs app edit permission and a verified email.

Both requests return crons, source, and configDeclared:

  • crons lists the effective jobs in declared order. Each entry includes path, schedule, nextRunAt, and lastRun.
  • source is "config" (production's deployment configuration), "console" (saved console/API settings), or "none" (neither is set).
  • configDeclared is true when the current ready production deployment declares crons, including an empty array. It remains true after a console/API edit: the next production deployment that declares crons will replace that edit.

Send {"crons": [{"path": "/api/cleanup", "schedule": "0 5 * * *"}]} to replace all console jobs, or {"crons": []} to remove all of them. The change takes effect immediately, without a deploy, and unchanged path/schedule pairs keep their run state. Jobs only run while a ready production deployment exists.

curl -X PUT \
  -H "Authorization: Bearer $NITROSHIP_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"crons":[{"path":"/api/cleanup","schedule":"0 5 * * *"}]}' \
  "https://api.nitroship.co/v1/apps/$NITROSHIP_APP_ID/crons"

Validation matches deployment configuration: at most 100 jobs; each path must start with /, be at most 512 bytes, and contain no fragment (#), whitespace, or control characters. Query strings are allowed. Schedules use the supported five-field UTC format, and duplicate path/schedule pairs are rejected. See Writing a schedule.

Invalid jobs return 400 invalid_crons; an app that does not exist or cannot be managed returns 404 not_found.

Console/API edits are always allowed, even if nitroship.json declares crons. They override the current production configuration until the next production deployment that declares crons replaces them, and that includes a redeploy, promotion, or rollback. An explicit "crons": [] declares no jobs and also overrides the console set. Deployments that do not declare crons fall back to the saved console set; remove the crons key from nitroship.json and deploy to hand control back to the console. The same rules apply to vercel.json cron declarations.

On this page