mx_live_ API key, and call the API three ways — with cURL, the Python SDK, and the JavaScript SDK.
Every ModuleX request authenticates with two pieces:
Authorization: Bearer mx_live_…— your API key, sent as a Bearer token.X-Organization-ID: <org-id>— the organization the request acts in, required on every organization-scoped endpoint.
Prerequisites
1
Sign in and pick an organization
Sign in to the ModuleX app. On first sign-in, a personal organization is provisioned for you automatically, so you always have at least one organization to act in. You can create more from the organization switcher. Every API call is scoped to exactly one organization — see Organizations, roles & membership.
2
Confirm your role
API keys are created by a signed-in user, and they inherit that user’s access. The live organization roles are
owner and admin (the member role has been retired). You do not need to be an owner to create a personal API key, but the actions a key can perform are still gated by your role and by usage limits. See Roles & permissions.3
Have a runtime ready
For the SDK examples you need Node.js 18+ (for the JavaScript SDK) or Python 3.9+ (for the async Python SDK). For the cURL examples you only need a terminal.
Step 1 — Create an API key
API keys are managed in the app, under your account’s API keys settings. Creating a key is the only time the full secret is shown — copy it immediately and store it somewhere safe. ModuleX stores only a salted hash and a short hint (for examplemx_live_2J9vK4xM********), so a lost key cannot be recovered and must be replaced.
When you create a key you can set the following fields.
string
required
A human-readable label for the key (1–255 characters), for example
CI/CD or Production worker. Shown in the key list to help you identify it later.string
default:"null"
Optional. Scopes the key to a single organization that you are a member of. When set, the key can only be used with a matching
X-Organization-ID header — any other organization returns 403. When omitted (null), the key works across all organizations you belong to, and you choose the organization per request via the header.string (ISO 8601)
default:"null"
Optional expiry timestamp. After this time the key is rejected with
401. Omit (null) for a key that never expires.integer
default:60
Optional per-key request limit (1–1000). Defaults to
60 requests per minute. A separate per-user limit of 300 requests per minute applies across all of your keys combined.string
The key’s unique identifier (UUID). Use it to fetch or revoke the key later.
string
The label you provided.
string
The full secret, for example
mx_live_2J9vK4xM8nP3wQ7tR5sL1yB6cF0dG2h4vN8pX5mK9rT3wY7u. Returned once, only on creation. Every other endpoint returns the masked form instead.string
The first 8 characters of the random part, used to recognize the key without exposing it.
string
The display form, for example
mx_live_2J9vK4xM********.string | null
The organization the key is scoped to, or
null for an unscoped key.integer
The per-key limit applied to this key.
string | null
The expiry timestamp, or
null if the key never expires.boolean
Whether the key is currently usable. Becomes
false after revocation.string
When the key was created (ISO 8601).
Step 2 — Find your organization ID
Every organization-scoped request needs theX-Organization-ID header. You can read your organizations from GET /auth/me/organizations, which lists each organization’s id, slug, name, and your role in it. The id value is what goes in the header.
GET /auth/me/organizations is not organization-scoped, so it does not itself require the X-Organization-ID header — it returns every organization you belong to.
id of the organization you want to work in. You will pass it as X-Organization-ID on every organization-scoped call.
Step 3 — Make your first authenticated call
With your key and organization ID in hand, call an organization-scoped endpoint.GET /organizations/llms returns the language-model catalog for your organization, including which models have credentials connected — a good first call that exercises both the Authorization and X-Organization-ID headers.
In the SDKs you set the organization once on the client (it is sent as X-Organization-ID automatically) and override it per call when you need a different organization.
200 response confirms your credentials work. If you get an error instead, jump to Troubleshooting below.
Install the SDKs
The SDK examples above assume you have the relevant package installed.https://api.modulex.dev and has no version prefix: do not append /api or /v1. See Base URLs, environments & versioning.
Client configuration
The two SDKs take the same options under language-idiomatic names (camelCase in JavaScript, snake_case in Python).Environment-variable fallback differs by SDK. The Python SDK reads
MODULEX_API_KEY, MODULEX_ORGANIZATION_ID, and MODULEX_BASE_URL when the matching argument is omitted, so you can construct Modulex() with no arguments if those are set. The JavaScript SDK has no environment-variable fallback — you must pass apiKey explicitly; reading process.env.MODULEX_API_KEY yourself is just your own code, not SDK behavior. Full details: JavaScript SDK and Python SDK.Response fields stay snake_case on the wire (
active_llm_total, created_at, organization_ids). The JavaScript SDK converts your request keys from camelCase to snake_case automatically, but it does not convert responses back — read response fields in snake_case in both SDKs.How authentication is evaluated
Understanding the order the backend checks credentials helps you read errors correctly.1
Credential resolution
The backend reads
Authorization: Bearer <token> first. If the token starts with mx_live_ it is treated as an API key; otherwise it is treated as a Clerk JWT (the app’s user-login path). If no Bearer credential resolves, it falls back to the X-API-KEY header (which must also start with mx_live_). With neither present, the request is rejected with 401.2
Key validation and rate limits
The key is looked up by its hash and must be active and unexpired. Each request consumes from the key’s per-minute limit (default
60) and your per-user limit (300 across all keys). Exceeding either returns 429 with X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, and Retry-After headers.3
Organization scope
For organization-scoped endpoints, the backend reads
X-Organization-ID. A missing header returns 400. If your key is scoped to a specific organization, the header must match that organization or you get 403. If you are not a member of the organization in the header, you get 403.Troubleshooting
401 — Authentication required / Invalid API key
401 — Authentication required / Invalid API key
No valid credential was found, or the key is wrong, revoked, or expired. Confirm you are sending
Authorization: Bearer mx_live_… (not X-Authorization), that the key is copied in full, and that it has not been revoked or passed its expires_at. Test the key in isolation with GET /auth/me, which needs no organization header.400 — X-Organization-ID header is required
400 — X-Organization-ID header is required
You called an organization-scoped endpoint without the
X-Organization-ID header. Add it with an organization ID from GET /auth/me/organizations. In the SDKs, set organizationId / organization_id on the client or pass it per call.403 — Not a member, wrong scope, or insufficient role
403 — Not a member, wrong scope, or insufficient role
Three causes share this status: the organization in
X-Organization-ID is not one you belong to; your key is scoped to a different organization than the header; or the endpoint requires the owner or admin role and yours is lower. Verify your membership and role with GET /auth/me/organizations.429 — Rate limited
429 — Rate limited
You exceeded the per-key (default
60/min) or per-user (300/min) limit. Read the Retry-After header and back off; both SDKs retry 429 automatically up to maxRetries / max_retries, honoring Retry-After. See Rate limiting.402 / 403 / 429 with a flat {code, layer, …} body
402 / 403 / 429 with a flat {code, layer, …} body
On run, Composer, Assistant, and managed-knowledge surfaces, a usage denial returns a flat envelope
{code, layer, key, current, limit, reason} (not wrapped in detail). The layer maps to the status: credit/wallet → 402, quota → 403, rate → 429. This is distinct from the plain {"detail": "…"} shape on standard CRUD routes. See Errors & status codes and Usage gating & limits.Next steps
Make your first API call
Run a workflow programmatically and stream its result in cURL, Python, and JavaScript.
Authentication reference
The complete header, token, and error reference for authenticating every request.
API overview
Base URLs, the request lifecycle, and how every operation is shown three ways.
SDKs overview
Install and configure the JavaScript and Python SDKs once, then use them everywhere.