Create Hosted API

Beta
Create a hosted API. The slug is a DNS label, globally unique on the shared domain (409 `hosted_api_slug_taken`) and immutable afterwards. `auth_mode` names the audience, narrowest first: `owner`, `workspace` (the platform's own credentials), `user_token` (a JWT your backend signs per user), `consumer_key` (a `ck_` key you mint) or `public` (anyone, reads only). A workspace can refuse `public` as policy (403 `hosted_api_public_refused`). Reads, runs and writes are each bounded per UTC day (`daily_read_cap`, `daily_run_cap`, `daily_write_cap`), and an API holds only a share of a server's requests open at once, so a slow upstream behind one API cannot take the capacity others need: past it, a request answers 429 `hosted_api_busy` with `Retry-After`. `mcp_enabled: true` also serves the API's routes as an MCP server at `POST <base_url>/mcp`, so an MCP client (Claude Code, Cursor) attaches to one address and gets them as tools, under the same audience, keys and caps. It is refused with `auth_mode: public`. On that face the API's `name` is the server's title and its `description` is the instructions the client's model reads, so describe what the tools are for. Dark launch: requires the `hosted_apis_access` entitlement (402 `hosted_apis_not_in_plan` otherwise).

Authentication

AuthorizationBearer

Enter your API key with the Bearer prefix, e.g. ‘Bearer sk_…’.

Headers

Speechify-VersionstringOptional
Idempotency-KeystringOptional<=255 characters
A client-generated key (an opaque string, max 255 chars) that makes a side-effect POST safe to retry: the server runs the operation exactly once and replays the first response (its status and body) for 24 hours. Reusing a key with a different request body, or while the first request is still in flight, returns `409 idempotency_conflict`. A replayed response carries the `Idempotent-Replayed: true` header.

Request

This endpoint expects an object.
slugstringRequiredformat: "^[a-z0-9](?:[a-z0-9-]{1,38}[a-z0-9])?$"

3-40 lowercase letters, digits or hyphens; a DNS label, unique on the shared domain; immutable.

namestringRequired
descriptionstringOptional<=1000 characters

What the API is for; also the instructions an MCP client hands its model when mcp_enabled is on.

auth_modeenumOptional

consumer_key when omitted. public is refused with 403 hosted_api_public_refused where the workspace’s policy does not allow internet-facing APIs.

cors_originslist of stringsOptional
daily_run_capintegerOptional1-100000

Runs the API may start per UTC day through its run routes; 1000 when omitted.

daily_read_capintegerOptional1-10000000

Reads the API’s store, file, run_latest and tool routes may serve per UTC day; 100000 when omitted.

daily_write_capintegerOptional1-1000000

Documents the API’s write routes may land per UTC day; 10000 when omitted.

mcp_enabledbooleanOptional

Serve the routes as an MCP server at POST <base_url>/mcp too; false when omitted. Refused with auth_mode: public (400 validation_failed naming mcp_enabled).

project_idstringOptionalformat: "^proj_[0-9a-hjkmnp-tv-z]{26}$"
user_token_jwks_urlstringOptional<=500 characters

Register the key set end-user tokens are verified against (an https URL on a public host).

Response headers

Speechify-Request-IdstringOptional
Unique identifier for this request, present on every response (2xx and non-2xx alike). If the caller sends a `Speechify-Request-Id` request header the server echoes it back (sanitized and length-capped) so one logical request can be traced end-to-end; otherwise the server generates a fresh value. Log it on every response and quote it in support requests - it is the stable handle that ties your observation to Speechify's server-side logs, and it matches the `request_id` field in the error envelope. The legacy alias `X-Request-ID` carries the same value and is still accepted on requests, until 2027-07-24. Prefer the un-prefixed name (RFC 6648).

Response

The created API.
idstringformat: "^api_[0-9a-hjkmnp-tv-z]{26}$"
slugstring
hostnamestring

The public host, <slug>.<hosted-api domain>; empty where no domain is configured.

base_urlstring
namestring

The API’s display name; the server title an MCP client shows when mcp_enabled is on.

descriptionstring

What the API is for. When mcp_enabled is on it is also the instructions an MCP client hands its model, so say what the tools are for and when to use them.

auth_modeenum

Who the API answers, narrowest first. owner: only the API’s owner, with their own Speechify API key or console session. workspace: any member of the owning workspace, the same way. user_token: every request presents a short-lived JWT your backend signed for the calling user (sub, exp within 24 hours, optional aud naming this API), verified against the API’s signing secret (HS256) or its registered JWKS URL (RS256 / ES256 / EdDSA). consumer_key: every request presents a ck_ bearer minted for this API. public: no credential; only read resolvers may be served, the per-IP limiter is the only bound, and a workspace can refuse it as policy (hosted_apis_public_allowed). The three modes that name a person (owner, workspace, user_token) let routes bind {{user.sub}}, key the response cache per person, and stamp a written document as that person’s.

cors_originslist of strings

Browser origins allowed to call the API (* for any). Empty for server-to-server only.

enabledboolean
daily_run_capinteger1-100000

Runs the API’s run routes may start per UTC day; the spend ceiling behind a leaked consumer key.

project_idstring or nullformat: "^proj_[0-9a-hjkmnp-tv-z]{26}$"
user_token_jwks_urlstring or null

The key set end-user tokens are verified against when set (an https URL on a public host, read on demand and cached briefly). When null, tokens are verified against the API’s signing secret.

user_token_secret_hintstring or null

The masked signing secret end-user tokens are verified against (the rotate-user-token-secret verb mints it); null until minted.

created_atdatetime
updated_atdatetime
daily_read_capintegerOptional1-10000000

Reads the API’s store, file, run_latest and tool routes may serve per UTC day; the ceiling behind a hot path, and for a tool route behind the vendor budget its calls draw on. Always present on a current API; optional on the wire so a reader built before it existed keeps parsing. A response served from the cache is not a read. Past the cap a read route answers 429 route_read_limit_reached. Without Redis nothing counts: a public route is paused by the limiter in that state, a keyed or tokened caller passes.

daily_write_capintegerOptional1-1000000

Documents the API’s write routes may land per UTC day; the storage ceiling behind a leaked key on a write route. Past the cap a write route answers 429 route_write_limit_reached. Optional on the wire for the same reason as daily_read_cap.

mcp_enabledbooleanOptional

Whether the API also serves its routes as an MCP server at POST <base_url>/mcp (streamable HTTP, stateless, POST only): every enabled route except a file route is listed as a tool, named by the route’s name and described by its description, and a call runs under the same audience, consumer keys, per-key limits, daily caps and request log the route has. An MCP client authenticates with the same bearer a plain consumer sends (a ck_ consumer key, an end-user token, or a Speechify API key on a workspace or owner API); the API’s OpenAPI document names the address under x-speechify-mcp.url. Off for a new API, where /mcp answers 404 hosted_mcp_not_enabled; never true on a public API (refused with 400 on either switch). Optional on the wire for the same reason as daily_read_cap.

mcp_urlstring or nullOptionalRead-only

The address an MCP client attaches to, <base_url>/mcp, while mcp_enabled is on; null while it is off. Read it here rather than assembling it.

Errors

400
Bad Request Error
401
Unauthorized Error
402
Payment Required Error
404
Not Found Error
409
Conflict Error
429
Too Many Requests Error
500
Internal Server Error