Skip to content

For developers

API documentation

The ModelMesh API is OpenAI-compatible: change the base_url and key, and your code, the OpenAI SDK, Cursor, LangChain or n8n will work with ModelMesh models.

You can import the spec into Postman or Insomnia, or generate a client from it. ModelMesh API 1.0.0, OpenAPI 3.1.0.

Quick start

Base URL for all requests:

https://api.modelmesh.shop/v1
  1. Create a key in your account, under "API keys". The full key is shown only once — save it.
  2. Set the base_url and your ModelMesh key in the OpenAI SDK.
  3. Pick a model from GET /models and send a request.
curl https://api.modelmesh.shop/v1/chat/completions \
  -H "Authorization: Bearer $MODELMESH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-5-mini",
    "messages": [{"role": "user", "content": "Hello!"}]
  }'

Authorization

Pass the key in the Authorization header of every request:

Authorization: Bearer mm_live_...
  • Live keys start with mm_live_, test keys with mm_test_.
  • A test key returns stubs instead of model responses and deducts nothing — handy for debugging your integration.
  • A key can be restricted: token limit (total, per day, per month) or a separate balance, a list of models and categories, requests per minute, expiry date.
  • Do not publish a key in frontend code or repositories. Revoke a leaked key in your account — it stops working immediately.

Endpoints

All paths are relative to the base URL. Detailed request and response schemas are in openapi.json.

  • GET/modelsList of models

    Models available to your key, with prices in Mesh tokens (MT).

    Responses: 200, 401

  • POST/chat/completionsChat (OpenAI chat.completions)

    Supports stream: true (SSE in the OpenAI format), tools (function calling) and vision (images in content). Charges are taken from the prepaid key's wallet or from the account balance within the key's limit. Test keys mm_test_ return a stub without charges. Only n=1 is supported. When streaming, the price arrives in the last usage chunk (cost_mt); the X-ModelMesh-Balance header is the balance before the request.

    • stream: true — streamed response (SSE in OpenAI format).
    • tools — function calling in OpenAI format.
    • vision — images in content (type: image_url) for models that understand them.

    Responses: 200, 400, 401, 402, 403, 404, 429, 502

  • POST/embeddingsEmbeddings (OpenAI embeddings)

    Responses: 200, 400, 401, 402, 403, 404, 429, 502

  • GET/balanceKey balance

    For a prepaid key — the key's wallet balance; for a limit key — the minimum of the remaining limit and the account balance.

    GET /balance returns balance_mt: for a key with a separate balance — the key balance, for a key with a limit — the smaller of the remaining limit and the account balance.

    Responses: 200, 401

  • GET/usageKey usage

    GET /usage accepts optional from and to (ISO 8601) and returns usage by day: total_cost_mt, total_requests and days[] with date, cost_mt, requests. Defaults to the last 30 days.

    Responses: 200, 400, 401

  • POST/images/generationsImage generation (OpenAI images)

    Models of the image category from GET /models. size is converted to the model's aspect ratio and resolution, n is the number of images (if the model supports it), other model parameters go in extra (the ui_schema keys from GET /models). The response arrives when the image is ready; url links are signed and valid for one day. Only the price of images actually received is charged; on a provider error nothing is charged (usage.cost_mt).

    Responses: 200, 202, 400, 401, 402, 403, 404, 429, 502

  • POST/images/editsImage editing (OpenAI images/edits)

    multipart/form-data: image (one or more PNG/JPEG/WebP/GIF files), prompt, model, n, size, response_format. Or JSON with data-URL images: images: [{ "image_url": "data:image/png;base64,..." }]. External URLs are not accepted, mask is not supported. Models are of the image category with an image field in ui_schema.

    Responses: 200, 202, 400, 401, 402, 403, 404, 429, 502

  • POST/audio/speechText to speech (OpenAI audio/speech)

    The response is an audio file (audio/*). voice is a model voice from ui_schema (standard OpenAI voices are replaced with the model's default voice). The price is per character count.

    Responses: 200, 202, 400, 401, 402, 403, 404, 429, 502

  • POST/audio/transcriptionsSpeech recognition (OpenAI audio/transcriptions)

    multipart/form-data: file (MP3, WAV, M4A, OGG, MP4, MOV), model, language, response_format (json, text, verbose_json). The price is per duration, measured from the file.

    Responses: 200, 202, 400, 401, 402, 403, 404, 429, 502

  • POST/tasksAsynchronous task: video, music, any media model

    Responds immediately with 202 and a task (status: queued). Tokens are reserved at the price when the task starts and charged only for a finished result; on an error or timeout (2 h) they are returned. You can learn about completion by polling GET /tasks/{id} or via webhook_url (https): ModelMesh sends a POST with the event task.succeeded, task.failed or task.canceled and the signature X-ModelMesh-Signature: t=<unix>,v1=<hex> — HMAC-SHA256 of <t>.<body> with the secret from GET /webhooks/secret. A failed delivery (non-2xx) is retried with a delay for up to ~a day; discard duplicates by the webhook-id header.

    Responses: 202, 400, 401, 402, 403, 404, 409, 429

  • GET/tasks/{id}Task status and result

    Only tasks and synchronous generations created by this key are visible. File links are signed and valid for one day — request the task again to get fresh ones.

    Responses: 200, 401, 404

  • GET/webhooks/secretWebhook signature verification secret

    The whsec_... secret for verifying X-ModelMesh-Signature on this key's task events.

    Responses: 200, 401

Streaming

With stream: true the response arrives as text/event-stream: data: {…} chunks in OpenAI format and a final data: [DONE]. Before [DONE] a chunk with usage arrives — it contains the request cost.

curl -N https://api.modelmesh.shop/v1/chat/completions \
  -H "Authorization: Bearer $MODELMESH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-5-mini",
    "stream": true,
    "messages": [{"role": "user", "content": "Write a haiku about tokens"}]
  }'

Images, audio and tasks

Images, speech synthesis and speech recognition work through the usual OpenAI SDK methods (images.generate, images.edit, audio.speech, audio.transcriptions). Video, music and any long generations run as asynchronous /tasks.

  • Synchronous methods wait up to 5 minutes for the result. If it is not ready in time, you get a 202 with empty data and the task in modelmesh_task: fetch the result via GET /tasks/{id}.
  • Model parameters (aspect ratio, duration, voice…) go in extra or in the task input: the ui_schema keys from GET /models.
  • Tokens are reserved at the price at launch and charged only for a finished result; on a provider error or timeout they are returned. The key limit and balance are enforced for media too.
  • Result file links are signed and valid for a day; the Idempotency-Key header protects against a repeated launch on network errors.
# Image (the response arrives when it is ready)
curl https://api.modelmesh.shop/v1/images/generations \
  -H "Authorization: Bearer $MODELMESH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "nano-banana-2", "prompt": "Astronaut cat, watercolor", "size": "1792x1024"}'

# Video — a task with a notification to webhook_url
curl https://api.modelmesh.shop/v1/tasks \
  -H "Authorization: Bearer $MODELMESH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "kling-3-pro", "input": {"prompt": "A lighthouse in a storm"}, "webhook_url": "https://example.com/modelmesh"}'

# Task status
curl https://api.modelmesh.shop/v1/tasks/$TASK_ID -H "Authorization: Bearer $MODELMESH_API_KEY"

Verifying webhook_url

When a task finishes, ModelMesh sends a POST to webhook_url: body — {"id", "type": "task.succeeded" | "task.failed" | "task.canceled", "data": task}. The signature is in the X-ModelMesh-Signature header: t=<unix>,v1=<hex> — HMAC-SHA256 of "<t>.<body>" with the secret from GET /webhooks/secret. Reply with 2xx; otherwise delivery is retried (for up to ~a day). Retries of the same event arrive with the same webhook-id header.

import crypto from 'node:crypto';

// secret — from GET https://api.modelmesh.shop/v1/webhooks/secret (whsec_...)
export function verifyModelMeshWebhook(rawBody, header, secret) {
  const { t, v1 } = Object.fromEntries(header.split(',').map((p) => p.split('=')));
  const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
  const fresh = Math.abs(Date.now() / 1000 - Number(t)) < 300;
  return fresh && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1 ?? ''));
}

Request cost

Model prices are in Mesh tokens (MT), visible in GET /models. The cost of each request is returned:

  • in the usage.cost_mt field of the response (a decimal string, MT; usage.cost_mmt — the same in thousandths);
  • in the X-ModelMesh-Cost header — charged for the request, MT;
  • in the X-ModelMesh-Balance header — the balance after the request, MT.

Only the actual cost is charged. Test keys charge nothing. Plans and token packages

Errors

Errors use the OpenAI format, so SDKs handle them as usual:

{
  "error": {
    "message": "Your API key does not have access to the model `gpt-5-mini`.",
    "type": "permission_error",
    "param": "model",
    "code": "model_not_allowed"
  }
}
StatuscodeMeaning
401invalid_api_keyThe key is missing, invalid, paused, revoked or expired.
402insufficient_balanceNot enough tokens: the key balance, the account balance or the key limit is used up.
403model_not_allowedThis model or category is not allowed for the key (or the model is unavailable on your plan).
429rate_limitedThe key's requests-per-minute limit is exceeded. Retry later (Retry-After header).
400content_policy_violationThe request was rejected by moderation (ours or the model provider's). No tokens charged.
409conflictThe Idempotency-Key has already been used by another request.
502provider_errorThe model provider failed. No tokens charged — retry later.

Examples

Embeddings, model list, key balance and usage:

# Embeddings
curl https://api.modelmesh.shop/v1/embeddings \
  -H "Authorization: Bearer $MODELMESH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "text-embedding-3-small", "input": "Text to search"}'

# Key balance
curl https://api.modelmesh.shop/v1/balance -H "Authorization: Bearer $MODELMESH_API_KEY"

# Usage by day
curl "https://api.modelmesh.shop/v1/usage?from=2026-10-01T00:00:00Z" \
  -H "Authorization: Bearer $MODELMESH_API_KEY"