Overview
Martini provides a typed client for asynchronous image and video generation. Requests are authenticated, validated, billed in olives, added to Generation History, and returned with durable output URLs.
Quick start
1. Install the Martini client
Use Node.js 18 or later. Call Martini from trusted server code, never from the browser.
2. Create an API key
Generation API access is currently enabled for selected accounts. Create a Martini API key or request access in your Martini API settings. The full key is shown once. Store it in a server-side secret such as MARTINI_API_KEY; never expose it in browser code or a NEXT_PUBLIC_* variable.
Sign in with a full Martini account. Organization admins can create and revoke keys; billing members can view existing key metadata but cannot manage keys.
The client sends the key as Authorization: Key <key>. Revoking the key blocks submit, status, and result calls.
3. Create the client
Supported endpoints
| Endpoint alias | Purpose |
|---|---|
bytedance/seedance-2.0/text-to-video | Generate one video from a prompt. |
bytedance/seedance-2.0/image-to-video | Generate one video from a start image, with an optional end image. |
bytedance/seedance-2.0/reference-to-video | Generate one video using supported image, MP4, and audio references. |
bytedance/seedance-2.0/fast/text-to-video | Generate one 480p or 720p video with the faster Seedance tier. |
bytedance/seedance-2.0/fast/image-to-video | Generate one 480p or 720p video from a start image with the faster Seedance tier. |
bytedance/seedance-2.0/fast/reference-to-video | Generate one 480p or 720p video from reference media with the faster Seedance tier. |
bytedance/seedance-2.0/mini/text-to-video | Generate one 480p or 720p video with the most affordable Seedance tier. |
bytedance/seedance-2.0/mini/image-to-video | Generate one 480p or 720p video from a start image with the most affordable Seedance tier. |
bytedance/seedance-2.0/mini/reference-to-video | Generate one 480p or 720p video from reference media with the most affordable Seedance tier. |
xai/grok-imagine-video/v1.5/text-to-video | Generate one video from a prompt at up to 1080p with native audio. |
xai/grok-imagine-video/v1.5/image-to-video | Generate one video from a start image at up to 1080p with native audio. |
xai/grok-imagine-video/v1.5/reference-to-video | Generate one 480p or 720p video from up to seven images and one tagged audio reference. |
bytedance/seedance-2.5/text-to-video | Generate one video with up to 30 seconds of native audio and video. |
bytedance/seedance-2.5/image-to-video | Generate one video from a start image, with an optional end image. |
bytedance/seedance-2.5/reference-to-video | Generate one video from up to 50 image, video, and audio references. |
nano-banana-2 | Generate one image from a prompt. |
nano-banana-2/edit | Generate one edited image from a prompt and image references. |
Supported input subset
Seedance 2.0 supports prompts, 4–15 second durations, resolution, aspect ratio, native audio, bitrate mode, start and end images, supported image/MP4/audio references, and an end-user ID. If duration is omitted or set to auto, Martini uses 5 seconds.
Seedance 2.5 supports the same three generation modes at 480p, 720p, or 1080p, with 4–30 second durations and no bitrate mode. Its auto duration lets the model select a 4–30 second take.
Grok Imagine 1.5 supports text-to-video and image-to-video at 480p, 720p, or 1080p. Its reference mode accepts up to seven images and one optional tagged audio reference at 480p or 720p. Duration is an integer from 1–15 seconds, and native audio is always enabled.
Nano Banana 2 supports a prompt, seed, aspect ratio, output format, safety tolerance, resolution, limit-generations, web search, thinking level, and system prompt. Image references are accepted only on nano-banana-2/edit.
num_images must be 1.
Reference media and prompt binding
Seedance reference arrays are positional, and array order determines each 1-based token. Bind every reference meant to influence the generation. Multiple video references and every audio reference must be bound.
| Seedance input field | Prompt token | Example |
|---|---|---|
image_urls | @ImageN | @Image1 is the lead character. |
video_urls | @VideoN | Follow the camera movement from @Video1. |
audio_urls | @AudioN | Time the movement to @Audio1. |
NORMAN or “the first character image” do not bind media. Write @Image1 is Norman and use @Image1 wherever the prompt needs that identity.Grok reference-to-video uses reference_image_urls and reference_audio_urls. It requires 1–7 images and accepts at most one audio file; address them with the same @ImageN and @Audio1 tokens.
Grok reference-to-video has no video-reference array, accepts at most one audio reference, and supports 480p or 720p. Preserve array order and keep every token within the supplied image or audio array.
The following reference rules and limits apply to Seedance 2.0:
- Each media type has its own numbering: image 1, video 1, and audio 1 are separate references.
- One URL is one reference. A contact sheet is one image; Martini does not split or name its panels.
- Use separate files when individual people, locations, or storyboard panels need distinct prompt tokens. If one sheet repeats a person across views, state that every panel is the same single character.
- Only tagged audio references are used. This is separate from generated audio.
- Unbound images do not fail validation. They are still submitted, with an advisory in
martini.warnings, because Seedance may not use them. Treat that advisory as a prompt bug unless the reference was meant to be unbound. - Multiple video references and every audio reference must have matching tokens.
- Out-of-range tokens and duplicate URLs fail validation.
Seedance 2.0 accepts up to 9 images, 3 videos, and 3 audio files, with 12 files total. Seedance 2.5 accepts up to 30 images, 10 videos, and 10 audio files, with 50 files total. Reference videos must total 2–15 seconds for Seedance 2.0 or 2–30 seconds for Seedance 2.5. Use the image-to-video endpoint for start and end frames; use the reference-to-video endpoint for reference arrays.
Keep the action achievable within the selected duration. Split prompts with many cuts, dialogue lines, entrances, and impacts into separate generations when their order must remain clear.
Unsupported features and values
The following fail before olives are charged or a model provider is contacted:
- Synchronous
run(), streaming status or output, and realtime/WebSocket inference - Provider BYOK
- Webhooks or callback URLs
sync_mode: true, unregistered endpoints, unknown fields, and multiple outputs
Queue methods
| Method | Martini behavior |
|---|---|
martini.subscribe() | Submit, poll status, and return the completed result. |
martini.queue.submit() | Generate immediately by default, or create an editable draft with mode: draft. |
martini.queue.generate() | Start generation for an API-created draft using its latest Martini settings. |
martini.queue.status() | Return the current queue state. |
martini.queue.result() | Return the completed output with a durable Martini URL. |
martini.queue.cancel() | Cancel a queued or running request when the model provider still allows it. |
subscribe() uses polling. A failed or cancelled request ends polling with an error; it does not remain in a polling loop. It always generates immediately; draft mode is available only on queue.submit().
Each API key may have up to 300 active generations. Submit traffic is limited to 300 requests per 10 seconds and 600 requests per minute; excess requests return RATE_LIMITED.
Submit and status responses include olive_cost once pricing is available, and result responses always include it. An overlapping retry of the same idempotent submission can briefly omit the field while the original request is still being priced. This is the snapshotted charge in olives, so it does not change when model pricing changes. Martini applies any negotiated billing-organization, model, and resolution multiplier before rounding the charge to four decimal places. The same negotiated multipliers apply to generations in the Martini interface; MCP generations keep standard pricing.
Create a draft without generating
Set mode: 'draft' to author an editable canvas node without reserving olives, consuming generation capacity, or contacting a model provider. Generate it later using its latest Martini draft settings.
Destinations and Martini options
| Option | Behavior |
|---|---|
projectId | Override the key’s default project. Without a canvas override, Martini uses the project’s first canvas. |
canvasId | Override the destination canvas. It must belong to the selected project. |
mode: 'draft' | Create an editable draft without starting or charging for generation. Omit it to generate immediately. |
placement: 'auto' | Request automatic placement. This is the only supported placement value. |
idempotencyKey | Optional key for replaying the same submit without another charge or provider job. |
Without destination options, Martini uses the defaults bound to the API key. The first key for an organization creates or reuses the private API Generations project and its Canvas 1.
Overrides require project write access and must resolve to the key’s billing organization. Projects without a canvas return CANVAS_NOT_FOUND.
Media URLs and base64
Base64 works in the normal URL fields
Use a data:<mime>;base64,<bytes> URL directly in fields such as image_url, image_urls, video_urls, and audio_urls. There is no separate base64 field or upload call.
Media URL fields accept:
- Public HTTPS URLs
- Martini-hosted HTTPS URLs
- Base64 data URLs in the form
data:<mime>;base64,<bytes>
| Media | Limit |
|---|---|
| Images | 10 MB decoded or fetched bytes per image |
| MP4 video references | 30 MB decoded or fetched bytes per video |
| MP3 or WAV references | 15 MB decoded or fetched bytes per audio file |
| Complete request body | 72 MiB, including base64 and JSON |
Martini validates type and content, then uploads data URLs to durable storage before billing, provider submission, or canvas persistence. Binary File and Blob values are not accepted directly in generation media fields; upload them as reusable assets first. Completed results contain durable Martini URLs, not expiring provider URLs.
Base64 adds roughly 33% to the media size. One 30 MB video becomes about 40 MiB in the request; two maximum-sized inline videos exceed the request-body limit. Use HTTPS URLs for large or multiple references.
Asset uploads
Upload reusable project media before generation when you have a binary file, a large reference, or media you want to use across multiple requests. Uploads create visible canvas assets in the API key's default project and canvas, then return a stable Martini URL.
To upload into another accessible project, provide both projectId and canvasId. Upload destination overrides must include both values.
Direct upload flow
martini.assets.upload() wraps the full flow: prepare a create-only upload URL, PUT bytes directly to Martini storage, complete the upload, then poll asset status with jittered backoff until processing finishes.
| Step | Method | Behavior |
|---|---|---|
| 1 | assets.prepareUpload() | Creates an uploading canvas placeholder and returns a presigned PUT URL. |
| 2 | PUT uploadUrl | Uploads bytes directly to storage with the returned headers. |
| 3 | assets.completeUpload() | Verifies the object, claims the upload, and queues thumbnail, proxy, waveform, or metadata processing. |
| 4 | assets.get() | Polls for completed or failed status and returns the durable asset URL. |
Upload limits
| Media | Direct upload limit | Base64 fallback limit |
|---|---|---|
| Images | 10 MB JPEG, PNG, or WebP | 10 MB |
| Video | 30 MB MP4 | 30 MB MP4 |
| Audio | 15 MB MP3 or WAV | 15 MB |
| Batch prepare/complete | 50 files or asset IDs | One file per call |
The client uses direct upload for normal file transfers. assets.uploadBase64() exists for small server-side fallbacks and local development paths where direct storage is unavailable. At high volume, keep base64 low-concurrency and prefer direct uploads.
Upload completion can return queued while Martini waits for a bounded processing worker. Keep the default SDK polling backoff unless you are coordinating your own queue.
Automatic placement
Visible automatic placement
After validation and a successful charge, Martini creates a visible pending generation on the selected canvas, placed to the right of the rightmost root node. Completion replaces the pending state with the generated asset; failure remains visible on the canvas.
If inference completes but final canvas placement fails, the generation remains completed and billed. The durable result is still available, and placement can be retried without another provider call or olive charge.
Submit, status, and result responses include a martini object with preview_url, project_id, canvas_id, asset_id, and any non-blocking input advisories in warnings.
Queue lifecycle
| Generation state | Queue response |
|---|---|
| Pending | IN_QUEUE |
| Running | IN_PROGRESS |
| Completed | COMPLETED |
| Failed | HTTP error |
queue.submit() returns a Martini-owned public request ID. Provider operation IDs remain private. Returned response, status, and cancellation URLs use Martini API URLs. Status, result, and cancel calls must use the same API key and endpoint alias used at submission.
Error contract
Errors use JSON with a stable Martini code. Provider details are sanitized before they are returned.
| HTTP status | Stable codes |
|---|---|
400 | INVALID_TARGET_URL, UNSUPPORTED_OPERATION, WEBHOOKS_UNSUPPORTED, UNSUPPORTED_MARTINI_MODE |
401 | INVALID_API_KEY, API_KEY_REVOKED |
402 | INSUFFICIENT_OLIVES, SPEND_CAP_EXCEEDED |
403 | PROJECT_ACCESS_DENIED, BILLING_ORGANIZATION_MISMATCH, GENERATION_API_DISABLED |
404 | ENDPOINT_NOT_REGISTERED, REQUEST_NOT_FOUND, CANVAS_NOT_FOUND |
409 | API_KEY_DEFAULTS_MISSING, DRAFT_ALREADY_GENERATING, REQUEST_ALREADY_TERMINAL, RESULT_NOT_READY, CANCELLED |
422 | INVALID_INPUT, UNSUPPORTED_INPUT, INVALID_MEDIA, MEDIA_TOO_LARGE, GENERATION_FAILED |
429 | RATE_LIMITED |
500 | GENERATION_SUBMISSION_FAILED, PLACEMENT_FAILED |
503 | GENERATION_API_ACCESS_UNAVAILABLE |
Seedance 2.5 example
This example uses duration: 'auto', allowing the model to select a 4–30 second take.
Nano Banana 2 example
Nano Banana returns one image. Keep num_images: 1. Use the /edit endpoint for image references; video, audio, and PDF context are unsupported.