{"openapi":"3.1.0","info":{"title":"Texel API","description":"\nThe Texel API processes video and audio on cloud GPUs. You send a JSON request\nthat points to your media. The API returns a job ID right away and does the work\nin the background.\n\n## What you can do\n\n- **Edit and transcode video.** `POST /v1/video_encoder/encode` runs an FFmpeg\n  filter graph with hardware-accelerated (NVENC) encoding. In one request you can\n  resize and reframe, trim and join clips, add overlays and watermarks, grade\n  color, add transitions, change speed, and mix audio. Many inputs and filters can\n  be chained together.\n- **Enhance video and audio with AI.** Eye contact correction (`maxine_cuda`) and\n  studio voice enhancement (`nvafx`) are available as encoder filters.\n- **Lip-sync video.** `POST /v1/lipsync/run_lipsync` matches a speaker's lip\n  movements to a new audio track.\n- **Track usage and costs.** `GET /v1/billing/usage` itemizes your jobs and charges.\n\n## How it works\n\n1. Authenticate every request with the `Authorization: Bearer <api_key>` header.\n2. Submit a job with input URLs that the API can read and an output URL that it can\n   write to, such as a signed upload URL.\n3. Check progress with the job's status endpoint, or add a `callback` URL to be\n   notified when the job finishes.\n4. Get the result from your output location.\n\nEach section has an **Examples** folder with ready-to-use requests. Video Processing\nexamples include a before-and-after preview.\n","version":"0.1.0"},"paths":{"/v1/lipsync/status/{job_id}":{"get":{"tags":["Status Checks API"],"summary":"Get lipsync job status","description":"Retrieve the current status, progress, and any error messages for a lipsync job.","operationId":"get_lipsync_status","security":[{"HTTPBearer":[]}],"parameters":[{"name":"job_id","in":"path","required":true,"schema":{"title":"Job Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/lipsync/get_url/{job_id}":{"get":{"tags":["Video Processing API"],"summary":"Get download URL for lipsync output","description":"Generate a signed URL to download the processed lipsync video output file.","operationId":"get_lipsync_url","security":[{"HTTPBearer":[]}],"parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/lipsync/run_lipsync":{"post":{"tags":["Video Processing API"],"summary":"Generate lip-synced video","description":"Synchronize lip movements in a video with provided audio using AI-powered lip sync technology. Submit video and audio inputs to generate a new video with accurate lip synchronization.","operationId":"run_lipsync","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LipsyncRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/video_encoder/status/{job_id}":{"get":{"tags":["Status Checks API"],"summary":"Check video processing job status by job ID","description":"Monitor the progress of video encoding, transcoding, and editing jobs using the unique job identifier. Returns current processing status (pending, processing, completed, failed), progress percentage, the current Pub/Sub unacknowledged-message backlog, and any error messages encountered during video processing operations.","operationId":"get_video_processing_status","security":[{"HTTPBearer":[]}],"parameters":[{"name":"job_id","in":"path","required":true,"schema":{"title":"Job Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/video_encoder/status_client_id/{client_id}":{"get":{"tags":["Status Checks API"],"summary":"Check video processing job status by client ID","description":"Retrieve the status of video processing jobs using your custom client identifier. Useful for tracking jobs when you've assigned your own reference ID during submission. Returns processing status, completion progress, and detailed error information if applicable.","operationId":"get_video_processing_status_by_client_id","security":[{"HTTPBearer":[]}],"parameters":[{"name":"client_id","in":"path","required":true,"schema":{"title":"Client Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/video_encoder/cancel/{task_id}":{"post":{"tags":["Video Processing API"],"summary":"Cancel a video processing task if not yet started","description":"Mark a video processing task as canceled using its task ID.","operationId":"cancel_video_processing_task","security":[{"HTTPBearer":[]}],"parameters":[{"name":"task_id","in":"path","required":true,"schema":{"title":"Task Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/video_encoder/encode":{"post":{"tags":["Video Processing API"],"summary":"Process videos with encoding, transcoding, and editing","description":"Submit a video processing job. The API downloads every URL in `inputs`, runs the `filters` graph with hardware-accelerated (NVENC) encoding, and uploads the result to `outputs[0].url`. It returns a `job_id` right away and processes the job asynchronously.\n\nAuthenticate with `Authorization: Bearer <api_key>` and send the body as JSON.\n\n## Inputs and outputs\n\n- Each input needs a URL the API can read and a `label` that filters refer to.\n- `outputs[0].url` must be writable, such as a signed upload URL.\n- Add a top-level `callback` URL to be notified when the job finishes.\n\n## Filters\n\n`filters` is a list of [FFmpeg filters](https://ffmpeg.org/ffmpeg-filters.html), plus Texel AI filters such as `maxine_cuda` (eye contact) and `nvafx` (studio voice). Each filter has:\n\n- `name`: the filter name.\n- `inputs`: labels of inputs or earlier filters. Use `label.video` or `label.audio` to pick a stream, and `label.video.N` for the Nth output of `split`.\n- `args` and `kwargs`: positional and named filter parameters.\n- `label`: a name that later filters can refer to.\n- `output`: `[\"video\"]` (default), `[\"audio\"]`, or `[\"all\"]`. The last video filter and the last audio filter become the output streams.\n\nSee **Examples** for ready-to-use requests, each with a before-and-after preview.\n\n## Job status\n\nPoll `GET /v1/video_encoder/status/{job_id}` until `job_status` is `success`, then download the result from your output location.","operationId":"process_video","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EncoderRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/billing/usage":{"get":{"tags":["Billing API"],"summary":"Get itemized public API usage and costs","description":"Public API customers only. Prices use each job's UTC posted timestamp.\n\nDates are start-inclusive and end-exclusive. Omit both for the previous\ncalendar month. Summary totals cover the full filtered period on every page.\nJobs are ordered by posted time and document ID. Only successful jobs are\ncharged; pending/unpriced amounts are null. Decimal values are JSON strings.\nUp to 50,000 jobs per request; narrow the period/service for larger reports.","operationId":"get_usage_v1_billing_usage_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"start_date","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"Inclusive UTC date; defaults to the previous month's start.","title":"Start Date"},"description":"Inclusive UTC date; defaults to the previous month's start."},{"name":"end_date","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"Exclusive UTC date; supply both dates or neither. Maximum span: 90 days.","title":"End Date"},"description":"Exclusive UTC date; supply both dates or neither. Maximum span: 90 days."},{"name":"service","in":"query","required":false,"schema":{"anyOf":[{"enum":["maxine","studio_voice","lipsync"],"type":"string"},{"type":"null"}],"title":"Service"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":1000,"minimum":1,"description":"Maximum job details per page. Totals cover the entire filtered period.","default":100,"title":"Limit"},"description":"Maximum job details per page. Totals cover the entire filtered period."},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"description":"Continuation token from this endpoint, using the same dates and service.","title":"Cursor"},"description":"Continuation token from this endpoint, using the same dates and service."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsageResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"AudioEncoderProperties":{"properties":{"bitrate":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bitrate"},"sample_rate":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Sample Rate"},"channels":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Channels","default":2},"quality":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Quality"}},"additionalProperties":true,"type":"object","title":"AudioEncoderProperties"},"EncoderRequest":{"properties":{"inputs":{"anyOf":[{"items":{"$ref":"#/components/schemas/URL"},"type":"array"},{"type":"null"}],"title":"Inputs"},"outputs":{"anyOf":[{"items":{"$ref":"#/components/schemas/URL"},"type":"array"},{"type":"null"}],"title":"Outputs"},"video_encoder_codec":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Video Encoder Codec","default":"hevc_nvenc"},"video_properties":{"anyOf":[{"$ref":"#/components/schemas/VideoEncoderProperties"},{"type":"null"}]},"audio_encoder_codec":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Audio Encoder Codec","default":"copy"},"audio_properties":{"anyOf":[{"$ref":"#/components/schemas/AudioEncoderProperties"},{"type":"null"}]},"file_format":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"File Format","default":"mp4"},"filters":{"anyOf":[{"items":{"$ref":"#/components/schemas/Filter"},"type":"array"},{"type":"null"}],"title":"Filters","default":[]},"kwargs":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Kwargs","default":{}},"callback":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Callback"},"client_job_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Job Id"},"input_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Input Url","description":"Use `inputs_url` instead","deprecated":true},"output_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Output Url","description":"Use `outputs_url` instead","deprecated":true},"content_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Content Type","description":"Use `inputs_url.content_type` instead","deprecated":true}},"additionalProperties":true,"type":"object","title":"EncoderRequest"},"Filter":{"properties":{"name":{"type":"string","title":"Name"},"inputs":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Inputs","default":[]},"output":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Output","default":["video"]},"args":{"anyOf":[{"items":{"anyOf":[{"type":"string"},{"type":"integer"},{"type":"number"}]},"type":"array"},{"type":"null"}],"title":"Args","default":[]},"kwargs":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Kwargs","default":{}},"label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Label","default":"filter"}},"additionalProperties":true,"type":"object","required":["name"],"title":"Filter"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"LipsyncRequest":{"properties":{"inputs":{"items":{"$ref":"#/components/schemas/URL"},"type":"array","title":"Inputs"},"outputs":{"items":{"$ref":"#/components/schemas/URL"},"type":"array","title":"Outputs","default":[]},"file_format":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"File Format","default":"mp4"},"callback":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Callback"},"audio_length":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Audio Length","default":1000},"high_res":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"High Res","default":false},"params":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Params","default":{}}},"additionalProperties":true,"type":"object","required":["inputs"],"title":"LipsyncRequest"},"ServiceSummary":{"properties":{"total_requests":{"type":"integer","title":"Total Requests"},"successful_requests":{"type":"integer","title":"Successful Requests"},"pending_requests":{"type":"integer","title":"Pending Requests"},"failed_requests":{"type":"integer","title":"Failed Requests"},"unpriced_success_requests":{"type":"integer","title":"Unpriced Success Requests"},"missing_pricing_requests":{"type":"integer","title":"Missing Pricing Requests"},"missing_length_requests":{"type":"integer","title":"Missing Length Requests"},"actual_seconds":{"type":"string","title":"Actual Seconds"},"billable_seconds":{"type":"string","title":"Billable Seconds"},"priced_cost":{"type":"string","title":"Priced Cost","description":"USD subtotal for priced successful usage, rounded once to cents. Unpriced jobs are excluded."},"complete":{"type":"boolean","title":"Complete","description":"False when matching jobs are pending or unpriced; not a guarantee of historical retention."},"service":{"type":"string","title":"Service"}},"type":"object","required":["total_requests","successful_requests","pending_requests","failed_requests","unpriced_success_requests","missing_pricing_requests","missing_length_requests","actual_seconds","billable_seconds","priced_cost","complete","service"],"title":"ServiceSummary"},"URL":{"properties":{"url":{"type":"string","title":"Url"},"content_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Content Type","default":""},"label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Label","default":"url"},"streams":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Streams","default":["video","audio"]}},"additionalProperties":true,"type":"object","required":["url"],"title":"URL"},"UsageItem":{"properties":{"job_id":{"type":"string","title":"Job Id"},"service":{"type":"string","title":"Service"},"posted_at":{"type":"string","format":"date-time","title":"Posted At"},"status":{"type":"string","title":"Status"},"billing_status":{"type":"string","enum":["charged","not_billable","pending","unpriced"],"title":"Billing Status"},"actual_seconds":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Actual Seconds"},"billable_seconds":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Billable Seconds"},"rate_per_minute":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rate Per Minute"},"minimum_seconds":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Minimum Seconds"},"amount":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Amount","description":"Unrounded USD charge; null for pending or unpriced usage. Summaries round after aggregation."},"unpriced_reasons":{"items":{"type":"string"},"type":"array","title":"Unpriced Reasons"}},"type":"object","required":["job_id","service","posted_at","status","billing_status","actual_seconds","billable_seconds","rate_per_minute","minimum_seconds","amount","unpriced_reasons"],"title":"UsageItem"},"UsageResponse":{"properties":{"account_id":{"type":"string","title":"Account Id"},"currency":{"type":"string","const":"USD","title":"Currency","default":"USD"},"start_date":{"type":"string","format":"date","title":"Start Date"},"end_date":{"type":"string","format":"date","title":"End Date"},"generated_at":{"type":"string","format":"date-time","title":"Generated At"},"provisional":{"type":"boolean","title":"Provisional","default":true},"covered_services":{"items":{"type":"string"},"type":"array","title":"Covered Services"},"retention_days":{"type":"integer","title":"Retention Days","default":90},"summary":{"$ref":"#/components/schemas/UsageSummary"},"services":{"items":{"$ref":"#/components/schemas/ServiceSummary"},"type":"array","title":"Services"},"items":{"items":{"$ref":"#/components/schemas/UsageItem"},"type":"array","title":"Items"},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"},"warnings":{"items":{"type":"string"},"type":"array","title":"Warnings"}},"type":"object","required":["account_id","start_date","end_date","generated_at","covered_services","summary","services","items","next_cursor","warnings"],"title":"UsageResponse"},"UsageSummary":{"properties":{"total_requests":{"type":"integer","title":"Total Requests"},"successful_requests":{"type":"integer","title":"Successful Requests"},"pending_requests":{"type":"integer","title":"Pending Requests"},"failed_requests":{"type":"integer","title":"Failed Requests"},"unpriced_success_requests":{"type":"integer","title":"Unpriced Success Requests"},"missing_pricing_requests":{"type":"integer","title":"Missing Pricing Requests"},"missing_length_requests":{"type":"integer","title":"Missing Length Requests"},"actual_seconds":{"type":"string","title":"Actual Seconds"},"billable_seconds":{"type":"string","title":"Billable Seconds"},"priced_cost":{"type":"string","title":"Priced Cost","description":"USD subtotal for priced successful usage, rounded once to cents. Unpriced jobs are excluded."},"complete":{"type":"boolean","title":"Complete","description":"False when matching jobs are pending or unpriced; not a guarantee of historical retention."}},"type":"object","required":["total_requests","successful_requests","pending_requests","failed_requests","unpriced_success_requests","missing_pricing_requests","missing_length_requests","actual_seconds","billable_seconds","priced_cost","complete"],"title":"UsageSummary"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"VideoEncoderProperties":{"properties":{"fps":{"anyOf":[{"type":"number","maximum":240.0,"minimum":1.0},{"type":"null"}],"title":"Fps"},"bitrate":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bitrate"},"preset":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Preset","default":"p4"},"tune":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tune","default":"hq"}},"additionalProperties":true,"type":"object","title":"VideoEncoderProperties"}},"securitySchemes":{"HTTPBearer":{"type":"http","scheme":"bearer"}}},"tags":[{"name":"Video Processing API","x-displayName":"API","description":"Submit, cancel, and download video jobs."},{"name":"Video Processing Examples","x-displayName":"Examples","description":"Ready-to-use request bodies for `POST /v1/video_encoder/encode`, each showing the original video next to the API's output."},{"name":"Billing API","x-displayName":"API","description":"Usage and costs for your API key."},{"name":"Billing Examples","x-displayName":"Examples","description":"Common usage and cost queries."},{"name":"Status Checks API","x-displayName":"API","description":"Track the progress of submitted jobs."},{"name":"Status Checks Examples","x-displayName":"Examples","description":"Checking, waiting for, and debugging jobs."}],"x-tagGroups":[{"name":"Video Processing","tags":["Video Processing API","Video Processing Examples"]},{"name":"Billing","tags":["Billing API","Billing Examples"]},{"name":"Status Checks","tags":["Status Checks API","Status Checks Examples"]}],"webhooks":{"video-eye-contact":{"post":{"tags":["Video Processing Examples"],"summary":"Eye contact","description":"![Eye contact: before and after](https://storage.texel.ai/api_samples/eye_contact.gif)\n\nMake the person in your video appear to look toward the camera with the `maxine_cuda` filter, which wraps the NVIDIA Maxine eye gaze model. `inputs[0].label` names the source `stream1`, and the filter's `inputs` list refers to that label.\n\nPass any of these parameters in the filter's `kwargs`. Omitted parameters use the defaults. Ranges are inclusive.\n\n| Parameter | Default | Range / allowed values |\n| --- | --- | --- |\n| `eye_size` | `3` | `[2, 6]` |\n| `temporal` | `-1` (on) | `-1` (on), `0` (off) |\n| `detect_closure` | `1` | `[0, 1]` |\n| `look_away` | `0` | `[0, 1]` |\n| `look_away_offset_max` | `5` | `[1, 10]` |\n| `look_away_interval_min` | `100` | `[1, 600]` |\n| `look_away_interval_range` | `250` | `[1, 600]` |\n| `gaze_pitch_threshold_low` | `20.0` | `[10.0, 35.0]` |\n| `gaze_yaw_threshold_low` | `20.0` | `[10.0, 35.0]` |\n| `head_pitch_threshold_low` | `15.0` | `[10.0, 35.0]` |\n| `head_yaw_threshold_low` | `25.0` | `[10.0, 35.0]` |\n| `gaze_pitch_threshold_high` | `30.0` | `[10.0, 35.0]` |\n| `gaze_yaw_threshold_high` | `30.0` | `[10.0, 35.0]` |\n| `head_pitch_threshold_high` | `25.0` | `[10.0, 35.0]` |\n| `head_yaw_threshold_high` | `30.0` | `[10.0, 35.0]` |\n\nSend this body to `POST /v1/video_encoder/encode`, replacing every `url_*` placeholder with your own URLs.","operationId":"example_video_eye_contact","requestBody":{"content":{"application/json":{"example":{"inputs":[{"label":"stream1","url":"url_input"}],"outputs":[{"url":"url_output"}],"filters":[{"inputs":["stream1"],"name":"maxine_cuda","kwargs":{"eye_size":3}}]}}}},"responses":{"201":{"description":"Job created. The response contains `job_id` and `status`."}}}},"video-resize-h264":{"post":{"tags":["Video Processing Examples"],"summary":"Resize to 720p H.264","description":"![Resize to 720p H.264: before and after](https://storage.texel.ai/api_samples/resize_h264.gif)\n\nConvert any video to a 1280x720 H.264 MP4 at 30 fps and 4 Mbps. `scale` fits the video inside 1280x720 without stretching it, and `pad` centers it on black bars.\n\nSend this body to `POST /v1/video_encoder/encode`, replacing every `url_*` placeholder with your own URLs.","operationId":"example_video_resize_h264","requestBody":{"content":{"application/json":{"example":{"inputs":[{"label":"video","url":"url_input"}],"outputs":[{"url":"url_output"}],"video_encoder_codec":"h264_nvenc","video_properties":{"fps":30,"bitrate":"4M"},"filters":[{"name":"scale","inputs":["video.video"],"label":"fit","args":[1280,720],"kwargs":{"force_original_aspect_ratio":"decrease"}},{"name":"pad","inputs":["fit"],"label":"letterbox","args":[1280,720,"(ow-iw)/2","(oh-ih)/2","black"]}]}}}},"responses":{"201":{"description":"Job created. The response contains `job_id` and `status`."}}}},"video-vertical-9x16":{"post":{"tags":["Video Processing Examples"],"summary":"Landscape to vertical 9:16","description":"![Landscape to vertical 9:16: before and after](https://storage.texel.ai/api_samples/vertical_9x16.gif)\n\nTurn a landscape video into a 1080x1920 vertical video for Shorts, Reels or TikTok. `scale` fills the frame and `crop` keeps the center.\n\nSend this body to `POST /v1/video_encoder/encode`, replacing every `url_*` placeholder with your own URLs.","operationId":"example_video_vertical_9x16","requestBody":{"content":{"application/json":{"example":{"inputs":[{"label":"video","url":"url_input"}],"outputs":[{"url":"url_output"}],"filters":[{"name":"scale","inputs":["video.video"],"label":"cover","args":[1080,1920],"kwargs":{"force_original_aspect_ratio":"increase"}},{"name":"crop","inputs":["cover"],"label":"vertical","args":[1080,1920]}]}}}},"responses":{"201":{"description":"Job created. The response contains `job_id` and `status`."}}}},"video-trim-concat":{"post":{"tags":["Video Processing Examples"],"summary":"Trim and join two clips","description":"![Trim and join two clips: before and after](https://storage.texel.ai/api_samples/trim_concat.gif)\n\nKeep seconds 0-5 of the first clip and seconds 10-15 of the second clip, then join them with `concat`. Trim video with `trim` and audio with `atrim`, and reset timestamps with `setpts` and `asetpts`. Both clips must have the same resolution. Add a `scale` filter to each clip if they don't. Set `\"output\": [\"all\"]` on `concat` so that both its video and audio go to the output.\n\nSend this body to `POST /v1/video_encoder/encode`, replacing every `url_*` placeholder with your own URLs.","operationId":"example_video_trim_concat","requestBody":{"content":{"application/json":{"example":{"inputs":[{"label":"clip1","url":"url_input_1"},{"label":"clip2","url":"url_input_2"}],"outputs":[{"url":"url_output"}],"filters":[{"name":"trim","inputs":["clip1.video"],"label":"v1","kwargs":{"start":0,"end":5}},{"name":"setpts","inputs":["v1"],"label":"v1_pts","args":["PTS-STARTPTS"]},{"name":"atrim","inputs":["clip1.audio"],"label":"a1","kwargs":{"start":0,"end":5},"output":["audio"]},{"name":"asetpts","inputs":["a1"],"label":"a1_pts","args":["PTS-STARTPTS"],"output":["audio"]},{"name":"trim","inputs":["clip2.video"],"label":"v2","kwargs":{"start":10,"end":15}},{"name":"setpts","inputs":["v2"],"label":"v2_pts","args":["PTS-STARTPTS"]},{"name":"atrim","inputs":["clip2.audio"],"label":"a2","kwargs":{"start":10,"end":15},"output":["audio"]},{"name":"asetpts","inputs":["a2"],"label":"a2_pts","args":["PTS-STARTPTS"],"output":["audio"]},{"name":"concat","inputs":["v1_pts","a1_pts","v2_pts","a2_pts"],"label":"joined","kwargs":{"n":2,"v":1,"a":1},"output":["all"]}]}}}},"responses":{"201":{"description":"Job created. The response contains `job_id` and `status`."}}}},"video-watermark":{"post":{"tags":["Video Processing Examples"],"summary":"Add a logo watermark","description":"![Add a logo watermark: before and after](https://storage.texel.ai/api_samples/watermark.gif)\n\nOverlay a transparent PNG logo in the bottom-right corner, 32 pixels from the edges. `W`/`H` are the video size and `w`/`h` are the logo size.\n\nSend this body to `POST /v1/video_encoder/encode`, replacing every `url_*` placeholder with your own URLs.","operationId":"example_video_watermark","requestBody":{"content":{"application/json":{"example":{"inputs":[{"label":"video","url":"url_input"},{"label":"logo","url":"url_logo_png"}],"outputs":[{"url":"url_output"}],"filters":[{"name":"overlay","inputs":["video.video","logo.video"],"label":"branded","kwargs":{"x":"W-w-32","y":"H-h-32"}}]}}}},"responses":{"201":{"description":"Job created. The response contains `job_id` and `status`."}}}},"video-crossfade":{"post":{"tags":["Video Processing Examples"],"summary":"Crossfade between two clips","description":"![Crossfade between two clips: before and after](https://storage.texel.ai/api_samples/crossfade.gif)\n\nJoin two clips with a 1-second dissolve. Set `offset` to the length of the first clip minus the transition duration. Here the first clip is 6 seconds long, so `offset` is 5. `xfade` needs both clips at the same size and frame rate, so each clip is normalized first. `acrossfade` blends the audio over the same second.\n\nSend this body to `POST /v1/video_encoder/encode`, replacing every `url_*` placeholder with your own URLs.","operationId":"example_video_crossfade","requestBody":{"content":{"application/json":{"example":{"inputs":[{"label":"clip1","url":"url_input_1"},{"label":"clip2","url":"url_input_2"}],"outputs":[{"url":"url_output"}],"filters":[{"name":"acrossfade","inputs":["clip1.audio","clip2.audio"],"label":"audio_mix","kwargs":{"d":1},"output":["audio"]},{"name":"scale","inputs":["clip1.video"],"label":"c1_scale","args":[1920,1080]},{"name":"fps","inputs":["c1_scale"],"label":"c1","kwargs":{"fps":30}},{"name":"scale","inputs":["clip2.video"],"label":"c2_scale","args":[1920,1080]},{"name":"fps","inputs":["c2_scale"],"label":"c2","kwargs":{"fps":30}},{"name":"xfade","inputs":["c1","c2"],"label":"dissolve","kwargs":{"transition":"fade","duration":1,"offset":5}}]}}}},"responses":{"201":{"description":"Job created. The response contains `job_id` and `status`."}}}},"video-background-music":{"post":{"tags":["Video Processing Examples"],"summary":"Add background music","description":"![Add background music: before and after](https://storage.texel.ai/api_samples/background_music.gif)\n\nMix a music track under the original audio at 20% volume. `duration=first` stops the music when the video ends. Setting `video_encoder_codec` to `copy` passes the video through without re-encoding.\n\nSend this body to `POST /v1/video_encoder/encode`, replacing every `url_*` placeholder with your own URLs.","operationId":"example_video_background_music","requestBody":{"content":{"application/json":{"example":{"inputs":[{"label":"video","url":"url_input"},{"label":"music","url":"url_music"}],"outputs":[{"url":"url_output"}],"video_encoder_codec":"copy","filters":[{"name":"amix","inputs":["video.audio","music.audio"],"label":"mixed","kwargs":{"inputs":2,"weights":"1 0.2","duration":"first","normalize":0},"output":["audio"]}]}}}},"responses":{"201":{"description":"Job created. The response contains `job_id` and `status`."}}}},"video-studio-voice":{"post":{"tags":["Video Processing Examples"],"summary":"Studio voice enhancement","description":"**Listen:** [▶ Original](https://storage.texel.ai/api_samples/studio_voice_before.wav) · [▶ Enhanced](https://storage.texel.ai/api_samples/studio_voice_after.wav)\n\nClean up speech recorded in a noisy or echoey room with NVIDIA's studio voice model through the `nvafx` filter. Set `outputs[0].streams` to `[\"audio\"]` to return only the enhanced audio.\n\nAudio sample: NVIDIA Studio Voice demo (build.nvidia.com/nvidia/studiovoice).\n\nSend this body to `POST /v1/video_encoder/encode`, replacing every `url_*` placeholder with your own URLs.","operationId":"example_video_studio_voice","requestBody":{"content":{"application/json":{"example":{"inputs":[{"label":"stream1","url":"url_input"}],"outputs":[{"url":"url_output","streams":["audio"]}],"filters":[{"name":"nvafx","inputs":["stream1"],"label":"enhanced","kwargs":{"effect":"studio_voice_high_quality"},"output":["audio"]}]}}}},"responses":{"201":{"description":"Job created. The response contains `job_id` and `status`."}}}},"billing-last-month":{"get":{"tags":["Billing Examples"],"summary":"Usage for last month","description":"`GET /v1/billing/usage`\n\nCall the endpoint with no parameters to get the previous calendar month. `summary` totals the whole period, `services` breaks it down per service, and `items` lists individual jobs. Decimal values are strings. Only successful jobs are charged.\n\n```bash\ncurl -H \"Authorization: Bearer $TEXEL_API_KEY\" \\\n  \"https://api.prod.texel.ai/v1/billing/usage\"\n```","operationId":"example_billing_last_month","responses":{"200":{"description":"Sample response.","content":{"application/json":{"example":{"account_id":"acct_7f3c2a","currency":"USD","start_date":"2026-09-01","end_date":"2026-10-01","generated_at":"2026-10-05T18:00:00Z","provisional":true,"covered_services":["maxine","studio_voice","lipsync"],"retention_days":90,"summary":{"total_requests":3,"successful_requests":2,"pending_requests":0,"failed_requests":1,"unpriced_success_requests":0,"missing_pricing_requests":0,"missing_length_requests":0,"actual_seconds":"184.2","billable_seconds":"190.0","priced_cost":"0.95","complete":true},"services":[{"service":"maxine","total_requests":2,"successful_requests":1,"pending_requests":0,"failed_requests":1,"unpriced_success_requests":0,"missing_pricing_requests":0,"missing_length_requests":0,"actual_seconds":"64.2","billable_seconds":"70.0","priced_cost":"0.35","complete":true},{"service":"studio_voice","total_requests":1,"successful_requests":1,"pending_requests":0,"failed_requests":0,"unpriced_success_requests":0,"missing_pricing_requests":0,"missing_length_requests":0,"actual_seconds":"120.0","billable_seconds":"120.0","priced_cost":"0.60","complete":true}],"items":[{"job_id":"5c89b55c-c7a7-4dcb-9077-eaa0ce45887f","service":"maxine","posted_at":"2026-09-14T16:02:11Z","status":"success","billing_status":"charged","actual_seconds":"64.2","billable_seconds":"70.0","rate_per_minute":"0.30","minimum_seconds":"10","amount":"0.35","unpriced_reasons":[]},{"job_id":"9bbbc5f6-7a72-4f0e-9def-6314927de8a8","service":"maxine","posted_at":"2026-09-14T16:09:40Z","status":"encode_error","billing_status":"not_billable","actual_seconds":null,"billable_seconds":null,"rate_per_minute":null,"minimum_seconds":null,"amount":null,"unpriced_reasons":[]}],"next_cursor":null,"warnings":[]}}}}}}},"billing-date-range-service":{"get":{"tags":["Billing Examples"],"summary":"Usage for a date range and service","description":"`GET /v1/billing/usage?start_date=2026-09-01&end_date=2026-10-01&service=studio_voice`\n\nPass `start_date` (inclusive) and `end_date` (exclusive) together, up to 90 days apart, and optionally a single `service`: `maxine`, `studio_voice`, or `lipsync`.\n\n```bash\ncurl -H \"Authorization: Bearer $TEXEL_API_KEY\" \\\n  \"https://api.prod.texel.ai/v1/billing/usage?start_date=2026-09-01&end_date=2026-10-01&service=studio_voice\"\n```","operationId":"example_billing_date_range_service","responses":{"200":{"description":"Sample response.","content":{"application/json":{"example":{"account_id":"acct_7f3c2a","currency":"USD","start_date":"2026-09-01","end_date":"2026-10-01","generated_at":"2026-10-05T18:00:00Z","provisional":true,"covered_services":["studio_voice"],"retention_days":90,"summary":{"total_requests":1,"successful_requests":1,"pending_requests":0,"failed_requests":0,"unpriced_success_requests":0,"missing_pricing_requests":0,"missing_length_requests":0,"actual_seconds":"120.0","billable_seconds":"120.0","priced_cost":"0.60","complete":true},"services":[{"service":"studio_voice","total_requests":1,"successful_requests":1,"pending_requests":0,"failed_requests":0,"unpriced_success_requests":0,"missing_pricing_requests":0,"missing_length_requests":0,"actual_seconds":"120.0","billable_seconds":"120.0","priced_cost":"0.60","complete":true}],"items":[],"next_cursor":null,"warnings":[]}}}}}}},"billing-pagination":{"get":{"tags":["Billing Examples"],"summary":"Page through job details","description":"`GET /v1/billing/usage?limit=1&cursor=<next_cursor>`\n\n`limit` caps the number of `items` per page (1-1000, default 100). When `next_cursor` is not null, repeat the request with `cursor` set to it, keeping the same dates and service. Totals in `summary` always cover the full period.\n\n```bash\ncurl -H \"Authorization: Bearer $TEXEL_API_KEY\" \\\n  \"https://api.prod.texel.ai/v1/billing/usage?limit=1\"\n\n# Next page\ncurl -H \"Authorization: Bearer $TEXEL_API_KEY\" \\\n  \"https://api.prod.texel.ai/v1/billing/usage?limit=1&cursor=eyJwIjoiMjAyNi0wOS0xNFQxNjowMjoxMVoifQ\"\n```","operationId":"example_billing_pagination","responses":{"200":{"description":"Sample response.","content":{"application/json":{"example":{"account_id":"acct_7f3c2a","currency":"USD","start_date":"2026-09-01","end_date":"2026-10-01","generated_at":"2026-10-05T18:00:00Z","provisional":true,"covered_services":["maxine","studio_voice","lipsync"],"retention_days":90,"summary":{"total_requests":3,"successful_requests":2,"pending_requests":0,"failed_requests":1,"unpriced_success_requests":0,"missing_pricing_requests":0,"missing_length_requests":0,"actual_seconds":"184.2","billable_seconds":"190.0","priced_cost":"0.95","complete":true},"services":[{"service":"maxine","total_requests":2,"successful_requests":1,"pending_requests":0,"failed_requests":1,"unpriced_success_requests":0,"missing_pricing_requests":0,"missing_length_requests":0,"actual_seconds":"64.2","billable_seconds":"70.0","priced_cost":"0.35","complete":true},{"service":"studio_voice","total_requests":1,"successful_requests":1,"pending_requests":0,"failed_requests":0,"unpriced_success_requests":0,"missing_pricing_requests":0,"missing_length_requests":0,"actual_seconds":"120.0","billable_seconds":"120.0","priced_cost":"0.60","complete":true}],"items":[{"job_id":"5c89b55c-c7a7-4dcb-9077-eaa0ce45887f","service":"maxine","posted_at":"2026-09-14T16:02:11Z","status":"success","billing_status":"charged","actual_seconds":"64.2","billable_seconds":"70.0","rate_per_minute":"0.30","minimum_seconds":"10","amount":"0.35","unpriced_reasons":[]}],"next_cursor":"eyJwIjoiMjAyNi0wOS0xNFQxNjowMjoxMVoifQ","warnings":[]}}}}}}},"status-video-job":{"get":{"tags":["Status Checks Examples"],"summary":"Check a video job","description":"`GET /v1/video_encoder/status/{job_id}`\n\nUse the `job_id` returned by `POST /v1/video_encoder/encode`. `job_status` moves from `pending` to `encoding` to `success`, `progress` is a percentage, and `queue_size` is the number of jobs waiting.\n\n```bash\ncurl -H \"Authorization: Bearer $TEXEL_API_KEY\" \\\n  \"https://api.prod.texel.ai/v1/video_encoder/status/658728b9-d2ab-4d79-bc05-b19557770549\"\n```","operationId":"example_status_video_job","responses":{"200":{"description":"Sample response.","content":{"application/json":{"example":{"job_status":"encoding","progress":42,"error_message":null,"queue_size":0}}}}}}},"status-wait-for-job":{"get":{"tags":["Status Checks Examples"],"summary":"Wait for a video job to finish","description":"`GET /v1/video_encoder/status/{job_id}`\n\nPoll every few seconds until `job_status` is no longer `pending` or `encoding`. Instead of polling, you can add a top-level `callback` URL to the encode request.\n\n```python\nimport os, time, requests\n\nAPI = \"https://api.prod.texel.ai/v1/video_encoder\"\nheaders = {\"Authorization\": f\"Bearer {os.environ['TEXEL_API_KEY']}\"}\n\ndef wait(job_id):\n    while True:\n        s = requests.get(f\"{API}/status/{job_id}\", headers=headers).json()\n        if s[\"job_status\"] not in (\"pending\", \"encoding\"):\n            return s\n        time.sleep(5)\n```\n\n```bash\ncurl -H \"Authorization: Bearer $TEXEL_API_KEY\" \\\n  \"https://api.prod.texel.ai/v1/video_encoder/status/658728b9-d2ab-4d79-bc05-b19557770549\"\n```","operationId":"example_status_wait_for_job","responses":{"200":{"description":"Sample response.","content":{"application/json":{"example":{"job_status":"success","progress":100,"error_message":null,"queue_size":0}}}}}}},"status-failed-job":{"get":{"tags":["Status Checks Examples"],"summary":"Read a failed job's error","description":"`GET /v1/video_encoder/status/{job_id}`\n\nWhen a job fails, `job_status` names the failing stage (`download_error`, `encode_error`, `upload_error`, or `unknown_error`) and `error_message` explains why. A `download_error` usually means an input URL isn't readable; an `upload_error` usually means the output URL isn't writable or has expired.\n\n```bash\ncurl -H \"Authorization: Bearer $TEXEL_API_KEY\" \\\n  \"https://api.prod.texel.ai/v1/video_encoder/status/9bbbc5f6-7a72-4f0e-9def-6314927de8a8\"\n```","operationId":"example_status_failed_job","responses":{"200":{"description":"Sample response.","content":{"application/json":{"example":{"job_status":"upload_error","progress":100,"error_message":"Upload Error: PUT upload to https://storage.googleapis.com/my-bucket/out.mp4 failed: HTTP 403 Forbidden","queue_size":0}}}}}}},"status-client-job-id":{"get":{"tags":["Status Checks Examples"],"summary":"Look up a job by your own ID","description":"`GET /v1/video_encoder/status_client_id/{client_job_id}`\n\nSet `client_job_id` in the encode request, then look the job up with it. Use a unique `client_job_id` for each job.\n\n```bash\ncurl -H \"Authorization: Bearer $TEXEL_API_KEY\" \\\n  \"https://api.prod.texel.ai/v1/video_encoder/status_client_id/order-1234-thumbnail\"\n```","operationId":"example_status_client_job_id","responses":{"200":{"description":"Sample response.","content":{"application/json":{"example":{"job_status":"success","progress":100,"error_message":null}}}}}}},"status-lipsync-job":{"get":{"tags":["Status Checks Examples"],"summary":"Check a lip-sync job","description":"`GET /v1/lipsync/status/{job_id}`\n\nUse the job ID returned by `POST /v1/lipsync/run_lipsync`. `job_status` is `processing`, `success`, or `error`. When it is `success`, get a download link from `GET /v1/lipsync/get_url/{job_id}`.\n\n```bash\ncurl -H \"Authorization: Bearer $TEXEL_API_KEY\" \\\n  \"https://api.prod.texel.ai/v1/lipsync/status/3f1a9c52-8d0e-4b7a-9e61-2c4d5b6a7f80\"\n```","operationId":"example_status_lipsync_job","responses":{"200":{"description":"Sample response.","content":{"application/json":{"example":{"job_status":"processing","progress":60,"error_message":""}}}}}}}}}