v0.1 (beta)

API

Overview

Base URL, auth model, error shape, rate limits.

TurbineX has a REST API that covers everything the UI does — submit jobs, download output, manage turbine configs, pull results into a notebook. The UI and your scripts talk to the same endpoints; you authenticate with a JWT and get per-org scoping for free.

Base URL

Production API lives at https://api.turbinex.app. The web app reaches the same hosts through a Next.js rewrite (/api/* https://api.turbinex.app/*), so if you're debugging in the browser you'll see /api/... paths, but scripts should hit the bare hostname.

Authentication

Every request (bar the handful of public endpoints below) needs a Bearer JWT in the Authorization header. Access tokens expire in 15 minutes; refresh tokens in 30 days. See Authentication for the login, refresh, and PAT (personal access token) flows.

Public endpoints

Mounted under /api/public/ — no auth needed, scoped by share token. Used by the read-only Share Space viewer:

  • GET /api/public/studies/{token} — study header + metadata
  • GET /api/public/studies/{token}/artifacts — list uploaded files
  • GET /api/public/studies/{token}/artifacts/{id}/download — download a single artifact

Tokens are 64-char URL-safe strings, generated server-side, and only valid while the study's share-link is enabled. Owners toggle / rotate / disable from the Share Space tab on each study.

curl -H "Authorization: Bearer $TURBINEX_TOKEN" \
  https://api.turbinex.app/jobs

Error shape

Every error response is JSON with a detail field:

{
  "detail": "Monthly token cap reached: 50,000 tokens. ..."
}

Common status codes:

  • 401 Unauthorized — missing / expired token.
  • 402 Payment Required — spend cap or token cap hit.
  • 403 Forbidden — request was authenticated but not permitted. Common cases: member trying to change org settings, unverified account hitting a verified-only endpoint (custom-turbine create, parametric submit, certification report), or PUT/DELETE on a demo-derived turbine row (clone the demo first).
  • 404 Not Found— also returned for private-scope leaks (e.g. a note whose owner's private visibility hides it from you); we deliberately don't distinguish “doesn't exist” from “you can't see it”.
  • 409 Conflict — duplicate resource (e.g. turbine name collision in your org).
  • 429 Too Many Requests — per-user and per-org rate limits. Response includes Retry-After.

Rate limits

Per-user rate limits depend on plan:

  • Free — 30 req/min per user, 60 req/min per org.
  • Pro — 300 req/min per user, 600 req/min per org.
  • Enterprise — 1,000 req/min per user, 2,000 req/min per org.

The server responds with X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, and Retry-After headers on 429s.

Idempotency

Job submission isn't idempotent by default — submitting the same ZIP twice queues two runs, billed as two runs. If you need dedupe, hash the ZIP client-side and skip the submit when the hash matches a recent job. A server-side idempotency key header is on the roadmap.

Pagination

List endpoints return a PaginatedResponse<T>:

{
  "items": [...],
  "total": 42,
  "page": 1,
  "page_size": 20,
  "total_pages": 3
}

Pass ?page=&page_size= query params. Max page_size varies per endpoint (50–200).

Where to go next