Images, speech and video. The endpoints follow the OpenAI shapes, so the SDK methods you already know work — you just name an Upfyn model.
Which model generates what#
| Model | Images | Speech | Video |
|---|---|---|---|
upfyn-bala | No | No | No |
upfyn-yuva | Yes | Yes | No |
upfyn-rishi | Yes | Yes | Yes |
Video is Rishi only
Naming any other model on a video route returns400 model_generation_unsupported. Bala generates nothing at all.Images#
image = client.images.generate(model="upfyn-yuva",prompt="A wide shot of a rain-soaked Jaipur street at dusk, warm streetlights",size="1024x1024",n=1,)print(image.data[0].url)
| Field | Type | Required | Notes |
|---|---|---|---|
| prompt | string | Yes | Up to 32,000 characters. |
| size | string | — | auto, 512x512, 1024x1024, 1024x1536, 1536x1024, 1024x1792, 1792x1024. |
| quality | string | — | auto, low, medium, high, standard, hd. |
| n | integer | — | 1 to 10. |
| response_format | string | — | url or b64_json. |
Editing an image
POST /v1/images/edits takes an uploaded image or an image_file_id you already own. One of the two is required. Passing a file id to the generations endpoint is rejected rather than ignored — edits and generation are separate routes on purpose.
Speech#
audio = client.audio.speech.create(model="upfyn-yuva",voice="alloy",input="Your order has shipped and arrives on Thursday.",response_format="mp3",)audio.stream_to_file("notice.mp3")
| Field | Type | Required | Notes |
|---|---|---|---|
| input | string | Yes | Up to 4,096 characters. |
| voice | string | — | A voice id. |
| speed | number | — | 0.25 to 4. |
| response_format | string | — | mp3, opus, aac, flac, wav, pcm. |
Voice cloning is refused, not ignored
reference_audio, voice_sample and similar parameters return 400 unsupported_parameter. We do not clone voices from a sample, and failing loudly is better than accepting the request and quietly using a stock voice.Video#
Video is always asynchronous — it returns a task you poll.
curl https://ai.upfyn.com/v1/videos \-H "Authorization: Bearer $UPFYN_API_KEY" \-H "Content-Type: application/json" \-d {"model": "upfyn-rishi","prompt": "A paper plane crossing a sunlit office, slow motion","seconds": 6,"size": "1280x720"}
{"id": "task_8f21...","object": "video.generation.job","status": "queued","poll_url": "https://ai.upfyn.com/v1/tasks/task_8f21...","events_url": "https://ai.upfyn.com/v1/tasks/task_8f21.../events","cancel_url": "https://ai.upfyn.com/v1/tasks/task_8f21.../cancel","credits_reserved": 75.62}
| Field | Type | Required | Notes |
|---|---|---|---|
| prompt | string | — | Required unless you send input_reference. |
| seconds | integer | — | 4 to 8. |
| size | string | — | 1280x720, 720x1280, 720x720, 1024x1792, 1792x1024. |
Collecting the result
- Poll
poll_url— statuses arequeued,running,submitted,retrying,succeeded,failed,cancelled. - Or subscribe to
events_url, an SSE stream, instead of polling in a loop. - On success the task carries
output[]with a signedurl, anexpires_atand afile_id. - Asking for the content before it is ready returns
409 video_not_ready. cancel_urlstops a job you no longer want and releases the reservation.
Making images and speech async too#
Image and speech calls run synchronously by default. For long jobs, ask for a task instead — any of these three works.
curl https://ai.upfyn.com/v1/images/generations \-H "Authorization: Bearer $UPFYN_API_KEY" \-H "Prefer: respond-async" \-H "Content-Type: application/json" \-d '{ "model": "upfyn-yuva", "prompt": "A quiet library at night" }'
- The
Prefer: respond-asyncheader. "async": truein the body."background": truein the body.
You get a 202 with the same task shape as video, plus a Location header and Retry-After.
Where the files go#
Everything generated is stored against your account and appears in Files.
Generated media is kept for 30 days
After that it is removed automatically. Download anything you need to keep, or store the bytes yourself — signed URLs expire sooner than the file does.