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/meSome 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:
| Area | Paths | Handles |
|---|---|---|
| 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 usage | Everything else | Apps, 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 object | Meaning |
|---|---|
currency, currency_minor_digits | Requested currency and its minor-unit scale (USD: 2, KRW: 0). |
effective_from | The 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_price | Fiat price of one credit in the requested currency, as a decimal string. |
rates[].unit_price_credits, rates[].unit_price | Unit rate in credits and its exact fiat product with credit_price, both decimal strings. |
plans[].monthly_credits, plans[].usage_credit | Monthly plan credits and their fiat value, both decimal strings. These credits offset usage only and do not roll over. |
plans[].platform_fee | Directly entered monthly platform fee in fiat, as a decimal string; not converted from credits. |
plans[].seat_price | Catalog seat price converted to fiat, as a decimal string. |
plans[].included | Included usage quantities by metric. |
flat_tiers[].fee_credits, flat_tiers[].fee | Flat 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) andcursor. - Pass the response's
nextCursorunchanged ascursorin the next request. AnextCursorofnullmeans the last page. - Only deployment event lists differ: they use
after/nextAfterandlimit(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": "..."}}| Status | Usual cause |
|---|---|
401 | Missing or expired access token |
403 email_verification_required | A change was requested before the email was verified |
403 or 404 | A resource in a team you cannot access. 404 may be returned to hide whether it exists. |
429 | Too 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
| Request | What it does |
|---|---|
GET /v1/compute/options | Available regions (id, label, maxTier) and tiers (name, label, cpuMillicores, memoryMiB). Only users with a verified email can call it. |
GET /v1/apps/{appId}/compute-settings | Read the app's compute defaults. Needs app read permission. |
PUT /v1/apps/{appId}/compute-settings | Replace the app's compute defaults entirely. Needs app edit permission. |
POST /v1/apps/{appId}/deployments/{deploymentId}/redeploy | Redeploy. With applyComputeSettings: true it matches Apply now in the console. |
cpuMillicoresis in thousandths of a vCPU.1000is one vCPU; values are multiples of 1000 between 1000 and 64000.compute-settingsfields areregions,tier,minInstances,maxInstances, andport. Anullor missing field means "not set". Responses also includeupdatedAt(last change) andproductionSources(where current production's values came from); either may be null.- A deployment's compute details include the effective
portand the value sources insources. applyComputeSettings: trueredeploys production without rebuilding. Non-null app settings replace the previous deployment's values, including those fromnitroship.jsonor CLI flags, and get sourceapp. Fields not set in app settings retain the previous deployment's values. Old deployments without source records get409; 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
| Request | What it does |
|---|---|
GET /v1/apps/{appId}/crons | Read the app's effective cron jobs. Needs app read permission. |
PUT /v1/apps/{appId}/crons | Replace the entire console cron set. Needs app edit permission and a verified email. |
Both requests return crons, source, and configDeclared:
cronslists the effective jobs in declared order. Each entry includespath,schedule,nextRunAt, andlastRun.sourceis"config"(production's deployment configuration),"console"(saved console/API settings), or"none"(neither is set).configDeclaredistruewhen the current ready production deployment declarescrons, including an empty array. It remainstrueafter a console/API edit: the next production deployment that declarescronswill 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.