{"mcp":"https://www.spicyapi.com/api/mcp","rest":"https://www.spicyapi.com/api/v1/tools/{name}","auth":"Authorization: Bearer sk-spicy-...","openapi":"https://www.spicyapi.com/openapi.json","tools":[{"name":"list_models","title":"List models","description":"Every model with its kind (image, image-edit, video, video-edit, chat, speech, realtime, transcription, embedding), USD price (per image, per second by resolution or quality, per 1M tokens, per 10,000 characters of speech input, or per minute of audio), limits (image sizes, outputs per call, resolutions, clip lengths, aspect ratios, prompt character caps), whether it needs or accepts an input image or references, and example clips. Video rows carry `inputs`: which of first frame, last frame, reference images, clip and audio the model takes, `inputs.audio.mode` (driving: the clip is the soundtrack and the mouth follows it; reference: the model generates sound and uses the clip for voice, tone and beat; none) and `inputs.combinations`, the valid input combinations in plain sentences. Read those before generate_video. `silent: true` marks a video model that renders without sound; `bills_input_video_seconds: true` one that also bills the seconds of an input clip. Video-edit rows carry `video_edit` (instruction or motion mode, clip lengths, reference images, whether input seconds are billed) for edit_video. Speech rows carry `speech`: preset voices, whether `instructions` are accepted, custom voices, languages, inline `tags` and voice creation fees. Realtime rows (spicy-live-1) are live voice calls over a WebSocket: a server or app opens a session with POST /v1/realtime/sessions and connects the returned ticket URL; they cannot be driven from a tool call. Prices here are exactly what the API bills. Works with a $0 balance.","guarded":false,"public":true,"annotations":{"title":"List models","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},"argsSchema":{"type":"object","properties":{"kind":{"description":"Filter by kind.","type":"string","enum":["image","image-edit","video","video-edit","chat","speech","realtime","transcription","embedding"]}},"additionalProperties":false}},{"name":"estimate_cost","title":"Estimate cost","description":"USD cost of a request before making it, from the same price table the gateway bills: images by count, video by seconds and resolution (plus the input clip or action clip on models that bill it), video edits by clip length (input plus output seconds for instruction edits, output seconds for motion transfer), chat by tokens, speech by characters of input (plus instructions), transcription by seconds of audio, embeddings by tokens, live calls by minutes. Use it to quote the user and to check against get_account before a spend. Works with a $0 balance.","guarded":false,"public":true,"annotations":{"title":"Estimate cost","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},"argsSchema":{"type":"object","properties":{"model":{"type":"string","description":"A model id from list_models, e.g. spicy-image-1, spicy-image-1-pro, spicy-image-photo-1."},"n":{"description":"Images per call (image models), clamped to the model's maximum.","type":"integer","minimum":1,"maximum":6},"resolution":{"description":"Video or video-edit resolution, e.g. 720P (default) or 1080P.","type":"string"},"quality":{"description":"Motion transfer tier on spicy-animate-1, default standard.","type":"string","enum":["standard","pro"]},"duration":{"description":"Video seconds, default 5; -1 for a model-chosen length where supported.","type":"integer","minimum":-1,"maximum":60},"input_seconds":{"description":"Length of the input clip in seconds: the clip to edit or motion clip on video-edit models (default 5), or a video_url on video models that bill input seconds.","type":"number","minimum":1,"maximum":60},"motion":{"description":"A library motion id (list_motions) on spicy-animate-1; its length sets the output length.","type":"string"},"action":{"description":"An action id (list_actions) on spicy-motion-3 and -fast: its reference clip seconds (4 on most) are billed as input, and duration defaults to 8.","type":"string"},"fps":{"description":"Video frame rate: 30 (default) or 60. 60 smooths motion with frame interpolation for 20% on top of the video price (input or action clip seconds included); audio is kept. If that step fails the 30 fps clip is delivered and the fee refunded.","anyOf":[{"type":"number","const":30},{"type":"number","const":60}]},"tokens":{"description":"Chat: total tokens, default 1000. Embeddings: text tokens, default 1000.","type":"integer","minimum":0,"maximum":9007199254740991},"image_tokens":{"description":"Embeddings on spicy-embed-vision-1: image tokens, default 0.","type":"integer","minimum":0,"maximum":9007199254740991},"characters":{"description":"Speech models: characters of input plus instructions (CJK ideographs and inline tags count too), default 100.","type":"integer","minimum":1,"maximum":9007199254740991},"audio_seconds":{"description":"Transcription: seconds of audio, default 60.","type":"number","minimum":1,"maximum":300},"minutes":{"description":"Live calls (spicy-live-1): minutes of conversation, default 1.","type":"number","minimum":0.1,"maximum":13},"count":{"description":"How many such requests, default 1.","type":"integer","minimum":1,"maximum":100000}},"required":["model"],"additionalProperties":false}},{"name":"get_account","title":"Get account","description":"Balance in USD, spend this calendar month, the monthly spend limit if set, whether approvals are skipped for agents, whether the account has ever topped up, the webhook URL, and whether this key is a sandbox key. Call it first and before any spend.","guarded":false,"public":true,"annotations":{"title":"Get account","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},"argsSchema":{"type":"object","properties":{},"additionalProperties":false}},{"name":"list_images","title":"List images","description":"The account's generated images, newest first. These are the only URLs accepted as image inputs by edit_image, generate_video, edit_video and create_embeddings (no uploads, no third-party URLs). Use it to pick a first frame to animate or a performer for motion transfer.","guarded":false,"public":true,"annotations":{"title":"List images","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},"argsSchema":{"type":"object","properties":{"limit":{"description":"Default 20.","type":"integer","minimum":1,"maximum":100}},"additionalProperties":false}},{"name":"list_characters","title":"List characters","description":"The account's saved characters: reusable identities built from its own generated images. Pass a character id as `character` to generate_image, edit_image or generate_video to get the same person again across images, edits and video. No uploads are involved; a character can only be made from images this account generated. The exception is `imported: true` characters: a registered business can apply in the dashboard (Settings, Character import: https://www.spicyapi.com/dashboard/import) to bring its own platform's existing AI-generated characters over with POST /v1/characters/import. There is no tool for importing, and an agent cannot apply for the user. One with `status: \"under_review\"` cannot be used until a person approves it.","guarded":false,"public":true,"annotations":{"title":"List characters","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},"argsSchema":{"type":"object","properties":{},"additionalProperties":false}},{"name":"create_character","title":"Create character","description":"Save a reusable identity from 1 to 3 of the account's own generated images of the same person (list_images or a generate_image result). Renders one neutral reference sheet (billed as one image edit) and returns the character id to pass as `character` elsewhere. The sheet shows the face and upper body; add_character_view keeps the rest of the body the same from other angles. Guarded: without approval_token it returns {error: \"approval_required\", summary, approval_token}; show the summary (it carries the cost), get the user's yes, call again with the token.","guarded":true,"public":false,"annotations":{"title":"Create character","readOnlyHint":false,"destructiveHint":true,"idempotentHint":false,"openWorldHint":false},"argsSchema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":60,"description":"A label, e.g. Mara."},"image_urls":{"minItems":1,"maxItems":3,"type":"array","items":{"type":"string","format":"uri"},"description":"Generated images of the same person, from this account."},"approval_token":{"description":"Approval token from a previous approval_required response, after the user said yes.","type":"string"}},"required":["name","image_urls"],"additionalProperties":false}},{"name":"add_character_view","title":"Add character view","description":"Add one of the account's own generated images of a character's person from another angle (from behind, a pose, a close-up), with a label saying what it shows. The character's sheet only shows the face and upper body; with views, generate_image, edit_image and generate_video keep the rest of the body the same too (proportions, skin, moles, intimate anatomy), sending the views that fit each prompt (or the ones named in `views`). Free, up to 8 per character. Works with a $0 balance.","guarded":false,"public":true,"annotations":{"title":"Add character view","readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false},"argsSchema":{"type":"object","properties":{"id":{"type":"string","description":"A character id from list_characters."},"image_url":{"type":"string","format":"uri","description":"One of this account's own images of the same person (list_images)."},"label":{"type":"string","minLength":1,"maxLength":80,"description":"What it shows, e.g. close-up of her buttocks from behind, close-up between her legs. Close-ups of one area work best; a full-body view also pulls the pose."},"crop":{"description":"Only this box of the image becomes the view, in fractions from its top left corner (at least 256 px a side): the way to make a close-up from a full-body image.","type":"object","properties":{"x":{"type":"number","minimum":0,"maximum":1},"y":{"type":"number","minimum":0,"maximum":1},"width":{"type":"number","exclusiveMinimum":0,"maximum":1},"height":{"type":"number","exclusiveMinimum":0,"maximum":1}},"required":["x","y","width","height"],"additionalProperties":false}},"required":["id","image_url","label"],"additionalProperties":false}},{"name":"delete_character","title":"Delete character","description":"Remove a saved character. Its reference sheet stays in the image library. Guarded: without approval_token it returns {error: \"approval_required\", summary, approval_token}; show the summary (it carries the cost), get the user's yes, call again with the token.","guarded":true,"public":false,"annotations":{"title":"Delete character","readOnlyHint":false,"destructiveHint":true,"idempotentHint":false,"openWorldHint":false},"argsSchema":{"type":"object","properties":{"id":{"type":"string","description":"A character id from list_characters."},"approval_token":{"description":"Approval token from a previous approval_required response, after the user said yes.","type":"string"}},"required":["id"],"additionalProperties":false}},{"name":"generate_image","title":"Generate image","description":"Text to image, synchronous (10 to 30 seconds), returns durable CDN URLs reusable as image_url for edits and video. Or an image action: model spicy-image-action-1 with an action from list_image_actions and image_url of the woman (one of list_images) puts her face, hair and skin tone into that ready-made explicit scene, no prompt (a minute or two); style studio gives the finished picture a warm glamour colour grade, and anime, 3d or cartoon put her into the drawn version of the scene, for a drawn woman. Or a reference: model spicy-image-reference-1 with reference_image (a picture the user supplies, https or data URL) describes it (faces left out) and draws a brand new image from that description, new people with the same look, and returns the description to reuse as a prompt (30 to 60 seconds, one image). One to six images per call, billed per image at the model's rate; the exact amount is cost_usd in the result. Prompts are screened first and outputs after (withheld images are refunded and counted in `withheld`); blocked prompts cost nothing. Every request depicts adults only. Guarded: without approval_token it returns {error: \"approval_required\", summary, approval_token}; show the summary (it carries the cost), get the user's yes, call again with the token.","guarded":true,"public":false,"annotations":{"title":"Generate image","readOnlyHint":false,"destructiveHint":true,"idempotentHint":false,"openWorldHint":false},"argsSchema":{"type":"object","properties":{"model":{"type":"string","description":"An image model id: spicy-image-1 (fast), spicy-image-1-pro (best prompt adherence), spicy-image-photo-1 (candid natural-light photo look), spicy-image-action-1 (image actions only) or spicy-image-reference-1 (reference_image only; businesses approved for character import)."},"prompt":{"description":"What to generate, up to 4000 characters. Required, except with action or reference_image where it is not accepted.","type":"string","minLength":1,"maxLength":4000},"reference_image":{"description":"On spicy-image-reference-1 only, and required there: the picture to recreate as new people, a public https URL or a data URL (JPEG, PNG or WebP). No prompt, size, style, character or enhance_prompt; n is 1. Pictures of anyone who may be under 18 or of known people are refused at no charge.","type":"string"},"action":{"description":"An image action id from list_image_actions, on spicy-image-action-1 only, e.g. pov-missionary: the still sets the act, pose, room and shape; image_url (or character) supplies the woman's face, hair and skin tone. No prompt, negative_prompt, size or enhance_prompt. Her body stays the still's. style studio adds a warm glamour colour grade to the result; anime, 3d or cartoon picks the drawn version of the still (see styles in list_image_actions); match it to her image.","type":"string"},"image_url":{"description":"With action: the woman, one of this account's own generated images (list_images). Photo women take no style (or studio); anime, 3D or cartoon women take the matching style.","type":"string","format":"uri"},"negative_prompt":{"type":"string","maxLength":2000},"size":{"description":"width*height from the model's limits.sizes, one price per model whatever the size: 1024*1024, 832*1216, 1216*832, 1280*1280, 1440*1440, 1024*1536, 1536*1024, 1080*1920, 1920*1080, 1152*2048, 2048*1152. Default 1024*1024.","type":"string"},"n":{"description":"Images to return, 1 to 6, default 1. Billed per image delivered.","type":"integer","minimum":1,"maximum":6},"seed":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"enhance_prompt":{"description":"Let the model expand the prompt with lighting, camera and detail cues. Default false: the prompt goes as written.","type":"boolean"},"enhance_mode":{"description":"With enhance_prompt: direct (default) rewrites once, agent plans the shot then writes.","type":"string","enum":["direct","agent"]},"character":{"description":"A saved character id (list_characters): the same person is rendered in the new scene, billed at the image-edit rate.","type":"string"},"views":{"description":"With character: body view ids (cv_...) to send, or [] for none; omitted, the views that fit the prompt are picked.","maxItems":2,"type":"array","items":{"type":"string"}},"style":{"description":"Art style (list_styles): photorealistic, studio (glossy glamour), anime, 3d (animated-film CGI) or cartoon (2D adult cartoon). Prompt text only, same price.","type":"string","enum":["photorealistic","studio","anime","3d","cartoon"]},"approval_token":{"description":"Approval token from a previous approval_required response, after the user said yes.","type":"string"}},"required":["model"],"additionalProperties":false}},{"name":"edit_image","title":"Edit image","description":"Prompt-driven edit of one to three images the account generated (list_images), for outfit, pose and scene changes. Synchronous; returns one to six durable CDN URLs, billed per output image (withheld outputs are refunded). Guarded: without approval_token it returns {error: \"approval_required\", summary, approval_token}; show the summary (it carries the cost), get the user's yes, call again with the token.","guarded":true,"public":false,"annotations":{"title":"Edit image","readOnlyHint":false,"destructiveHint":true,"idempotentHint":false,"openWorldHint":false},"argsSchema":{"type":"object","properties":{"model":{"type":"string","description":"An image-edit model id, e.g. spicy-image-edit-1."},"prompt":{"type":"string","minLength":1,"maxLength":4000,"description":"The change to make, up to 4000 characters."},"image_urls":{"description":"Source images, all from this account's own generations. Optional when `character` is given (the character sheet is Image 1; these follow as Image 2 and 3).","maxItems":3,"type":"array","items":{"type":"string","format":"uri"}},"negative_prompt":{"type":"string","maxLength":2000},"size":{"description":"Output width*height from the model's limits (same eleven sizes as generate_image). Omit to let the model choose.","type":"string"},"n":{"description":"Variations to return, 1 to 6, default 1. Billed per image.","type":"integer","minimum":1,"maximum":6},"seed":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"enhance_prompt":{"description":"Let the model expand the prompt with lighting, camera and detail cues. Default false.","type":"boolean"},"character":{"description":"A saved character id (list_characters) to keep the same person.","type":"string"},"views":{"description":"With character: body view ids (cv_...) to send, or [] for none; omitted, the views that fit the prompt are picked.","maxItems":2,"type":"array","items":{"type":"string"}},"approval_token":{"description":"Approval token from a previous approval_required response, after the user said yes.","type":"string"}},"required":["model","prompt"],"additionalProperties":false}},{"name":"generate_video","title":"Generate video","description":"Image to video, text to video or references to video. Asynchronous: returns a task id at once with the price already debited; poll get_job every 10 to 15 seconds until succeeded (output.video_url, usage.output_seconds) or failed (refunded). Every input must be the account's own: images from list_images, clips from its own finished tasks (video_url), audio from upload_audio or generate_speech. Price = seconds x the per-second rate for the resolution; models with bills_input_video_seconds (spicy-motion-3, -fast) also bill the seconds of a video_url clip or of an action clip. duration -1 on spicy-motion-3 lets the model pick the length, debited at the maximum and refunded down to what is rendered. Text to video: spicy-motion-3, -fast, spicy-video-1, spicy-cinema-1. Image to video: spicy-motion-2, spicy-cinema-1-image, spicy-motion-3. Character or references to video: spicy-character-video-1, spicy-cinema-1-character (up to 9 references named Image 1, Image 2 in the prompt), spicy-motion-3. Cheap previews: spicy-motion-draft-1 is a silent draft tier from a first frame; render the keeper on spicy-motion-2 or 3. The spicy-cinema-1 family runs 3 to 15 seconds at 480P to 1080P with native audio always on, no negative_prompt, no enhance_prompt and no audio inputs. Which inputs may travel together differs per model: read list_models inputs.combinations first. Audio comes in two modes (inputs.audio.mode): driving (spicy-motion-2, spicy-video-1: the clip is the soundtrack, the mouth follows it, works with a first frame) and reference (spicy-motion-3, spicy-motion-3-fast: the model generates sound and uses the clip for voice, tone and beat; only with a text prompt or references, never with a first frame). An invalid combination is rejected here before any spend with the valid combinations spelled out. Actions (spicy-motion-3, -fast): pass an action id from list_actions and its reference clip of that sex act supplies the camera, pose and motion second for second while image_url supplies the woman, e.g. {model: \"spicy-motion-3\", action: \"pov-missionary\", image_url: <one of list_images>, prompt: \"hotel room at night, red lingerie\"}. To change a finished clip or transfer motion onto a person, use edit_video. Guarded: without approval_token it returns {error: \"approval_required\", summary, approval_token}; show the summary (it carries the cost), get the user's yes, call again with the token.","guarded":true,"public":false,"annotations":{"title":"Generate video","readOnlyHint":false,"destructiveHint":true,"idempotentHint":false,"openWorldHint":false},"argsSchema":{"type":"object","properties":{"model":{"type":"string","description":"A video model id, e.g. spicy-motion-2, spicy-motion-3, spicy-cinema-1, spicy-motion-draft-1."},"prompt":{"description":"What should happen in the clip; required unless image_url or action is sent. With action it is only the scene (setting, outfit, lighting, extra direction). Capped per model (limits.maxPromptChars: 1500 on spicy-motion-1 and spicy-motion-draft-1, 20000 on spicy-motion-3 and -fast, 5000 on the rest).","type":"string","minLength":1,"maxLength":20000},"image_url":{"description":"First frame, from list_images or a generate_image result. list_models inputs.first_frame says whether it is required, optional or not taken. Not with reference_image_urls; on spicy-motion-3 a first frame pairs only with last_frame_url.","type":"string","format":"uri"},"last_frame_url":{"description":"Closing frame (one of the account's images); the clip travels from image_url to it. Models with inputs.last_frame true; needs image_url (or a continuation video_url).","type":"string","format":"uri"},"reference_image_urls":{"description":"Up to inputs.reference_images of the account's images guiding identity and look without fixing the first shot (a character counts as one). Exclusive with image_url. See inputs.combinations.","maxItems":10,"type":"array","items":{"type":"string","format":"uri"}},"video_url":{"description":"output.video_url of one of the account's own finished tasks. inputs.continuation_clip (spicy-motion-2): continues the clip in place of image_url (2 to 10 s in; duration is the whole output incl. the clip, billed in full). inputs.reference_video (spicy-motion-3, -fast): reference clip to extend or edit (15 s max; clip plus duration within 30 s), never with image_url.","type":"string","format":"uri"},"audio_url":{"description":"A clip from upload_audio or generate_speech (its url, exact; 2 to 30 seconds). What it does depends on inputs.audio.mode: driving (spicy-motion-2, spicy-video-1) makes it the soundtrack with the mouth following it, and works with a first frame; reference (spicy-motion-3, -fast) guides voice, tone and beat while the model generates the sound, and only goes with a text prompt or references, never with image_url. Rejected on models with mode none (spicy-motion-1, spicy-character-video-1).","type":"string","format":"uri"},"audio_urls":{"description":"Several upload_audio clips, up to inputs.audio.max_clips (5 on spicy-motion-3 and -fast, 15 s total); other models take a single audio_url.","maxItems":5,"type":"array","items":{"type":"string","format":"uri"}},"audio_mode":{"description":"Optional guard: what you expect the audio to do. Must equal the model's inputs.audio.mode or the call is rejected naming a model that offers it. Omit to use the model's mode.","type":"string","enum":["driving","reference"]},"resolution":{"description":"480P, 720P (default) or 1080P, within the model's limits.resolutions.","type":"string"},"duration":{"description":"Seconds, default 5 (8 with action), within the model's limits (2 to 30; 3 to 15 on the spicy-cinema-1 family). -1 where inputs.smart_duration is true (spicy-motion-3, -fast): the model chooses; debited at the maximum, settled to the rendered length.","type":"integer","minimum":-1,"maximum":30},"aspect_ratio":{"description":"Text-to-video, references and action only (no first frame or video_url), where inputs.aspect_ratio is true; values in limits.aspectRatios. 16:9, 9:16, 1:1, 4:3, 3:4 on spicy-video-1 (default 16:9); plus 21:9 on spicy-motion-3 and -fast, where omitting it lets the model choose; plus 4:5, 5:4 and 9:21 on spicy-cinema-1 and spicy-cinema-1-character.","type":"string"},"negative_prompt":{"description":"What to avoid. Ignored on spicy-motion-3, spicy-motion-3-fast and the spicy-cinema-1 family.","type":"string","maxLength":2000},"enhance_prompt":{"description":"Let the model expand the prompt with lighting, camera and detail cues. Default false. Not on the spicy-cinema-1 family.","type":"boolean"},"audio":{"description":"Generate audio where the model supports it. spicy-motion-draft-1 is always silent; the spicy-cinema-1 family always has audio.","type":"boolean"},"seed":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"character":{"description":"A saved character id (list_characters). Only on models that take references: spicy-character-video-1 (no first frame needed), spicy-cinema-1-character, spicy-motion-3, spicy-motion-3-fast.","type":"string"},"views":{"description":"With character: body view ids (cv_...) to send, or [] for none; omitted, the views that fit the prompt are picked.","maxItems":2,"type":"array","items":{"type":"string"}},"action":{"description":"An action id from list_actions, e.g. pov-missionary. Runs on spicy-motion-3 and spicy-motion-3-fast: the action's reference clip sets camera, pose and motion; image_url becomes the woman (her identity, not a first frame) and prompt is optional, only the scene. Not with video_url or last_frame_url. duration defaults to 8 and aspect_ratio to the action's own (any supported ratio may be requested); clip plus duration within 30 s. The clip's seconds are billed as input seconds on top of the output.","type":"string"},"style":{"description":"Art style (list_styles): photorealistic, studio (glossy glamour), anime, 3d (animated-film CGI) or cartoon (2D adult cartoon). Prompt text only, same price.","type":"string","enum":["photorealistic","studio","anime","3d","cartoon"]},"fps":{"description":"Video frame rate: 30 (default) or 60. 60 smooths motion with frame interpolation for 20% on top of the video price (input or action clip seconds included); audio is kept. If that step fails the 30 fps clip is delivered and the fee refunded.","anyOf":[{"type":"number","const":30},{"type":"number","const":60}]},"approval_token":{"description":"Approval token from a previous approval_required response, after the user said yes.","type":"string"}},"required":["model"],"additionalProperties":false}},{"name":"edit_video","title":"Edit video","description":"Change a finished clip, or make a person perform a motion. Asynchronous like generate_video: returns a task with the price debited; poll get_job every 10 to 15 seconds (refunded if it fails). Instruction edits (spicy-video-edit-1: 2 to 10 second clips, output the clip's length; spicy-cinema-1-edit: 3 to 30 second clips, the first 15 seconds are edited and returned): video_url (one of the account's finished video outputs) plus a prompt describing the change, optionally reference_image_urls for an outfit, prop or style (the account's own images), keep_audio to keep the soundtrack. Billed per second of input plus output at the resolution's rate, debited at the clip's length and settled to what is rendered. Motion transfer (spicy-animate-1): image_url (the performer, one of the account's images, full body and dressed works best) plus a motion id from list_motions or a video_url of the account's own clip (2 to 30 s); the output is as long as the motion clip, billed per output second by quality. Motion transfer refuses explicit or nude images and clips (refunded, error_code blocked). Guarded: without approval_token it returns {error: \"approval_required\", summary, approval_token}; show the summary (it carries the cost), get the user's yes, call again with the token.","guarded":true,"public":false,"annotations":{"title":"Edit video","readOnlyHint":false,"destructiveHint":true,"idempotentHint":false,"openWorldHint":false},"argsSchema":{"type":"object","properties":{"model":{"type":"string","description":"A video-edit model id: spicy-video-edit-1, spicy-cinema-1-edit or spicy-animate-1."},"video_url":{"description":"The clip: output.video_url of one of the account's finished video tasks. Required for instruction edits; on spicy-animate-1 an alternative to motion.","type":"string","format":"uri"},"prompt":{"description":"Instruction edits (required): the change to make, up to 5000 characters. Not needed for motion transfer.","type":"string","maxLength":5000},"reference_image_urls":{"description":"Instruction edits: outfit, prop or style images from list_images, up to 4 on spicy-video-edit-1 and 5 on spicy-cinema-1-edit.","maxItems":5,"type":"array","items":{"type":"string","format":"uri"}},"resolution":{"description":"Instruction edits: 720P (default) or 1080P.","type":"string","enum":["720P","1080P"]},"aspect_ratio":{"description":"spicy-video-edit-1 only: reshape to 16:9, 9:16, 1:1, 4:3 or 3:4.","type":"string"},"keep_audio":{"description":"Instruction edits: true keeps the clip's original soundtrack.","type":"boolean"},"negative_prompt":{"type":"string","maxLength":2000},"seed":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"enhance_prompt":{"description":"spicy-video-edit-1 only: let the model expand the prompt.","type":"boolean"},"image_url":{"description":"spicy-animate-1 (required): the person who performs the motion, one of the account's images.","type":"string","format":"uri"},"motion":{"description":"spicy-animate-1: a library motion id from list_motions, e.g. mo_hip_dance. Send this or video_url.","type":"string"},"quality":{"description":"spicy-animate-1: standard (default) or pro, priced separately.","type":"string","enum":["standard","pro"]},"user":{"description":"Your own id for the end user making this request.","type":"string","maxLength":128},"approval_token":{"description":"Approval token from a previous approval_required response, after the user said yes.","type":"string"}},"required":["model"],"additionalProperties":false}},{"name":"list_motions","title":"List motions","description":"The motion library for motion transfer on edit_video (spicy-animate-1): id, label, description, preview url, duration_s and aspect_ratio of each clip. Every clip is 9:16, full body, fixed camera and was generated with SpicyAPI video models (no real performer footage). The output is as long as the clip; match the performer image to its framing for best results. Works with a $0 balance.","guarded":false,"public":true,"annotations":{"title":"List motions","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},"argsSchema":{"type":"object","properties":{},"additionalProperties":false}},{"name":"list_actions","title":"List actions","description":"The action library for generate_video with action (spicy-motion-3 and spicy-motion-3-fast): id, label, category (sex, anal, oral, handjob, cumshot, solo, trans), url (the reference clip, 4 to 6 seconds), preview_url (the full 8 second action), poster_url, duration_s (the reference clip's length) and aspect_ratio. The clip supplies the camera, pose and motion of one sex act; the output runs 8 seconds by default and carries the act on past the clip. The caller's image supplies the woman. Every clip was generated with SpicyAPI video models (no real performer footage). Its seconds are billed as input seconds. Works with a $0 balance.","guarded":false,"public":true,"annotations":{"title":"List actions","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},"argsSchema":{"type":"object","properties":{},"additionalProperties":false}},{"name":"list_image_actions","title":"List image actions","description":"The image action library for generate_image on spicy-image-action-1: id, label, category (sex, anal, oral, handjob, cumshot, solo, trans), image_url (the still), preview_url (a small copy), size (the output width*height) and styles (the still redrawn as anime, 3d and cartoon, for style on a drawn woman). 68 ready-made explicit stills; most share their id with a video action (list_actions). The still supplies the act, pose, framing, room and her body; the caller's image supplies her face, hair and skin tone. Every still was generated with AI (no real performers). Billed per image at the model's rate. Works with a $0 balance.","guarded":false,"public":true,"annotations":{"title":"List image actions","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},"argsSchema":{"type":"object","properties":{},"additionalProperties":false}},{"name":"list_styles","title":"List styles","description":"The art styles for `style` on generate_image and generate_video: id, label, description and preview_url (a sample image). photorealistic, studio (glossy glamour), anime, 3d (animated-film CGI) and cartoon (2D adult cartoon). A style only adds prompt text, so the model, seed and price are unchanged. Works with a $0 balance.","guarded":false,"public":true,"annotations":{"title":"List styles","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},"argsSchema":{"type":"object","properties":{},"additionalProperties":false}},{"name":"upload_audio","title":"Upload audio","description":"Bring in a WAV or MP3 (2 to 30 seconds, 15 MB max) from a public https URL for lip-sync and audio-driven video. The clip is transcribed and the transcript screened like a prompt; flat $0.01 for that screening. A clip that fails the screen is discarded with the same 422 shape as a blocked prompt; a 503 means screening was unavailable and nothing was charged. The returned url is what audio_url and audio_urls on generate_video accept; external audio URLs are rejected there. Guarded: without approval_token it returns {error: \"approval_required\", summary, approval_token}; show the summary (it carries the cost), get the user's yes, call again with the token.","guarded":true,"public":false,"annotations":{"title":"Upload audio","readOnlyHint":false,"destructiveHint":true,"idempotentHint":false,"openWorldHint":false},"argsSchema":{"type":"object","properties":{"url":{"type":"string","format":"uri","description":"Public https URL of the WAV or MP3 to fetch (private and plain-IP hosts are refused)."},"approval_token":{"description":"Approval token from a previous approval_required response, after the user said yes.","type":"string"}},"required":["url"],"additionalProperties":false}},{"name":"list_audio","title":"List audio","description":"The account's audio clips, newest first: uploads and generated speech (source upload or speech), with id, url, duration_s, mime and transcript. These urls are the only values generate_video accepts as audio_url or in audio_urls.","guarded":false,"public":true,"annotations":{"title":"List audio","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},"argsSchema":{"type":"object","properties":{"limit":{"description":"Default 20.","type":"integer","minimum":1,"maximum":100}},"additionalProperties":false}},{"name":"delete_audio","title":"Delete audio","description":"Forget an uploaded clip so it can no longer be used as a video input. Videos already made with it are unaffected. Guarded: without approval_token it returns {error: \"approval_required\", summary, approval_token}; show the summary (it carries the cost), get the user's yes, call again with the token.","guarded":true,"public":false,"annotations":{"title":"Delete audio","readOnlyHint":false,"destructiveHint":true,"idempotentHint":false,"openWorldHint":false},"argsSchema":{"type":"object","properties":{"id":{"type":"string","description":"A clip id from list_audio (au_...)."},"approval_token":{"description":"Approval token from a previous approval_required response, after the user said yes.","type":"string"}},"required":["id"],"additionalProperties":false}},{"name":"transcribe_audio","title":"Transcribe audio","description":"Speech to text from a public https URL of an audio file (wav, mp3, m4a/mp4, ogg/opus, flac, webm and more; up to 10 MB and 5 minutes). Synchronous; returns {id, text, language, duration_s, cost_usd}. Billed by the second at the model's per-minute rate (minimum charge applies); the audio is not stored and nothing is screened, since nothing is generated. Use it for voice notes in a companion app or to read back a clip. Guarded: without approval_token it returns {error: \"approval_required\", summary, approval_token}; show the summary (it carries the cost), get the user's yes, call again with the token.","guarded":true,"public":false,"annotations":{"title":"Transcribe audio","readOnlyHint":false,"destructiveHint":true,"idempotentHint":false,"openWorldHint":false},"argsSchema":{"type":"object","properties":{"url":{"type":"string","format":"uri","description":"Public https URL of the audio file (private and plain-IP hosts are refused)."},"model":{"description":"A transcription model id, default spicy-transcribe-1.","type":"string"},"language":{"description":"Optional hint: English, Chinese, German, Italian, Portuguese, Spanish, Japanese, Korean, French or Russian. Omit to detect.","type":"string"},"user":{"description":"Your own id for the end user making this request.","type":"string","maxLength":128},"approval_token":{"description":"Approval token from a previous approval_required response, after the user said yes.","type":"string"}},"required":["url"],"additionalProperties":false}},{"name":"generate_speech","title":"Generate speech","description":"Text to speech, synchronous. Returns {id, url, duration_s, voice, format, cost_usd}: a durable clip (WAV 24 kHz mono, or MP3 on the spicy-voice-2 and spicy-voice-3 families) that is also one of the account's audio clips, so its url goes straight into generate_video as audio_url (driving audio on spicy-motion-2: the mouth follows the words, with a first frame; reference audio on spicy-motion-3: voice, tone and beat, no first frame). Video audio must be 2 to 30 seconds. Models: spicy-voice-1 (preset voices), spicy-voice-1-expressive (preset voices plus instructions for emotion, pace and delivery), spicy-voice-1-custom (the account's own vc_... voices from create_voice), spicy-voice-2 and spicy-voice-2-flash (their own preset voices, instructions, languages Auto, English and Chinese, up to 5,000 characters, and inline tags inside input: control tags switch delivery until the next tag, [sad] [amazed] [deep and loud shouting] [trembling] [angry] [excited] [sarcastic] [curious] [like dracula] [bored] [tired] [scornful] [shouting] [asmr] [panicked] [mischievously] [empathetic] [whispers] [reluctantly] [crying] [serious] [very slowly] [very fast]; sound tags insert a sound, [gasp] [sighing] [clears throat] [giggles] [laughing] [cough] [snorts]; e.g. \"[excited]Hey you.[giggles] Come here.[whispers] Closer.\"), spicy-voice-3 (the highest quality: acts the emotion and pacing of the text, 21 voices, any listed language, free-form tags such as [whispers], [sighs] or [softly, a little shy], no instructions) and spicy-voice-3-flash (the same, lower latency, half the price). Tags count as billed characters. Input caps: 600 characters on the spicy-voice-1 family, 5,000 on spicy-voice-2. Billed per 10,000 characters of input plus instructions (CJK ideographs count 2), settled down to the billed count. Streaming (stream: true) is a REST feature for apps (POST /v1/audio/speech); this tool always returns a stored clip. The text is screened like a prompt; blocked text costs nothing. Guarded: without approval_token it returns {error: \"approval_required\", summary, approval_token}; show the summary (it carries the cost), get the user's yes, call again with the token.","guarded":true,"public":false,"annotations":{"title":"Generate speech","readOnlyHint":false,"destructiveHint":true,"idempotentHint":false,"openWorldHint":false},"argsSchema":{"type":"object","properties":{"model":{"type":"string","description":"A speech model id: spicy-voice-1, spicy-voice-1-expressive, spicy-voice-1-custom, spicy-voice-2, spicy-voice-2-flash, spicy-voice-3 (highest quality) or spicy-voice-3-flash (low latency)."},"input":{"type":"string","minLength":1,"maxLength":5000,"description":"The text to speak: up to 600 characters on the spicy-voice-1 family, 5,000 on the spicy-voice-2 and spicy-voice-3 families, where inline tags like [whispers] or [giggles] are performed."},"voice":{"type":"string","minLength":1,"description":"A preset voice name the model speaks (list_voices), e.g. Cherry on spicy-voice-1, Lingxin on spicy-voice-2, Eva on spicy-voice-2-flash, Sarah on spicy-voice-3, or a vc_... id on spicy-voice-1-custom."},"instructions":{"description":"spicy-voice-1-expressive and the spicy-voice-2 family: how to say it (emotion, pace, pitch, delivery) in plain English or Chinese. Not on spicy-voice-3: use tags.","type":"string","maxLength":1600},"language":{"description":"Auto (default), English, Chinese, German, Italian, Portuguese, Spanish, Japanese, Korean, French or Russian. The spicy-voice-2 family takes Auto, English or Chinese; spicy-voice-3 takes all of them.","type":"string"},"response_format":{"description":"wav (default) on every model; mp3 on the spicy-voice-2 and spicy-voice-3 families.","type":"string","enum":["wav","mp3"]},"user":{"description":"Your own id for the end user making this request.","type":"string","maxLength":128},"approval_token":{"description":"Approval token from a previous approval_required response, after the user said yes.","type":"string"}},"required":["model","input","voice"],"additionalProperties":false}},{"name":"list_voices","title":"List voices","description":"Voices for generate_speech: every preset voice with the models that speak it (spicy-voice-1 has them all, spicy-voice-1-expressive a subset), then the account's designed and cloned voices (vc_... ids, spoken by spicy-voice-1-custom), newest first. Works with a $0 balance.","guarded":false,"public":true,"annotations":{"title":"List voices","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},"argsSchema":{"type":"object","properties":{},"additionalProperties":false}},{"name":"create_voice","title":"Create voice","description":"Create a custom voice for spicy-voice-1-custom, returned as a vc_... id. Design: name plus a description of vocal qualities (gender, age range, pitch, pace, tone, accent); a description that names, references or imitates a real person is refused. Clone: name plus audio_url (one of the account's clips from list_audio or upload_audio, 10 MB max) plus consent: true, the user's attestation that the voice is their own or that they hold the speaker's written consent; it is stored with the voice. Never set consent without the user confirming it. One-off fee per voice, refunded if creation fails. Guarded: without approval_token it returns {error: \"approval_required\", summary, approval_token}; show the summary (it carries the cost), get the user's yes, call again with the token.","guarded":true,"public":false,"annotations":{"title":"Create voice","readOnlyHint":false,"destructiveHint":true,"idempotentHint":false,"openWorldHint":false},"argsSchema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":60,"description":"A label for the voice, e.g. Mara."},"description":{"description":"Design: the voice's qualities. Omit when cloning.","type":"string","maxLength":2048},"preview_text":{"description":"Design: a line spoken in the preview clip.","type":"string","maxLength":1024},"audio_url":{"description":"Clone: a clip url from list_audio. Omit when designing.","type":"string","format":"uri"},"consent":{"description":"Clone: must be true, the user's attestation that the voice is theirs or that the speaker gave written consent.","type":"boolean"},"language":{"description":"English (default), Chinese, German, Italian, Portuguese, Spanish, Japanese, Korean, French or Russian.","type":"string"},"user":{"description":"Your own id for the end user making this request.","type":"string","maxLength":128},"approval_token":{"description":"Approval token from a previous approval_required response, after the user said yes.","type":"string"}},"required":["name"],"additionalProperties":false}},{"name":"delete_voice","title":"Delete voice","description":"Delete a designed or cloned voice (vc_...) so it can no longer be spoken. Clips already generated with it are unaffected. Guarded: without approval_token it returns {error: \"approval_required\", summary, approval_token}; show the summary (it carries the cost), get the user's yes, call again with the token.","guarded":true,"public":false,"annotations":{"title":"Delete voice","readOnlyHint":false,"destructiveHint":true,"idempotentHint":false,"openWorldHint":false},"argsSchema":{"type":"object","properties":{"id":{"type":"string","description":"A vc_... id from list_voices."},"approval_token":{"description":"Approval token from a previous approval_required response, after the user said yes.","type":"string"}},"required":["id"],"additionalProperties":false}},{"name":"create_embeddings","title":"Create embeddings","description":"Embedding vectors for search, memory and recommendations, OpenAI-compatible. spicy-embed-1: text, input a string or up to 10 strings (8,192 tokens each), dimensions 64, 128, 256, 512, 768, 1024 (default), 1536 or 2048. spicy-embed-vision-1: text and images in one 768-dimension space, input items are strings, {text} or {image} where every image is one of the account's own generated images (list_images); use it for \"more like this\" over the account's outputs, tagging and near-duplicate detection. Returns {data: [{index, embedding}], usage, cost_usd}; billed per token (minimum charge applies). Vectors are long: ask for small dimensions (256) when you only need to compare a few items here. Guarded: without approval_token it returns {error: \"approval_required\", summary, approval_token}; show the summary (it carries the cost), get the user's yes, call again with the token.","guarded":true,"public":false,"annotations":{"title":"Create embeddings","readOnlyHint":false,"destructiveHint":true,"idempotentHint":false,"openWorldHint":false},"argsSchema":{"type":"object","properties":{"model":{"type":"string","description":"An embedding model id: spicy-embed-1 (text) or spicy-embed-vision-1 (text and images)."},"input":{"anyOf":[{"type":"string","minLength":1},{"minItems":1,"maxItems":10,"type":"array","items":{"anyOf":[{"type":"string","minLength":1},{"type":"object","properties":{"text":{"type":"string","minLength":1}},"required":["text"],"additionalProperties":false},{"type":"object","properties":{"image":{"type":"string","format":"uri"}},"required":["image"],"additionalProperties":false}]}}],"description":"A string, or up to 10 items. Objects ({text} or {image}) only on spicy-embed-vision-1."},"dimensions":{"description":"spicy-embed-1 only: 64, 128, 256, 512, 768, 1024 (default), 1536 or 2048.","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"encoding_format":{"description":"float (default) or base64 (little-endian float32).","type":"string","enum":["float","base64"]},"approval_token":{"description":"Approval token from a previous approval_required response, after the user said yes.","type":"string"}},"required":["model","input"],"additionalProperties":false}},{"name":"get_job","title":"Get job","description":"Status of a video task from generate_video or edit_video (queued, processing, finalizing, succeeded with output.video_url and usage, or failed and refunded) or of a logged image, chat, speech, transcription or embedding request by id.","guarded":false,"public":true,"annotations":{"title":"Get job","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},"argsSchema":{"type":"object","properties":{"id":{"type":"string","minLength":1,"description":"Task or request id, e.g. sj_..."}},"required":["id"],"additionalProperties":false}},{"name":"list_jobs","title":"List jobs","description":"Recent requests on the account (images, edits, video tasks including edits and motion transfer, chat, speech, voice creation, transcription, embeddings, live calls), newest first: id, type, model, status, cost_usd, output URL when finished, error when failed, and whether it came from the playground, the API or an agent.","guarded":false,"public":true,"annotations":{"title":"List jobs","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},"argsSchema":{"type":"object","properties":{"limit":{"description":"Default 20.","type":"integer","minimum":1,"maximum":100},"type":{"type":"string","enum":["image","image-edit","video","chat","speech","voice","transcription","embedding","realtime"]}},"additionalProperties":false}},{"name":"get_usage","title":"Get usage","description":"Spend and request counts over the last N days, totalled and broken down by model, type and source (playground, api, agent), with the error count. Reads the account's request log.","guarded":false,"public":true,"annotations":{"title":"Get usage","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},"argsSchema":{"type":"object","properties":{"days":{"description":"Default 7.","type":"integer","minimum":1,"maximum":90}},"additionalProperties":false}},{"name":"create_api_key","title":"Create API key","description":"Mint a new key for this account (plaintext returned once, stored hashed; at most 500 active keys). sandbox: true makes a key that answers everything from fixtures and bills nothing. Keys made with a sandbox key are always sandbox keys. Keep keys in environment variables, never in chat. Guarded: without approval_token it returns {error: \"approval_required\", summary, approval_token}; show the summary (it carries the cost), get the user's yes, call again with the token.","guarded":true,"public":false,"annotations":{"title":"Create API key","readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false},"argsSchema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":60,"description":"A label, e.g. Production or Agent."},"sandbox":{"description":"Fixture-only key, nothing billed. Default false.","type":"boolean"},"approval_token":{"description":"Approval token from a previous approval_required response, after the user said yes.","type":"string"}},"required":["name"],"additionalProperties":false}},{"name":"revoke_api_key","title":"Revoke API key","description":"Permanently revoke a key by id (the id is returned by create_api_key and shown on the API keys page). Applications using it stop working at once. Guarded: without approval_token it returns {error: \"approval_required\", summary, approval_token}; show the summary (it carries the cost), get the user's yes, call again with the token.","guarded":true,"public":false,"annotations":{"title":"Revoke API key","readOnlyHint":false,"destructiveHint":true,"idempotentHint":true,"openWorldHint":false},"argsSchema":{"type":"object","properties":{"id":{"type":"string","minLength":1,"description":"Key id (sha256 hash)."},"approval_token":{"description":"Approval token from a previous approval_required response, after the user said yes.","type":"string"}},"required":["id"],"additionalProperties":false}},{"name":"get_topup_link","title":"Get top-up link","description":"Checkout URL to add funds by card (hosted checkout) or crypto for a preset amount ($50, $100, $250, $500, $1000; minimum $50). The USER opens it and pays in the browser; never enter payment details yourself. The balance is credited by the payment webhook; call get_account afterwards. If it returns commitment_required, the user must first sign the one-page Customer Commitment Letter on the Billing page; an agent cannot sign it. Works with a $0 balance.","guarded":false,"public":true,"annotations":{"title":"Get top-up link","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},"argsSchema":{"type":"object","properties":{"amount_usd":{"description":"One of 50, 100, 250, 500, 1000. Default 50.","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"method":{"description":"Default card.","type":"string","enum":["card","crypto"]}},"additionalProperties":false}},{"name":"set_spend_limit","title":"Set spend limit","description":"Cap what this account can spend per calendar month (UTC), enforced at debit time on every surface: requests past the cap return 402 without charging. Lowering or setting a limit never needs approval; raising or removing one does. null removes the limit.","guarded":true,"public":false,"annotations":{"title":"Set spend limit","readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},"argsSchema":{"type":"object","properties":{"monthly_usd":{"anyOf":[{"type":"number","minimum":1,"maximum":1000000},{"type":"null"}],"description":"Monthly cap in USD, or null to remove it."},"approval_token":{"description":"Approval token from a previous approval_required response, after the user said yes.","type":"string"}},"required":["monthly_usd"],"additionalProperties":false}},{"name":"read_acceptable_use","title":"Read acceptable use policy","description":"The acceptable use policy as markdown: prohibited content (minors in any form, images or video of real people with or without consent, non-consensual scenarios and the rest), age-verification duties for the customer's product, enforcement. Read it before the first generation and tell the user what their product must do. Works with a $0 balance.","guarded":false,"public":true,"annotations":{"title":"Read acceptable use policy","readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},"argsSchema":{"type":"object","properties":{},"additionalProperties":false}}]}