InferOptimal / API

Deploy a model. Use it. Stop when you're done.

The API and MCP launch dedicated endpoints on InferOptimal GPUs. Choose a prepared recipe, a supported Hugging Face checkpoint, or your own serving container. One lifecycle handles quoting, launch, connection details and Stop.

Dedicated-service data handling explains gateway and Z.ai startup processing, operational retention and credentials. The separate Models API has its own data policy.

Open the console

Create a full-access platform key (vbc_…) in API keys. Use it on your server to manage sessions. The separate inference key returned for each session calls the model. No MCP or cloud account connection is required.

Choose what to run

Prepared recipe: select an offering from GET /console/api/optimized and quote its ID. It specifies the model, runtime and hardware. The GLM example below includes inference during GPU startup.

Hugging Face checkpoint: quote its repository ID or URL. The planner checks model support and access, then recommends available hardware for the requested workload. You can name hardware explicitly. A quote checks compatibility; it does not promise a particular throughput or instantaneous startup.

POST https://www.inferoptimal.com/console/api/deployments/plan
Authorization: Bearer $INFEROPTIMAL_API_KEY
Content-Type: application/json

{
  "model": "Qwen/Qwen3-Coder-30B-A3B-Instruct",
  "workload": "agents",
  "target_concurrency": 32,
  "max_runtime_hours": 2
}

Read the returned hardware, price, automation and blockers. Choose an automatic launch, then pass plan_id to the helper as planId, or use the HTTP steps below. Operator-assisted quotes require coordination through the console; the helper will not silently select one. For private or gated checkpoints, supply hf_token in both the quote and raw HTTP create request. Adapter-only repos and unfamiliar architectures may need a prepared runtime instead of the standard model engine.

Bring a serving container

Quote container with image, port and health_path, plus explicit hardware. The image must start an HTTP server; its own request and response formats remain unchanged. Supported VM hardware and a paid balance are required. There is no automatic sizing or code optimization for an arbitrary image.

{
  "container": {
    "image": "your-registry/your-serving-image:version",
    "port": 8000,
    "health_path": "/health"
  },
  "hardware": "H100",
  "max_runtime_hours": 2
}

Use raw HTTP create for private registry credentials. The connection returns the container's HTTPS address; do not assume it implements Chat Completions.

REST and MCP currently launch on InferOptimal cloud. To use your own cloud account, launch from the console. Model discovery is broader than runtime support: the quote returns requirements or blockers before launch.

One dedicated key for your application

Each optimized endpoint can have one dedicated key (vbs_…). Embed it in your application: the same key starts the endpoint, runs inference and stops it, and the URL and key stay the same every time. For example, your application calls Start when a user signs in, sends requests while they work and calls Stop when they are done. The dedicated key cannot change limits, price or keys, delete the endpoint or launch anything else. Dedicated keys are for optimized endpoints; for a custom model, contact us.

1. Get the key once

Create it from an optimized endpoint on the Endpoints page, or with your platform key. GET /console/api/services returns capabilities.canCreate. Choose startup behavior before quoting: for private_only quote rapid_coldboot: false; for qualified_bridge quote rapid_coldboot: true and approve the returned startup provider and capabilities.

POST /console/api/deployments/plan
Authorization: Bearer $INFEROPTIMAL_API_KEY
Content-Type: application/json

{
  "managed_endpoint": "OFFERING_FROM_CATALOG",
  "rapid_coldboot": false,
  "max_runtime_hours": 2,
  "max_spend_usd": 21.60
}

Review the price, startup behavior and blockers, then create the dedicated key with that plan and the same selector. Startup and spending options belong in policy, not selector.

POST /console/api/services
Authorization: Bearer $INFEROPTIMAL_API_KEY
Content-Type: application/json

{
  "name": "My application",
  "plan_id": "plan_FROM_QUOTE",
  "selector": {"managed_endpoint": "OFFERING_FROM_CATALOG"},
  "idempotency_key": "persisted-service-creation-id",
  "policy": {
    "startup": "private_only",
    "maxHourlyUsd": 10.80,
    "maxSpendUsd": 21.60,
    "totalBudgetUsd": 100,
    "maxRuntimeHours": 2,
    "maxConcurrentRequests": 4,
    "idleSeconds": null
  }
}

Use your quoted selector, price and supported concurrency; these example amounts are not an offer. Save the returned service_url, base_url, model and api_key in your application's secrets. The key is returned at creation and on identical creation retries for ten minutes; it is not included in listing or inspection. Creation starts no hardware. An endpoint is never stopped for idling by default: omit idleSeconds or send null, and its owner gets one warning email after 30 idle minutes. To opt in to stopping on idle, set a whole number of seconds from 120. Idle time is billed at the hourly rate, and active requests reset the idle clock.

2. Start

Read the endpoint's state with its dedicated key, then start it with the generation and admission.epoch you read.

curl "https://www.inferoptimal.com/services/svc_YOUR_ENDPOINT" \
  -H "Authorization: Bearer $INFEROPTIMAL_DEDICATED_KEY"

It returns state (stopped, starting, ready, stopping or attention_required), generation, admission.epoch, accepts_requests, idle_seconds, idle (for example "summary": "Idle for 41 minutes at $2.00/h", or null while serving or stopped) and the URLs.

curl -X POST "https://www.inferoptimal.com/services/svc_YOUR_ENDPOINT/start" \
  -H "Authorization: Bearer $INFEROPTIMAL_DEDICATED_KEY" \
  -H "Content-Type: application/json" \
  -d '{"idempotency_key":"user-login-7f3a9c","expected_generation":3,"expected_epoch":2,"expires_at":1792420000000}'

expires_at is Unix milliseconds, at most five minutes ahead. Save the whole body before sending and retry it unchanged after a lost response. Start is idempotent and deduplicated: a retried body and simultaneous Starts share one allocation. A Start prepared before a Stop returns 409 service_start_stale; read the state again. Right after a Stop, Start can return 409 service_cleanup_pending with Retry-After while the previous hardware is released. Start keeps the endpoint's hourly ceiling, per-start cap, total authorization, daily start limit and your balance hold, and returns 402 or 429 when one is reached. When state is attention_required, Start with the key returns the reason; fix it, then press Start in the console. Private prewarm is complete when an unstopped replica has phase: "ready" and inference_backend: "private"; state: "ready" alone can mean startup-bridge availability.

3. Send requests

curl "https://www.inferoptimal.com/services/svc_YOUR_ENDPOINT/v1/chat/completions" \
  -H "Authorization: Bearer $INFEROPTIMAL_DEDICATED_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"svc_YOUR_ENDPOINT","messages":[{"role":"user","content":"Hello"}]}'

Any OpenAI-compatible client works with base_url and the endpoint ID as model.

Supported api_modes identify /chat/completions for text/vision-language or /embeddings for synchronous embedding calls. This release uses one private replica; embedding services admit one active request, which may contain a batch. Asynchronous media jobs, private repository credentials and custom containers use the explicit deployment API.

Keep the inference HTTP connection open: validated requests wait in order for startup or an available concurrency slot. Waiting and inference share policy.requestTimeoutSeconds (default 1800, maximum 3600); X-InferOptimal-Request-Timeout-Ms can set a shorter total deadline. There are 256 waiting places by default, increasing to four times the concurrency limit with a ceiling of 1024. A full waiting queue returns 429 service_queue_full with Retry-After: 5; expiration before dispatch returns 504 service_request_timeout. Input validation also has a bounded memory allowance: large concurrent bodies may wait unread before joining the service queue. Bodies are limited to 8 MiB; validation overload returns 429 service_payload_capacity. FIFO order starts after validation; the oldest requests can fill available slots in parallel. The queue stores metadata only: a disconnected HTTP request is not a saved job. Your client or network may have an earlier timeout. No inference is automatically repeated after execution may have begun. If an approved startup provider explicitly refuses a request before execution, the same connection can keep waiting for its private GPU within the original deadline.

A dedicated key does not remove cold-start latency. Choose qualified_bridge at creation only after approving a quote that offers startup inference and its provider/data handling; no fallback model is selected silently. The serviceChat helper in inferoptimal.mjs also understands explicit retry-safe 503 service_starting responses, without retrying ambiguous inference failures.

4. Stop

curl -X POST "https://www.inferoptimal.com/services/svc_YOUR_ENDPOINT/stop" \
  -H "Authorization: Bearer $INFEROPTIMAL_DEDICATED_KEY"

Stop cancels waiting requests, lets active ones finish, then releases the hardware and ends billing. The URL and key stay the same, and requests return 410 until the next Start. An endpoint is never stopped for idling unless you set idleSeconds: after 30 idle minutes its owner gets one warning email with the hourly cost and a Stop link, and using it again re-arms the warning. Stopping an endpoint that is already stopped changes nothing.

Manage dedicated keys from your account

With your platform key: GET /console/api/services lists them and GET /console/api/services/{id} inspects one, including billing and limits. POST /console/api/services/{id}/start takes the same body as the key's Start and also clears an attention_required fault after you fix its cause. POST /console/api/services/{id}/stop with {} stops it. DELETE /console/api/services/{id} removes the endpoint and its key; active requests finish and the hardware is released. POST /console/api/services/{id}/rotate-key returns a replacement key and revokes the previous one; do not retry it automatically after a lost response. POST /console/api/services/{id}/budget with {"totalBudgetUsd": 200} changes the total authorization. The earlier /sleep and /disable routes are aliases of /stop, and /enable lets requests start it again without starting hardware.

Deployment lists mark private service allocations with service_id. Their /connection returns the parent’s stable service URL and model; reuse its saved service key. Child key issuance and rotation are refused with service_managed_key and a link to the parent. Use the dedicated-key endpoint’s Start, Stop, Delete and key replacement actions.

Retry a lost creation response with the same body and idempotency key. A failed create may include service_id for inspection; never assume a transport error proves nothing was created. Reusing the idempotency key of a deleted endpoint returns 409 service_deleted. MCP exposes these actions through manage_inference_service, using OAuth instead of a platform key.

Node.js example: private GLM with immediate startup

Launch a private GLM endpoint, use its OpenAI-compatible API, and stop it when your job finishes. With immediate startup enabled, Z.ai processes your requests while the private GPUs load. New requests then move to your private replica using the same URL and key. Set rapidColdboot: false to wait for private hardware instead; API-provider precision and tool support can differ during startup.

Pricing: the quote gives the hourly rate and credit hold before launch. The prepared 2× B200 GLM offering is $19.95/hour; the 4× B200 profile is $30/hour. Both are billed by the minute while active, including startup inference and idle time. Stop ends customer billing and releases the private hardware; provider cleanup may finish asynchronously.

Requires Node 20+ and the OpenAI SDK. The helper launches the endpoint and retrieves its connection and inference key. Your application does not need a readiness loop. Requiring immediate startup fails before rental if startup capacity is unavailable.

curl -fsSL https://www.inferoptimal.com/clients/inferoptimal.mjs -o inferoptimal.mjs
import OpenAI from 'openai';
import { provisionAndWait } from './inferoptimal.mjs';

const endpoint = await provisionAndWait({
  apiKey: process.env.INFEROPTIMAL_API_KEY,
  model: 'glm-5.3-flash-nvfp4-cached-tp2',
  rapidColdboot: true, // require inference during private GPU startup
  idempotencyKey: crypto.randomUUID(), // save this for retries of this job
  maxRuntimeHours: 12,
});
const client = new OpenAI({ baseURL: endpoint.baseURL, apiKey: endpoint.apiKey });
try {
  const response = await client.chat.completions.create({
    model: endpoint.model,
    messages: [{ role: 'user', content: 'Hello' }],
  });
  // Run the rest of your job using the same client.
} finally {
  await endpoint.stop(); // confirms Stop admission; hardware cleanup continues separately
}
Retries, idle time and cleanup

stop() returns when Stop is accepted, with the API's current billing and cleanup state. To wait for hardware release, use await endpoint.stop({ waitForCleanup: true, timeoutMs: 240000 }). The optional wait defaults to four minutes and accepts a limit of one second to fifteen minutes. If cleanup is not confirmed within that limit, cleanup_unconfirmed includes accepted: true and the last deployment state. Stop remains accepted; retry it using the same endpoint or inspect the deployment. API denials and unknown Stop outcomes still fail.

Use a new idempotency key for every new session. Reuse the saved key only to retry the same creation request. Reusing an old job's key returns its original deployment, even if it has already stopped.

maxSpendUsd is an optional spending ceiling, not a different pricing plan or an extra charge. maxRuntimeHours limits the session's duration. idleStopMinutes is off by default: an idle session is never stopped for you, and after 30 idle minutes you get one warning email with the hourly cost and a Stop link. Omit it or send null; set 2 or more minutes only if you want it to stop on idle. Every launch takes the same option: idle_stop_minutes on a deployment plan, idleStopMinutes on a console or AWS launch, and --idle-stop-minutes in the CLI. Setup time and tool/browser pauses do not end a job unless you explicitly select this timer. Runtime, spending and balance limits still apply. Stop the session explicitly when finished.

Keep one endpoint for the job and share its client across your browser workers. Creating one deployment per browser creates multiple hourly bills. Use the returned model name; a Hugging Face repository ID is not interchangeable with it.

During API startup, function tools need a description and tool_choice: "auto" (or omit it). Required, named, disabled or custom tool selection returns 503 startup_capability_unavailable; the request is not rewritten. Wait for dedicated_replica.state: "ready" for private-only tool semantics. The quote and connection expose startup_capabilities.

Raw HTTP API

Management base URL: https://www.inferoptimal.com/console/api/deployments. Every request below uses Authorization: Bearer $INFEROPTIMAL_API_KEY. POST requests also use Content-Type: application/json.

1. Quote a private endpoint

POST /plan — free; starts no session or hardware.

{
  "model": "glm-5.3-flash-nvfp4-cached-tp2",
  "rapid_coldboot": true,
  "max_runtime_hours": 12
}

The response includes plan_id, the hourly rate, required credit hold and session limits. For an immediate-start private deployment, preparation.immediate_inference: true and dedicated_in_background: true describe the handoff. A quote alone does not allocate hardware. Resolve returned blockers before creating a session.

2. Launch

POST / — creates the deployment. The hourly-rate meter starts when inference becomes available; immediate-start inference can be available before the private GPUs finish loading.

{
  "plan_id": "plan_FROM_QUOTE",
  "idempotency_key": "unique-persisted-job-id",
  "usage_plan": {
    "agent_tool": "none",
    "agents": 1,
    "calls": "Our service sends chat completions with the quoted request settings."
  }
}

A launch needs a working way to run agents on the endpoint. usage_plan says how you will use it: an agent tool id from the tool list, or none with how your program calls it. A tool other than none also needs usage_plan.setup: what node ~/.inferoptimal/agents.mjs version printed, or {"by": "user", "plan": "..."}. An account that already recorded its way in the console may leave usage_plan out. Without one the launch is held with 409 agent_setup_required, which names what to set up; nothing is ordered. In the JavaScript helper, pass usagePlan.

The launch and GET /{deployment_id} carry box_agents: optimized_agents, how many concurrent agents the endpoint's box is optimized for (null where none is measured), and use_all, one line saying to keep them all busy. ?detail=true adds agent_setup, the way to run agents the launch used; the connection carries the same line in usage.concurrency. By default keep all of them busy, through parallel launches or one larger campaign, unless you choose fewer.

Save deployment_id. An immediate-start deployment returns inference_ready: true when startup inference is available; the private GPU may still be loading. GET /{deployment_id} reads its state; GET / lists your deployments.

3. Retrieve the connection and inference key

POST /{deployment_id}/connection with {} returns base_url, model and supported API modes.

POST /{deployment_id}/key returns the session's api_key:

{"idempotency_key":"unique-persisted-key-request-id"}

Retrying the same key request works for ten minutes. Store the inference key securely; do not put your platform key into the inference client.

4. Call the model

Send POST {base_url}/chat/completions using Authorization: Bearer $INFEROPTIMAL_ENDPOINT_KEY:

{
  "model": "glm-5.3-flash-nvfp4",
  "messages": [{"role":"user","content":"Hello"}],
  "stream": true
}

The private GLM offering supports screenshot inputs, streaming, function tools and native /responses, alongside Chat Completions, with a 1,048,576-token context. For Responses, send the conversation and tool results in input with store:false; saved response IDs and background jobs are not supported. An approved startup bridge adapts supported requests while private hardware starts. This does not add Responses support to other model offerings. The request-body limit is 8 MiB, including inline images.

Screenshots can be inline data URLs or public PNG, JPEG, WebP or GIF URLs. Remote images are downloaded through an isolated public service before inference: up to 6 MiB each, 64 downloads and 10 seconds total, with the final inline request still limited to 8 MiB. Private URLs and remote audio/video URLs are not supported. Inline screenshot histories have no additional image-count limit.

5. Stop

POST /{deployment_id}/stop with {} revokes access and ends billing once accepted. A 202 response with status: "terminating" means hardware cleanup continues in the background. Read cost.metering_stopped and cleanup.provider_confirmed for their separate states; Stop admission does not claim provider cleanup has finished. Retry Stop with the same deployment ID if cleanup is still pending, or read GET /{deployment_id}. Shared startup capacity is not stopped.

Choose the service for your job

Discover offerings with GET /console/api/optimized or the catalog. Chat Completions, screenshots, tools and streaming work without an agent framework. OpenEvolve is optional research tooling, not a requirement for inference.

The helper retries transient management errors against the same deployment and preserves key-request idempotency. It does not replay model generations. If an inference stream has already returned output, do not blindly retry it as a new generation.

Errors return error and code: 401 invalid key; 403 insufficient access; 402 funding required; 409 state or idempotency conflict; 413 request too large; 429 rate limit; 503 inference unavailable. Preserve the response and deployment ID when reporting a failure.

LatentSync 1.6: batch video + audio → MP4

Quote model: "ByteDance/LatentSync-1.6", then launch and retrieve the connection and inference key using the lifecycle above. This prepared endpoint runs on InferOptimal cloud. Use its host as ENDPOINT, without /v1; media jobs use file uploads rather than Chat Completions.

Already launched? Reuse that endpoint and its inference key. The optional workload-sizing form is not needed to submit a job. Upload the actual files as multipart fields named video and audio; file paths, URLs or base64 inside JSON are not accepted. Large-file JSON requests use completed upload IDs instead. Let your HTTP client set the multipart Content-Type and boundary.

curl "$ENDPOINT/jobs" \
  -H "Authorization: Bearer $INFEROPTIMAL_ENDPOINT_KEY" \
  -H "Idempotency-Key: customer-job-001" \
  -F video=@face.mp4 -F audio=@speech.wav \
  -F steps=20 -F guidance=1.5

The 202 response includes id, status: "queued" and status_url. Save the returned ID as JOB_ID. Reuse the idempotency key with identical inputs when retrying this submission; different inputs with the same key return 409. Use a new key for a different job.

# Poll until status is "completed"; stop on "failed" or "cancelled".
curl "$ENDPOINT/jobs/$JOB_ID" \
  -H "Authorization: Bearer $INFEROPTIMAL_ENDPOINT_KEY"

# Download the result after completion.
curl -f "$ENDPOINT/jobs/$JOB_ID/result" \
  -H "Authorization: Bearer $INFEROPTIMAL_ENDPOINT_KEY" \
  -o lipsynced.mp4

New LatentSync 1.6 endpoints process video in bounded windows, with no fixed clip-duration limit. Multipart uploads allow 32 MiB per input; the client below automatically uses chunked uploads for larger files, up to 2 GiB per input. Video dimensions must not exceed 1920 pixels on either side. Leave enough time in your accepted endpoint lease for the job to finish; runtime and spending limits still apply. Read /runtime for your endpoint’s input and parameter requirements. The endpoint accepts one running job and eight queued jobs; additional submissions return 429. Existing LatentSync 1.5 deployments and its model selector remain supported.

Node.js: automatic upload, status and download

Node 20+. Download the media client beside your script. It uses an existing endpoint and reads its actual /runtime contract, so older endpoints do not silently acquire new capabilities.

import { submitMediaJob } from './media-jobs.mjs';
const job = await submitMediaJob({
  baseUrl: process.env.ENDPOINT,
  apiKey: process.env.INFEROPTIMAL_ENDPOINT_KEY,
  videoPath: './face.mp4', audioPath: './speech.wav',
  idempotencyKey: 'customer-job-001',
  parameters: { steps: 20, guidance: 1.5 }
});
console.log('Save this job ID:', job.id);
const status = await job.wait({ timeoutMs: 60 * 60 * 1000 });
if (status.status !== 'completed') throw new Error(status.status);
await job.download('./lipsynced.mp4');

Choose a waiting deadline within your endpoint lease. A client timeout only stops waiting; inspect or cancel the saved job explicitly. Stop the endpoint after all jobs finish and results are downloaded.

New LatentSync 1.6 endpoints use speed_mode: "cached" by default. DeepCache reuses intermediate features for faster inference and can change the output. Set speed_mode: "standard" to disable it. Existing endpoints keep their original runtime.

Direct integrations: POST /uploads with {name, bytes, sha256}, send chunks of at most 8 MiB to PUT /uploads/{id}?offset=N, then POST /jobs with {uploads: {video: ID, audio: ID}, parameters: {...}}. Exact chunk retries are idempotent. Inspect or abort staged uploads with GET or DELETE /uploads/{id}. Staging expires after one hour and is limited to 8 GiB per endpoint.

For larger batches, keep excess jobs in your client queue; on 429, wait for a submitted job to finish, or retry with exponential backoff, keeping the same job key and inputs. Download each completed result promptly: it expires one hour after that job finishes, and stopping the endpoint removes access to all results.

Cancel a job or stop the endpoint

DELETE $ENDPOINT/jobs/$JOB_ID with the inference key cancels that job. A running job stops at its next denoising callback. Cancellation leaves the endpoint running and billing continues.

# When all jobs are finished, stop the deployment using your platform key.
curl -X POST \
  "https://www.inferoptimal.com/console/api/deployments/$DEPLOYMENT_ID/stop" \
  -H "Authorization: Bearer $INFEROPTIMAL_API_KEY" \
  -H "Content-Type: application/json" -d '{}'

Accepted Stop ends access and customer billing. Check cleanup.provider_confirmed for hardware release.

Agent and MCP setup guide · Billing