Building

Generating media

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#

ModelImagesSpeechVideo
upfyn-balaNoNoNo
upfyn-yuvaYesYesNo
upfyn-rishiYesYesYes

Video is Rishi only

Naming any other model on a video route returns 400 model_generation_unsupported. Bala generates nothing at all.

Images#

POST /v1/images/generations
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)
FieldTypeRequiredNotes
promptstring YesUp to 32,000 characters.
sizestring—auto, 512x512, 1024x1024, 1024x1536, 1536x1024, 1024x1792, 1792x1024.
qualitystring—auto, low, medium, high, standard, hd.
ninteger—1 to 10.
response_formatstring—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#

POST /v1/audio/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")
FieldTypeRequiredNotes
inputstring YesUp to 4,096 characters.
voicestring—A voice id.
speednumber—0.25 to 4.
response_formatstring—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.

POST /v1/videos
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"
}
202 Accepted
{
"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
}
FieldTypeRequiredNotes
promptstring—Required unless you send input_reference.
secondsinteger—4 to 8.
sizestring—1280x720, 720x1280, 720x720, 1024x1792, 1792x1024.

Collecting the result

  • Poll poll_url — statuses are queued, 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 signed url, an expires_at and a file_id.
  • Asking for the content before it is ready returns 409 video_not_ready.
  • cancel_url stops 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.

Prefer: respond-async
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-async header.
  • "async": true in the body.
  • "background": true in 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.