Generate AI videos and images (text-to-video, image-to-video, speech-to-video, animate, text-to-image, image-to-image), bring your own LoRA models, and generate AI product/model photography for e-commerce (Image Studio) via the Phosor AI platform.
For detailed API endpoints, parameters, pricing, and limits, see references/api.md.
Set your API key:
bash
export PHOSOR_API_KEY="your-api-key-here"
Get an API key at phosor.ai → Settings → API Keys.
The CLI script is at scripts/phosor_client.py. All commands output JSON to stdout.
The client talks to https://phosor.ai by default, over HTTPS. Get your key from phosor.ai → Settings → API Keys.
bash
python3 scripts/phosor_client.py --api-key <your-key> check-key
To point the same client at another Phosor endpoint, pass --base-url <url> (or set PHOSOR_BASE_URL).
HTTPS is required; plain http:// is accepted only for localhost, or when you explicitly add --allow-http.
Note: studio-analyze / studio-suite --image-url must be the full https S3 URL returned by upload-image, not the bare S3 key path — a bare key errors with unsupported URL scheme.
H3 is duration-based, not frame-based: it ignores --num-frames / --fps (output is
always 24fps) and bills per output second. Pick the frame size with
--resolution-tier + --aspect instead of --width/--height.
bash
python3 scripts/phosor_client.py submit "A cat walking on a beach at sunset" \
--model minimax/h3/text-to-video \
--resolution-tier 768p --aspect 16:9 --duration 5
bash
# Upload first (direct URLs are not accepted); then submit with the returned s3_key
python3 scripts/phosor_client.py upload-image /path/to/first-frame.jpg
python3 scripts/phosor_client.py submit "The person starts dancing" \
--model minimax/h3/image-to-video \
--image-url "images/img-xxx.jpg" \
--end-image-url "images/img-yyy.jpg" \
--resolution-tier 480p --aspect 9:16 --duration 6
--end-image-url is optional and pins the closing frame.
Feed reference images, videos, and audio together; refer to them positionally in
the prompt as <Picture 1>, <Picture 2>, … At least one of --reference-image-urls /
--reference-video-urls is required.
bash
python3 scripts/phosor_client.py submit \
"Use <Picture 1> and <Picture 2> as sequential keyframes; slow push-in, cinematic 35mm look." \
--model minimax/h3/reference-to-video \
--reference-image-urls "images/a.jpg,images/b.jpg" \
--reference-audio-urls "audio/voice.mp3" \
--resolution-tier 768p --aspect 16:9 --duration 5
Reference inputs are billed on top of the output — see Ref2VA Pricing below, and Ref2VA Reference Limits for the caps (the same at 480p and 768p).
Wan is frame-based. Add /turbo to the model id for the fast, ~3x cheaper variant
(it ignores --steps / --guidance).
bash
# Submit T2V job (480p, 81 frames, 16fps)
python3 scripts/phosor_client.py submit "A cat walking on a beach at sunset" \
--width 854 --height 480 --num-frames 81 --fps 16
# Check status
python3 scripts/phosor_client.py status <request_id>
# Get result (video URL)
python3 scripts/phosor_client.py result <request_id>
Two-step flow: upload image first, then submit with the returned S3 key.
bash
# Step 1: Upload image
python3 scripts/phosor_client.py upload-image /path/to/photo.jpg
# Returns: {"file_id": "img-xxx", "s3_key": "images/img-xxx.jpg", ...}
# Step 2: Submit I2V job using the s3_key as image_url
python3 scripts/phosor_client.py submit "The person in the photo starts dancing" \
--image-url "images/img-xxx.jpg" --width 854 --height 480
bash
# Submit T2I job (1024x1024, default settings)
python3 scripts/phosor_client.py submit "A futuristic city skyline at dusk" \
--model qwen-image/v2512/text-to-image --width 1024 --height 1024
# Generate multiple images at once (1-4)
python3 scripts/phosor_client.py submit "A futuristic city skyline at dusk" \
--model z-image/turbo/text-to-image --width 1024 --height 768 --num-images 4
# Check status and get result (image URL)
python3 scripts/phosor_client.py status <request_id>
python3 scripts/phosor_client.py result <request_id>
# Returns: {"data": {"image": {"url": "..."}, "seed": 12345}, ...}
Two-step flow: upload source image first, then submit with the returned S3 key.
bash
# Step 1: Upload source image
python3 scripts/phosor_client.py upload-image /path/to/photo.jpg
# Returns: {"file_id": "img-xxx", "s3_key": "images/img-xxx.jpg", ...}
# Step 2: Submit I2I job using the s3_key as image_url
python3 scripts/phosor_client.py submit "Transform into oil painting style" \
--model z-image/turbo/image-to-image --image-url "images/img-xxx.jpg" \
--width 1024 --height 1024 --strength 0.7
Two-step flow: upload reference images first, then submit with S3 keys as image_urls.
bash
# Step 1: Upload reference images (up to 3)
python3 scripts/phosor_client.py upload-image /path/to/ref1.jpg
python3 scripts/phosor_client.py upload-image /path/to/ref2.jpg
# Step 2: Submit image-edit job
python3 scripts/phosor_client.py submit \
"The girl in image 1 is wearing the outfit from image 2" \
--model qwen-image/v2511/image-edit \
--image-urls '["images/img-ref1.jpg","images/img-ref2.jpg"]' \
--width 1024 --height 1024
# Turbo variant (faster, Lightning LoRA built-in)
python3 scripts/phosor_client.py submit \
"The girl in image 1 is wearing the outfit from image 2" \
--model qwen-image/v2511/image-edit \
--image-urls '["images/img-ref1.jpg","images/img-ref2.jpg"]'
Two-step flow: upload both audio and reference image first, then submit with the returned S3 keys.
bash
# Step 1: Upload reference image
python3 scripts/phosor_client.py upload-image /path/to/face.jpg
# Returns: {"file_id": "img-xxx", "s3_key": "images/img-xxx.jpg", ...}
# Step 2: Submit S2V job using the s3_key as image_url and audio URL as audio_url
python3 scripts/phosor_client.py submit "A person speaking naturally" \
--model wan/v2.2-a14b/speech-to-video \
--image-url "images/img-xxx.jpg" --audio-url "https://example.com/speech.wav" \
--width 854 --height 480
Two-step flow: upload both source video and reference image first, then submit.
bash
# Step 1: Upload reference image
python3 scripts/phosor_client.py upload-image /path/to/character.jpg
# Returns: {"file_id": "img-xxx", "s3_key": "images/img-xxx.jpg", ...}
# Step 2: Submit Animate job using the s3_key as image_url and video URL as video_url
python3 scripts/phosor_client.py submit "The character performs the dance moves" \
--model wan/v2.2-a14b/animate \
--image-url "images/img-xxx.jpg" --video-url "https://example.com/dance.mp4" \
--width 854 --height 480
Its own resolution set and always exactly 1 image ( --num-images is ignored).
bash
python3 scripts/phosor_client.py submit "A ceramic mug on a linen cloth, soft window light" \
--model openai/gpt-image-2/text-to-image --width 1024 --height 1024
Allowed sizes: 1024×1024 only. Any other size is rejected with 400 Invalid parameters.
It ignores --num-images, --steps and --guidance — the only parameters it accepts are
prompt, model, width, height and seed.
FLUX.2-dev has its own resolution whitelist and is fixed at 1 image per request
( --num-images and --steps are ignored).
bash
python3 scripts/phosor_client.py submit "Editorial product photo, soft window light" \
--model flux2/dev/text-to-image --width 2048 --height 1536
bash
python3 scripts/phosor_client.py upload-image /path/to/source.jpg
python3 scripts/phosor_client.py submit "Replace the background with a marble surface" \
--model flux2/dev/image-edit --image-url "images/img-xxx.jpg" \
--width 1024 --height 1024
TTS takes text (not a prompt) and is billed per character with a minimum charge.
bash
python3 scripts/phosor_client.py submit-tts "Hello, welcome to Phosor AI." \
--speaker Sohee --language English
Video LoRA requires two .safetensors files (high_noise + low_noise). Image LoRA requires a single .safetensors file.
bash
# Video LoRA: upload two .safetensors files
python3 scripts/phosor_client.py upload-lora high_noise.safetensors low_noise.safetensors --name "My Style"
# Image LoRA: import single .safetensors file via URL
python3 scripts/phosor_client.py import-lora \
"https://example.com/my_lora.safetensors" \
--name "My Image Style"
# Video LoRA: import two files via URL
python3 scripts/phosor_client.py import-lora \
"https://example.com/high_noise.safetensors" \
"https://example.com/low_noise.safetensors" \
--name "My Video Style"
# Check status, then use
python3 scripts/phosor_client.py lora-status <lora_id>
python3 scripts/phosor_client.py submit "A person walking" --lora-id <lora_id>
CommandDescriptionKey Argumentscheck-keyValidate API key—submitSubmit inference job (T2V/I2V/S2V/Animate/T2I/I2I)prompt, --width, --height, --num-frames, --fps, --steps, --guidance, --image-url, --audio-url, --video-url, --lora-id, --lora-scale, --loras, --seed, --negative-prompt, --model, --num-images, --strength, --output-format``statusGet job statusrequest_id``resultGet job result (video or image URL)request_id``pollPoll all pending jobs—listList locally tracked pending jobs—historyGet job history--limit``upload-imageUpload image for I2V or I2Ifile``import-imageImport image from URLurl, --filename``upload-loraUpload LoRA (two .safetensors for video)high_noise_file, low_noise_file, --name``import-loraImport LoRA from URLs (one or two files)high_noise_url, [low_noise_url], --name``lorasList LoRA models--limit, --offset``lora-statusGet LoRA upload/import statuslora_id``save-loraActivate a LoRA (extends expiry to 7 days)lora_id, --name``delete-loraDelete a LoRA modellora_id``submit-ttsSubmit a text-to-speech job (Qwen3-TTS) — keys off text, not a prompttext, --speaker, --language, --seed, --temperature, --top-p, --top-k, --repetition-penalty``modelsList available video/image models (static offline reference)—studio-featuresList Image Studio endpoints, fields, billing (static offline reference)—studio-pricingGet live Image Studio pricing—studio-analyzeAI-analyze a product/garment image or reference URL (freemium)--target agent|product|model|reference, --image-url, --url, --prompt, --language``studio-layoutsList the layout template library (query, then select) — static asset on the web front (phosor.ai), not a /api/v1 endpoint; needs no key; on a self-hosted deployment set PHOSOR_WEB_BASE_URL if the web front is not the gateway host--module (product|clothing), --type (selling_point|aplus|white_bg|scene|closeup|size_chart)studio-suiteGenerate a product image suite--image-url, --layout-types, --count-per-type, --custom-suggestions, --template-ids (ids from studio-layouts, auto-expanded to custom_suggestions like the UI's manual pick — use this to get text-callout selling-point / A+ layouts and model templates), --product-info, --aspect-ratio, --gen-language, --model, --same-style-reference``studio-clothing-suiteGenerate a model/garment image suite--image-urls, --main-image-types, --aplus-types, --product-info, --brand-config, --aspect-ratio, --gen-language, --model, --same-style-reference``studio-statusGet Image Studio job status (separate id space, same request_id key)request_id``studio-cancelCancel a running generation — queued images refunded, already-generating ones chargedrequest_id``studio-my-worksList past Image Studio generations--task-type, --limit, --offset``studio-callGeneric call for any other Image Studio endpoint (remove-bg, replace, inpaint, erase, handheld, translate, outpaint, recolor, enhance, upscale, scene-compose, scene-variation, real-model-swap, mannequin-swap, model-scene-swap, ai-outfit, pose-variation, ai-wearable)method, path, --json
Image Studio is a separate product surface for e-commerce sellers — AI product photography and model/clothing photography — reached through the same gateway and API key as video/LoRA, under the /api/v1/image-studio prefix. It has its own async namespace - the same key name request_id, but a separate id space: an Image Studio request_id is not valid on /api/v1/inference/status/... and vice versa - and its own pricing (flat per-image rate + freemium analyze quota, not per-frame). Full endpoint/parameter reference: references/api.md.
bash
# 1. Upload the product photo
python3 scripts/phosor_client.py upload-image /path/to/product.jpg
# Returns: {"file_id": "img-xxx", "s3_key": "images/img-xxx.jpg", ...}
# 2. (Optional) AI-analyze it first for richer generation context
python3 scripts/phosor_client.py studio-analyze --target product --image-url "images/img-xxx.jpg"
# 3. Generate a product image suite
python3 scripts/phosor_client.py studio-suite --image-url "images/img-xxx.jpg" \
--layout-types "white_background,lifestyle_scene" --count-per-type 2
# 4. Poll for the result
python3 scripts/phosor_client.py studio-status <request_id>
bash
python3 scripts/phosor_client.py upload-image /path/to/garment.jpg
python3 scripts/phosor_client.py studio-clothing-suite \
--image-urls "images/img-xxx.jpg" \
--main-image-types '{"model_shot":2,"selling_point":1}' \
--aplus-types '{"standard_aplus":1}'
python3 scripts/phosor_client.py studio-status <request_id>
The long tail of single-purpose editing endpoints doesn't get a dedicated subcommand — use studio-call with the exact field names from references/api.md:
bash
python3 scripts/phosor_client.py studio-call POST /product/remove-bg \
--json '{"image_url": "images/img-xxx.jpg", "count": 2}'
python3 scripts/phosor_client.py studio-status <request_id>
X-API-Key ( PHOSOR_API_KEY), including GET /pricing — there is no unauthenticated endpoint under this prefix.{"request_id": "...", "status": "pending"}; poll studio-status <request_id> until status is "done", "error" or "cancelled". Earlier revisions of this skill said the key was job_id - it is not, and reading it yields undefined. Image Studio ids live in a separate id space from video/LoRA: poll/ status/ result will not accept an Image Studio request_id.POST /jobs/{request_id}/cancel stops a running generation. Images still queued are refunded; images already generating are charged and cannot be stopped; images already delivered bill once through the normal path. The response reports the split as refunded_queued, charged_running and already_done, and the task then polls as status: "cancelled" - not an error.studio-pricing for the live rate. Partial success (e.g. 3 of 5 images) bills only the successes.agent/analyze, product/analyze, model/analyze share a daily free quota before per-call billing kicks in.model_attrs matters for model-photography endpoints (real-model-swap, mannequin-swap, ai-outfit, ai-wearable) — pass {gender, age_group, ethnicity, skin_tone, hair_color} explicitly; it is not reliably inferred from the source image alone.studio-features for the full offline endpoint/field catalog without leaving the terminal.PresetWidth × HeightMax Frames (turbo)Max Frames (standard)480p landscape854 × 480161161480p portrait480 × 854161161720p landscape1280 × 720161161720p portrait720 × 12801611611080p landscape1920 × 1080153811080p portrait1080 × 192015381
Standard (non-turbo) mode: 1080p is capped at 81 frames due to generation time limits.
PresetWidth x HeightMax Frames480p landscape854 x 480161480p portrait480 x 854161512p square512 x 512161720p landscape1280 x 720161720p portrait720 x 1280161
PresetWidth × HeightSquare small512 × 512Square1024 × 1024Landscape1024 × 768Portrait768 × 1024Wide landscape1280 × 768Tall portrait768 × 1280
--resolution-tier + --aspect)#Tier16:94:31:13:49:16480p832 × 480640 × 480480 × 480480 × 640480 × 832768p1344 × 7681024 × 768768 × 768768 × 1024768 × 1344
duration is 4–15 seconds (default 5). Output FPS is fixed at 24 and
frames_per_second is ignored.
minimax/h3/reference-to-video)#Caps differ per tier, and the image cap is higher when you send only images:
Limit480p768pReference images (with videos/audio present)55Reference images (images only)99Reference videos33Reference audios33LimitValueTotal reference video length15.1 s (across all reference videos)Reference video FPS ceiling24Reference audio length10 s eachReference image longest edge2048 pxReference image aspect ratio≤ 4.0
Width × Height2048 × 1536 · 1536 × 20482048 × 1152 · 1152 × 20482048 × 2048 · 1024 × 1024
FLUX.2-dev does not accept the general image resolution list above, and always returns exactly 1 image.
Frames must follow 1 + 4*k where k >= 1 (e.g. 5, 9, 13, ... 81, 85, ...). Server auto-aligns down.
ParameterDefaultRangeframes_per_second164–60num_inference_steps44–40guidance_scale1.01.0–10.0
ParameterDefaultRangeNotesnum_images11–4Number of images to generatenum_inference_stepsvaries1–4 (z-image turbo), 1–40 (qwen-image)Model-dependent maxguidance_scalevaries1.0–20.0—strength—0.0–1.0Image-to-image only: how much to transform the sourceoutput_formatpngpng, jpegOutput file format
Model API inference jobs run concurrently up to a per-account cap; over it the submit
returns 429 and you retry after a job finishes.
TierPlanConcurrent Model API jobsFree—11Starter22Standard43Pro8
Image Studio runs on its own pool and does not consume this quota — a suite and a video generation can run at the same time.
bash
python3 scripts/phosor_client.py submit "A person dancing" \
--loras '[{"lora_id": "lora-abc", "lora_scale": 0.8}, {"lora_id": "lora-def", "lora_scale": 0.5}]'
Files must be uploaded before use — direct URLs are NOT supported in submit --image-url:
upload-image / import-image → returns s3_key → use as --image-urlupload-lora / import-lora → returns lora_id → use as --lora-idtext
PENDING → PROCESSING → COMPLETED / FAILED
The poll command checks all locally-tracked pending jobs and removes completed/failed ones.
Per output second, regardless of clip length:
Model480p USD/sec768p USD/sec480p credits/sec768p credits/sectext-to-video, image-to-video$0.0045$0.0120.0450.12reference-to-video$0.0045$0.0120.0450.12
Output plus reference inputs:
text
total = duration x base_rate
+ max(0, reference_images - 5) x image_rate # first 5 images free
+ reference_video_seconds x base_rate # reference audio is free
ModelTierBase USD/secReference imageReference audioReference video USD/secreference-to-video480p$0.0045first 5 free, then $0.0075 eachfree$0.0045reference-to-video768p$0.012first 5 free, then $0.0075 eachfree$0.012
Example — reference-to-video, 5s at 768p with 4 reference images: 5 x $0.012 = 0.6 credits.
5s at 480p with 9 images: 5 x $0.0045 + 4 x $0.0075 = 0.525 credits.
TierStandard+ LoRATurbo480p$0.0009375$0.00125$0.0003125512p$0.0013125$0.001625$0.0004375720p$0.001875$0.0021875$0.0006251080p$0.0025$0.003$0.0010938
LoRA multiplier on turbo: 1.2x.
TierCost480p$0.0009375512p$0.0013125720p$0.001875
TierCost480p$0.00125512p$0.00175720p$0.0025
ModelUSDCreditsGPT Image 2 T2I$0.030.3FLUX.2-dev T2I$0.0060.06FLUX.2-dev Image Edit$0.0120.12qwen-image T2I$0.0150.15qwen-image T2I + LoRA$0.0180.18qwen-image-edit$0.0030.03z-image turbo (T2I / I2I)$0.00250.025z-image turbo + LoRA$0.0030.03
Flat per image at every resolution. Multiply by num_images (1–4) where the model
supports it; FLUX.2-dev is fixed at 1 image per request.
ItemCostQwen3-TTS$0.00003 per characterMinimum charge$0.003 per request
Exchange rate: 10 credits = $1 USD. Credits pre-deducted, auto-refunded on failure.
Live rates: GET /api/v1/pricing/config — always prefer it over any table here.