Every API here is available over the APIs.io API and to AI agents over MCP.
openapi: 3.2.0
info:
title: ViewMax Studio Generation API
summary: Public REST API for ViewMax Studio media generation.
description: 'Create video, image, music, speech, and sound-effect tasks with a ViewMax API key, then poll GET /api/v1/tasks/{taskId} every 5–10 seconds until `success`, `failed`, or `canceled`.
GET /api/v1/models and GET /api/v1/voices are public. Every other endpoint needs `Authorization: Bearer sk-...`; keys have full access to the account and REST does not check scopes. OAuth applies to the MCP server only.
Credits are deducted when a task is created; `failed` and `canceled` tasks are refunded automatically. Send an `Idempotency-Key` header so a retried POST returns the original task instead of charging again.
`/api/v1` sends no CORS headers: call it from a server.'
version: 1.1.0
contact:
name: ViewMax Studio
email: support@viewmax.studio
url: https://viewmax.studio/developers
license:
name: ViewMax API terms
url: https://viewmax.studio/terms-of-service
servers:
- url: https://viewmax.studio
description: ViewMax Studio production
security:
- bearerAuth: []
tags:
- name: Generation
description: Create credit-consuming generation tasks.
paths:
/api/v1/videos:
post:
operationId: createVideoTask
tags:
- Generation
summary: Create a video generation task
description: Validates the request, charges credits, and submits the job before responding; the video renders asynchronously. Poll GET /api/v1/tasks/{taskId}. Omitted duration, resolution and aspect_ratio take `modes..defaults` from GET /api/v1/models before pricing.
security:
- bearerAuth: []
parameters:
- $ref: '#/components/parameters/IdempotencyKey'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateVideoRequest'
responses:
'200':
$ref: '#/components/responses/Task'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'402':
$ref: '#/components/responses/InsufficientCredits'
'500':
$ref: '#/components/responses/InternalError'
'503':
$ref: '#/components/responses/ContentSafetyUnavailable'
/api/v1/images:
post:
operationId: createImageTask
tags:
- Generation
summary: Create an image generation task
description: 'Validates the request, charges credits, and submits the job before responding; most models render asynchronously. Poll GET /api/v1/tasks/{taskId}. On Pro and Ultra, flagship image models can be covered by the daily plan allowance (`costCredits: 0`).'
security:
- bearerAuth: []
parameters:
- $ref: '#/components/parameters/IdempotencyKey'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateImageRequest'
responses:
'200':
$ref: '#/components/responses/Task'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'402':
$ref: '#/components/responses/InsufficientCredits'
'500':
$ref: '#/components/responses/InternalError'
'503':
$ref: '#/components/responses/ContentSafetyUnavailable'
/api/v1/music:
post:
operationId: createMusicTask
tags:
- Generation
summary: Create a music generation task
description: 'Renders synchronously: the response usually already has `status: "success"` and the track in `taskUrls`. Costs `max(60, ceil(duration_seconds))` credits (default 60 s).'
security:
- bearerAuth: []
parameters:
- $ref: '#/components/parameters/IdempotencyKey'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateMusicRequest'
responses:
'200':
$ref: '#/components/responses/Task'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'402':
$ref: '#/components/responses/InsufficientCredits'
'500':
$ref: '#/components/responses/InternalError'
'503':
$ref: '#/components/responses/ContentSafetyUnavailable'
/api/v1/speech:
post:
operationId: createSpeechTask
tags:
- Generation
summary: Convert text to speech
description: Renders synchronously with a `voice_id` from GET /api/v1/voices. Costs `max(1, ceil(characters / 20))` credits.
security:
- bearerAuth: []
parameters:
- $ref: '#/components/parameters/IdempotencyKey'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateSpeechRequest'
responses:
'200':
$ref: '#/components/responses/Task'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'402':
$ref: '#/components/responses/InsufficientCredits'
'500':
$ref: '#/components/responses/InternalError'
/api/v1/sound-effects:
post:
operationId: createSoundEffectTask
tags:
- Generation
summary: Create a sound effect
description: Renders synchronously from a text prompt. Costs 5 credits.
security:
- bearerAuth: []
parameters:
- $ref: '#/components/parameters/IdempotencyKey'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateSoundEffectRequest'
responses:
'200':
$ref: '#/components/responses/Task'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'402':
$ref: '#/components/responses/InsufficientCredits'
'500':
$ref: '#/components/responses/InternalError'
'503':
$ref: '#/components/responses/ContentSafetyUnavailable'
components:
responses:
Task:
description: Task created, or the existing task for a repeated `Idempotency-Key`.
content:
application/json:
schema:
type: object
required:
- code
- message
- data
properties:
code:
type: integer
enum:
- 0
message:
type: string
enum:
- ok
data:
$ref: '#/components/schemas/Task'
Unauthorized:
description: 'Missing or invalid API key (`code: -1001`).'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
InternalError:
description: 'Provider or server failure. With `error.task_id` the task failed and was refunded: retry once. When the hint says the outcome is unknown, the task stays `pending` with the charge kept: poll it instead of retrying.'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
BadRequest:
description: '`invalid_request`: fix the request using `message` before retrying (includes disabled or offline models). `content_rejected`: change the prompt or media. When the provider rejected an accepted task, `error.task_id` is set and the task is failed and refunded.'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
ContentSafetyUnavailable:
description: External content-safety check temporarily unavailable. Retry after a short delay.
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
InsufficientCredits:
description: 'Balance below the task cost (`code: -1002`). No task was created; do not retry until credits are added.'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
schemas:
CreateImageRequest:
type: object
required:
- model
- prompt
properties:
model:
type: string
description: Model id from GET /api/v1/models?type=image.
prompt:
type: string
minLength: 1
scene:
type: string
enum:
- text-to-image
- image-to-image
description: 'Default: `image-to-image` when `image_urls` is non-empty, else `text-to-image`.'
image_urls:
type: array
items:
type: string
pattern: ^https?://
description: Required (at least one) for `image-to-image`. ViewMax sets no count limit; the model provider may.
options:
type: object
additionalProperties: true
properties:
aspect_ratio:
type: string
quality:
type: string
description: One of the scene's `qualities`, e.g. `1K`/`2K`/`4K`, `Basic`/`High`, or `Low`/`Medium`/`High`/`XHigh`/`Max` (GPT Image 2.5).
resolution:
type: string
enum:
- 1K
- 2K
- 4K
description: GPT Image 2.5 (default `1K`). Other models read it as `quality` when `quality` is omitted.
size:
type: string
pattern: ^[0-9]+x[0-9]+$
description: 'GPT Image 2.5 exact pixels, e.g. `1536x1024`: multiples of 16, longest side ≤ 3840, ratio ≤ 3:1, 655,360–8,294,400 pixels. Overrides `aspect_ratio` and `resolution`.'
Task:
type: object
description: Generation task. Returned by every create endpoint and by GET /api/v1/tasks/{taskId}.
required:
- id
- status
- mediaType
- costCredits
- taskUrls
properties:
id:
type: string
description: Task id. Poll GET /api/v1/tasks/{taskId} with it.
status:
type: string
enum:
- pending
- processing
- success
- failed
- canceled
description: '`pending`/`processing`: keep polling. `success`: read `taskUrls`. `failed`/`canceled`: terminal, credits refunded.'
mediaType:
type: string
enum:
- video
- image
- music
- audio
scene:
type: string
description: Mode or scene, e.g. `text-to-video`, `text-to-image`, `text-to-music`, `text-to-speech`, `text-to-sound-effects`.
model:
type: string
description: Model identifier used for the job. May differ from the catalog id sent in the request.
prompt:
type: string
options:
type:
- string
- 'null'
description: JSON-encoded options as priced and submitted, including filled-in defaults and `mode` for video.
taskUrls:
type: array
items:
type: string
format: uri
description: Result URLs. Empty until the task succeeds.
costCredits:
type: integer
description: Credits charged. `0` when a plan allowance covered it.
waivedCredits:
type: integer
description: Normal price waived by a plan allowance; otherwise `0`.
fairUseKey:
type:
- string
- 'null'
description: Plan allowance slot used by this task, or `null`.
taskInfo:
type:
- string
- 'null'
description: JSON string. While pending/processing/success it holds job diagnostics whose shape is not stable. On `failed`/`canceled` it is `{"errorCode","error","errorMessage"}`; `errorCode` is one of `insufficient_credits`, `auth_required`, `rate_limited`, `request_cancelled`, `provider_unavailable`, `invalid_input`, `content_rejected`, `busy`, `duplicate_request`, `generic`. `errorMessage` carries the provider's reason only for `invalid_input`.
taskResult:
type:
- string
- 'null'
description: JSON string. Same failure payload as `taskInfo` on `failed`/`canceled`; otherwise job diagnostics with no stable shape.
taskId:
type:
- string
- 'null'
description: Upstream job id (diagnostic only).
createdAt:
type: string
format: date-time
updatedAt:
type: string
format: date-time
userId:
type: string
creditId:
type:
- string
- 'null'
visibility:
type: string
deletedAt:
type:
- string
- 'null'
format: date-time
CreateMusicRequest:
type: object
required:
- prompt
properties:
prompt:
type: string
minLength: 1
description: Track description. When `options.style` or `options.title` is set, the prompt is treated as lyrics.
options:
type: object
additionalProperties: true
description: '`options.duration` and `options.music_length_ms` are rejected (400); use `duration_seconds`.'
properties:
duration_seconds:
type: number
minimum: 3
maximum: 300
default: 60
description: 'JSON number. Cost: `max(60, ceil(duration_seconds))` credits.'
instrumental:
type: boolean
description: '`true` for no vocals.'
style:
type: string
description: Genre or mood. Makes the prompt be treated as lyrics; do not also send `lyrics`.
title:
type: string
description: Song title. Also makes the prompt be treated as lyrics.
lyrics:
type: string
ErrorResponse:
type: object
additionalProperties: false
required:
- code
- message
- error
properties:
code:
type: integer
description: '`-1` for most errors, `-1001` unauthorized, `-1002` insufficient credits.'
message:
type: string
error:
type: object
additionalProperties: false
required:
- type
- hint
- docs
properties:
type:
type: string
enum:
- unauthorized
- invalid_request
- insufficient_credits
- not_found
- content_rejected
- internal_error
hint:
type: string
description: What to do next, including whether retrying the same request can help.
docs:
type: string
format: uri
task_id:
type: string
description: Present when the request created a task before failing. The task is `failed` and refunded, or still `pending` when the provider outcome is unknown (poll it instead of retrying).
CreateVideoRequest:
type: object
required:
- model
- prompt
properties:
model:
type: string
description: 'Model id from GET /api/v1/models. Offline models return 400 `video model temporarily unavailable: <id>. ...`.'
prompt:
type: string
minLength: 1
description: 'Trimmed. At most the model''s `max_prompt_length` characters when the catalog lists one (`minimax-h3`: 7000).'
mode:
type: string
enum:
- text-to-video
- image-to-video
- video-to-video
- frames-to-video
- reference-to-video
description: 'Must be a key of the model''s `modes`. Default: `image-to-video` when `image_urls` is non-empty, else `video-to-video` when `video_urls` is non-empty, else `text-to-video`.'
image_urls:
type: array
items:
type: string
pattern: ^https?://
description: 'http(s) URLs the provider can fetch without authentication. image-to-video: 1 (some models 2); frames-to-video: 2 (start, end); reference-to-video: 1 or more. Per-model maximums are in the docs.'
video_urls:
type: array
maxItems: 1
items:
type: string
pattern: ^https?://
description: One source video. Required for `video-to-video`; optional for `reference-to-video` on Seedance 2.0, 2.0 Fast, 2 Mini and MiniMax H3.
options:
$ref: '#/components/schemas/VideoOptions'
CreateSoundEffectRequest:
type: object
required:
- prompt
properties:
prompt:
type: string
minLength: 1
options:
type: object
additionalProperties: true
description: JSON numbers; out-of-range values return 400 `options.<key> must be a number from <min> to <max>`.
properties:
duration_seconds:
type: number
minimum: 0.5
maximum: 22
prompt_influence:
type: number
minimum: 0
maximum: 1
VideoOptions:
type: object
additionalProperties: true
description: Values must come from the model's `modes.<mode>` in GET /api/v1/models. Omitted `duration`, `resolution` and `aspect_ratio` take `modes.<mode>.defaults` before pricing. Other keys are forwarded unvalidated.
properties:
duration:
oneOf:
- type: string
pattern: ^[0-9]+[sS]?$
- type: integer
minimum: 1
description: '`"8s"`, `"8"` and `8` are equivalent.'
resolution:
type: string
description: Case-insensitive; send the catalog spelling, e.g. `720p`, `4k`, `768P`, `2K`.
aspect_ratio:
type: string
description: Exact case, e.g. `16:9`, `Auto`.
audio:
type: boolean
description: '`true` only when `audio_toggle` is true; `false` is always accepted.'
source_video_duration_seconds:
type: number
exclusiveMinimum: 0
description: Source video length. Required with `video_urls` when the mode lists `credits_per_second` (Seedance 2.x, Seedance 2.5, MiniMax H3).
CreateSpeechRequest:
type: object
required:
- text
- voice_id
properties:
text:
type: string
minLength: 1
description: 'Trimmed text to speak. Cost: `max(1, ceil(characters / 20))` credits.'
voice_id:
type: string
description: Voice `id` from GET /api/v1/voices.
options:
type: object
additionalProperties: true
description: Out-of-range numbers are clamped, not rejected.
properties:
model_id:
type: string
description: 'Default: the voice''s `recommended_model`.'
speed:
type: number
minimum: 0.25
maximum: 4
stability:
type: number
minimum: 0
maximum: 1
similarity_boost:
type: number
minimum: 0
maximum: 1
parameters:
IdempotencyKey:
name: Idempotency-Key
in: header
required: false
description: Optional. 1–200 visible ASCII characters, no spaces (otherwise 400). A repeated key for the same account, on any create endpoint, returns the task the first request created with its original `costCredits` and charges nothing; the body is not compared. Without it, every request creates and charges a new task.
schema:
type: string
minLength: 1
maxLength: 200
pattern: ^[\x21-\x7E]+$
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: API key
description: 'ViewMax API key from Settings → API Keys (`sk-` followed by 32 letters and digits). Send `Authorization: Bearer sk-...`. Every key has full access to its account; REST does not check scopes and has no per-key rate limit.'
oauth2:
type: oauth2
description: 'OAuth 2.0 for the ViewMax MCP server (`/api/mcp`) only. REST endpoints accept API keys, not OAuth tokens. Discovery: `/.well-known/oauth-protected-resource/api/mcp` and `/.well-known/oauth-authorization-server/api/auth`.'
flows:
authorizationCode:
authorizationUrl: https://viewmax.studio/api/auth/oauth2/authorize
tokenUrl: https://viewmax.studio/api/auth/oauth2/token
refreshUrl: https://viewmax.studio/api/auth/oauth2/token
scopes:
models:read: Read the public model and voice catalogs without creating generation tasks.
tasks:read: Read generation task status and output URLs for the authenticated account.
tasks:write: Create credit-consuming video, image, music, speech, and sound-effect tasks.
credits:read: Read the remaining credit balance for the authenticated account.
externalDocs:
description: ViewMax API documentation
url: https://viewmax.studio/docs/api
x-viewmax-mcp:
url: https://viewmax.studio/api/mcp
transport: streamable-http
serverCard: https://viewmax.studio/server-card