몇 분 만에 첫 미디어 작업을 배포하세요.
MediaRuntime이 가져올 수 있는 미디어 URL로 필요한 출력만 정확히 요청하고 서명된 최종 웹훅을 받으세요. 원본 URL이 없을 때만 선택적 업로드 엔드포인트를 사용합니다.
작업을 만들고 완료를 기다린 뒤 ZIP 번들을 다운로드하세요.
CLI는 상대 경로와 절대 경로의 로컬 파일을 받아 바이트를 자동으로 업로드합니다. MediaRuntime은 공개 HTTP(S) URL 또는 제한 시간이 있는 서명된 읽기 URL도 직접 받습니다. 첫 실행에서는 CLI의 --download 옵션이나 SDK의 job.wait() 헬퍼로 표준 ZIP 번들을 받으세요. 프로덕션에서는 폴링 대신 job_id를 저장하고 서명된 계정 웹훅을 처리하세요.
# Submit a public or time-limited HTTPS source directly.
# Keep the URL fetchable until MediaRuntime has downloaded the input.
curl -sS -X POST "https://mediaruntime.com/v1/jobs" \
-H "X-API-Key: $MEDIARUNTIME_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"source": "https://cdn.example.com/media/launch-trailer.mp4",
"metadata": { "asset_id": "asset_0426", "media_type": "video" },
"outputs": ["video.web"]
}'완전한 퀵스타트를 복제하세요
실행 가능한 Node.js 및 Python SDK 프로젝트, Go 및 PHP HTTP 예제, 서명된 웹훅 수신기, Postman 가이드를 하나의 공개 저장소에서 제공합니다.
# Optional: use this when you have local bytes but no fetchable source URL.
UPLOAD=$(curl -sS -X POST "https://mediaruntime.com/v1/upload-url" \
-H "X-API-Key: $MEDIARUNTIME_API_KEY" \
-H "Content-Type: application/json" \
-d '{"filename":"launch-trailer.mp4","content_type":"video/mp4"}')
UPLOAD_URL=$(printf '%s' "$UPLOAD" | jq -r .upload_url)
FILE_URI=$(printf '%s' "$UPLOAD" | jq -r .file_uri)
UPLOAD_CONTENT_TYPE=$(printf '%s' "$UPLOAD" | jq -r '.upload_headers["Content-Type"]')
UPLOAD_AUTH=$(printf '%s' "$UPLOAD" | jq -r '.upload_headers.Authorization // empty')
UPLOAD_ARGS=(-H "Content-Type: $UPLOAD_CONTENT_TYPE")
if [[ -n "$UPLOAD_AUTH" ]]; then
UPLOAD_ARGS+=(-H "Authorization: $UPLOAD_AUTH")
fi
curl -sS -X PUT "$UPLOAD_URL" "${UPLOAD_ARGS[@]}" \
--upload-file ./launch-trailer.mp4
# Then use "$FILE_URI" as source in POST /v1/jobs.{
"job_id": "job_1320c28b72104811b075a26a99496cf6",
"status": "QUEUED",
"tier": "standard",
"required_tier": "standard",
"outputs": [{
"alias": "video.web",
"type": "mp4",
"preset": "mp4_720p_h264_aac"
}],
"msg": "accepted"
}터미널에서 미디어 작업을 실행하고 확인하세요.
공식 CLI는 로컬 파일 처리, 프로덕션 진단, 번들 다운로드, 로컬 웹훅 수신기 테스트를 위한 가장 빠른 경로입니다. CLI, Node SDK, Python SDK는 문서화된 인터페이스에 시맨틱 버저닝을 적용하는 안정적인 1.x 패키지입니다. 모두 동일한 공개 작업 계약을 사용하며 ZIP 번들 모델을 변경하지 않습니다.
npm install --global @mediaruntime/cli
mediaruntime login
mediaruntime jobs list --limit 3로컬 파일을 제출하고 전체 번들 다운로드
./launch.mp4와 같은 상대 경로나 절대 로컬 파일 경로를 전달하면 CLI가 작업 생성 전에 자동으로 업로드합니다. 대화형 터미널에서는 스피너가 업로드, 대기, 검증된 다운로드 단계를 표시합니다. --download는 최종 결과를 기다리고 표준 ZIP을 원자적으로 게시합니다. --force를 명시하지 않으면 기존 파일을 보존합니다.
# The CLI uploads this local file before creating the job.
# Relative paths such as ./launch.mp4 work too.
mediaruntime run "/Users/you/Videos/launch.mp4" \
-o video.streaming \
-o audio.transcription \
--metadata '{"asset_id":"launch-01"}' \
--idempotency-key 'asset:launch-01:v1' \
--download ./launch-01.zip실시간 공개 카탈로그 확인
mediaruntime capabilities는 별칭과 기능을 요약하고, mediaruntime presets list는 순서가 보장된 공개 프리셋 카탈로그를 반환합니다. 이 읽기 전용 명령에는 브라우저 로그인이나 MEDIARUNTIME_API_KEY가 필요하지 않습니다.
# Public discovery commands do not require login or an API key.
mediaruntime capabilities
mediaruntime presets list
# Use JSON when another tool will consume the catalog.
mediaruntime presets list --json정확한 공개 프리셋 실행
DASH 또는 VP9 같은 정확한 카탈로그 항목에는 반복 가능한 --preset을 사용하세요. CLI는 작업 생성 전에 모든 이름을 실시간 공개 카탈로그와 대조하며, 별칭과 정확한 프리셋을 요청 순서대로 함께 사용할 수 있습니다.
# Preset names are validated against the live public catalog.
mediaruntime run ./launch.mp4 \
--preset dash_ladder_v1 \
--preset webm_vp9_1080p \
--download ./adaptive-and-vp9.zip작업 확인 및 가져오기
계정 작업 한 페이지를 나열하고 상태로 필터링하거나, 작업 하나를 확인하고 보관 중인 ZIP 번들을 다운로드하세요. jobs list가 출력한 불투명 커서를 다음 페이지 요청에 사용합니다.
mediaruntime jobs list --status COMPLETED --limit 20
mediaruntime jobs get job_123
mediaruntime jobs get job_123 --download ./job_123.zip자동화에는 API 키 사용
CI, 서버, 컨테이너는 비밀 관리자에서 MEDIARUNTIME_API_KEY를 주입해야 합니다. 자격 증명을 명령 인수, 소스 관리, 로그 또는 평문 설정 파일에 넣지 마세요.
# Permanently supported for CI, servers, and containers.
export MEDIARUNTIME_API_KEY="sk_..."
mediaruntime jobs list --limit 3| 기능 | 명령 계약 | 설명 |
|---|---|---|
| 출력 별칭 | --output video.web | 고정된 별칭 6개를 모두 사용할 수 있으며 여러 결과물이 필요하면 --output을 반복합니다. |
| 머신 출력 | --json | 스크립트와 CI용으로 URL을 제거한 간결한 JSON 결과 하나를 출력합니다. |
| 안전한 재시도 | --idempotency-key | 프로세스를 다시 시작해도 같은 논리 작업에는 하나의 비즈니스 키를 재사용합니다. |
| 번들 안전성 | --download / --force | 최종 번들만 다운로드하고 제공된 무결성을 검증하며 실수로 덮어쓰는 것을 방지합니다. |
| 종료 상태 | 0–9, 130 | 인증, API 거부, 최종 실패, 시간 초과, 트리거 및 번들 오류에 서로 다른 0이 아닌 코드를 사용합니다. |
export MEDIARUNTIME_WEBHOOK_SECRET="whsec_..."
mediaruntime trigger job.completed \
--to http://127.0.0.1:3000/webhooks/mediaruntimeAPI 키는 서버에만 보관하세요.
계정 → 개발자 설정 → API 키에서 키를 생성하세요. 원본 키는 한 번만 표시되며 시크릿 매니저에 보관해야 합니다. 브라우저 코드, 모바일 바이너리, 로그, 소스 관리에는 절대 두지 마세요.
헤더
모든 /v1 요청에 X-API-Key를 보내세요.
보관
MEDIARUNTIME_API_KEY를 서버 측 시크릿 매니저에 저장하세요.
교체
새 키를 만들어 배포하고 트래픽을 확인한 뒤 이전 키를 폐기하세요.
npm install --global @mediaruntime/cli
mediaruntime login
mediaruntime jobs list --limit 3metadata가 연동 작업을 대신하게 하세요.
wMedia가 사용하는 프로덕션 패턴은 의도적으로 단순합니다. 입력과 출력 레시피, 그리고 최종 이벤트를 자체 데이터베이스 레코드와 다시 연결할 수 있을 만큼의 metadata를 제출하면 됩니다.
{
"source": "https://cdn.example.com/media/source.mp4",
"metadata": {
"producer": "my-api",
"entity_id": "video_01J8Y4",
"owner_id": "user_482",
"media_type": "video",
"trace_id": "req_f839"
},
"moderation": {
"enabled": true,
"mode": "report",
"checks": ["sexual", "violence", "dangerous"]
},
"watermark": { "enabled": true },
"outputs": [
{
"type": "mp4",
"preset": "mp4_720p_h264_aac",
"path_suffix": "web",
"poster_time_sec": 2,
"gif_preview": {
"enabled": true,
"width": 320,
"fps": 8,
"start_time": 2,
"duration": 2.5
}
},
{
"type": "image",
"preset": "image_multi_v1",
"path_suffix": "covers",
"images": [
{ "width": 1280, "height": 720, "mode": "fit", "format": "webp", "quality": 84 },
{ "width": 480, "height": 480, "mode": "cover", "format": "webp", "quality": 80 }
]
}
]
}| 필드 | 타입 | 설명 |
|---|---|---|
| source | string 또는 object | 표준 단일 입력입니다. 공개 HTTP(S), 제한 시간이 있는 서명된 HTTP(S), 접근 가능한 gs:// URL 또는 url만 포함한 객체를 사용합니다. |
| file_url | string | 스칼라 source의 영구 호환 표기입니다. source와 file_url을 함께 보내지 마세요. |
| inputs | array | 1~25개 입력의 배치 팬아웃입니다. 각 항목은 표준 source를 사용하며 기존 file_url도 항목별로 계속 지원됩니다. 두 단일 입력 필드와 함께 보낼 수 없습니다. |
| outputs | array | 1~10개의 출력 레시피. 각 항목에 type이 필요하며 preset 지정을 강력히 권장합니다. |
| deliver_webhook | boolean | 기본값은 true입니다. false로 설정하면 웹훅 없이 로컬 SDK에서 폴링할 수 있습니다. 정상 요금이 적용되며 계정 웹훅 설정은 변경되지 않습니다. |
| metadata | object | 최대 32KiB의 JSON. 저장된 뒤 meta.request_metadata로 그대로 반환됩니다. |
| moderation | object | Premium 시각 미디어 검사: sexual, violence, dangerous. |
| watermark | object | Premium 시각 미디어 오버레이. 계정에 PNG 로고가 미리 등록되어 있어야 합니다. |
배치 팬아웃
모든 입력에 동일한 출력이 필요할 때 배치를 사용하세요. 입력별 metadata는 각 하위 작업에 병합되며, 상위 작업이 배치의 기준이 됩니다.
{
"inputs": [
{
"source": "https://cdn.example.com/a.mp4",
"input_id": "asset-a",
"metadata": { "position": 0 }
},
{
"source": "https://cdn.example.com/b.mp4",
"input_id": "asset-b",
"metadata": { "position": 1 }
}
],
"metadata": { "batch_id": "import_2026_08_09" },
"outputs": [{ "type": "mp4", "preset": "mp4_720p_h264_aac" }]
}Idempotency-Key로 안전하게 재시도하기
요청이 타임아웃되면 상태가 모호합니다. 작업은 이미 큐에 들어갔고 응답만 유실되었을 수 있습니다. Idempotency-Key 헤더를 보내면 재시도가 안전해집니다. 같은 키는 두 번째 작업을 생성하고 청구하는 대신 기존 작업을 반환합니다. 헤더가 없으면 기존 동작과 동일하게 매 POST마다 새 작업이 생성됩니다.
# One key per logical job. Generate it in your client, not inside the retry loop.
KEY=$(uuidgen) # or a deterministic id you can regenerate: asset_0426:mp4_720p:v1
curl -sS -X POST "https://mediaruntime.com/v1/jobs" \
-H "X-API-Key: $MEDIARUNTIME_API_KEY" \
-H "Idempotency-Key: $KEY" \
-H "Content-Type: application/json" \
-d '{
"source": "https://example.com/source.mp4",
"metadata": { "asset_id": "asset_0426" },
"outputs": [{ "type": "mp4", "preset": "mp4_720p_h264_aac" }]
}'
# Timed out? Send the exact same request again with the SAME key.
# You get the original job_id back - no second job, no second charge.키 규칙
- UUID도 좋지만
asset_0426:mp4_720p:v1같은 결정적 ID가 더 낫습니다. 장애 후에도 동일하게 재생성할 수 있습니다. - 키는 계정 단위로 구분되며 24시간 동안 유효합니다.
- 같은 키를 다른 본문으로 재사용하면 422를 반환합니다. 대개 루프에서 키 하나를 재사용한 경우입니다.
- 첫 요청이 처리 중일 때 재시도하면 409를 반환합니다. 잠시 후 다시 시도하세요.
- 같은 파일을 의도적으로 두 번 처리하려면 서로 다른 키를 사용하세요.
엔진이 생성할 결과물을 선택하세요.
일반적인 작업은 고정 출력 별칭으로 시작하세요. 레시피를 사용자 지정해야 할 때 명시적인 type과 preset을 사용하며, 짝이 맞지 않으면 거부되거나 잘못 라우팅될 수 있습니다.
고정 출력 별칭
별칭은 게이트웨이가 보장하는 안정적인 계약입니다. 유효성 검사, 예상 사용량, 결제 및 저장 전에 실제 레시피로 해석되며 같은 outputs 배열에서 명시적 출력 객체와 함께 사용할 수 있습니다.
| 별칭 | 해석 결과 | 결과물 | 티어 |
|---|---|---|---|
| video.web | mp4 / mp4_720p_h264_aac, JPG poster at 2s | 720p H.264/AAC MP4 및 JPG 포스터 | Standard |
| video.streaming | hls / hls_ladder_v1 | HLS 마스터, 1080p/720p 변형 및 세그먼트 | Standard |
| video.social | social / social_vertical_blur | 흐린 9:16 배경을 포함한 1080×1920 MP4 | Premium |
| audio.web | audio / audio_aac_128k | 128 kbps AAC/M4A | Standard |
| audio.transcription | audio / audio_aac_128k with base subtitles | AAC/M4A와 SRT 및 WebVTT 자막 | Standard |
| image.web | image / image_multi_v1 with two WebP renditions | 1200×630 및 320×320 WebP 렌디션 | Premium |
{
"source": "https://cdn.example.com/landscape-interview.mp4",
"outputs": ["video.social"]
}social_vertical_blur는 1080×1920 H.264/AAC MP4를 만듭니다. 원본을 잘라내지 않고 맞춰 넣고, 흐리게 처리한 복사본이 9:16 캔버스의 배경을 채웁니다. Reels, TikTok, Shorts에 적합합니다. 세로 출력 높이가 1920픽셀이므로 Premium 레시피입니다.비디오 파일 및 포스터
| 프리셋 | 전송 대상 | 작업 실행 내용 | 기본 티어 |
|---|---|---|---|
| video_clip_v1 (type: mp4) | 비디오 | 정확한 원본 구간을 H.264/AAC MP4로 렌더링하며 원본 또는 세로 흐림 배경과 제공된 대본 자막을 지원합니다. Standard 처리이며 재전사하지 않습니다. clip.mp4, clip.srt and clip.vtt when transcript is supplied. | Standard |
| mp4_720p_h264_aac (type: mp4) | 비디오 | 웹 재생을 위해 faststart가 적용된 720p H.264/AAC MP4입니다. MP4 video. | Standard |
| mp4_ladder_v1 (type: mp4) | 비디오 | 1080p, 720p, 480p의 MP4 렌디션 세 개와 각 렌디션의 포스터를 생성합니다. 1080p MP4, 720p MP4, 480p MP4. | Standard |
| transmux_mp4_fast (type: mp4) | 비디오 | 재인코딩 없이 기존 스트림을 faststart MP4로 복사합니다. 복사가 실패하고 폴백이 활성화되어 있으면 720p H.264 프리셋으로 재인코딩합니다. MP4 video. | Standard |
| poster_frame_v1 (type: mp4) | 비디오 | poster_time_sec 시점에서 캡처한 720p JPG 한 장입니다. 요청 type은 여전히 mp4입니다. JPG poster. | Standard |
| mp4_hevc_1080p (type: mp4) | 비디오 | Apple 재생을 위한 hvc1 태그가 적용된 1080p HEVC/H.265 + AAC MP4입니다. HEVC MP4. | Premium |
| mp4_av1_smart (type: mp4) | 비디오 | 압축 효율에 최적화된 1080p AV1 + Opus MP4이며, 인코딩에 CPU를 많이 사용합니다. AV1 MP4. | Premium |
| mov_prores_422 (type: mp4) | 비디오 | ProRes 422 HQ + PCM MOV 편집 마스터입니다. 원본 해상도를 유지하며 용량이 큰 중간 파일을 생성합니다. ProRes MOV. | Premium |
소셜 영상
| 프리셋 | 전송 대상 | 작업 실행 내용 | 기본 티어 |
|---|---|---|---|
| audiogram_v1 (type: social) | 오디오 또는 오디오가 있는 비디오 | 아트워크 맞춤, 고대비 파형, 안전 영역의 선택적 자막을 사용해 오디오를 Premium H.264/AAC 소셜 MP4와 깨끗한 포스터로 구성합니다. H.264/AAC audiogram MP4, caption-free poster JPEG, audiogram.json, audiogram.waveform.json. | Premium |
| social_vertical_blur (type: social) | 비디오 | 1080×1920 H.264/AAC MP4입니다. Reels, TikTok, Shorts용으로 흐린 9:16 배경 위에 원본을 맞춰 배치합니다. vertical MP4. | Premium |
애니메이션 GIF
| 프리셋 | 전송 대상 | 작업 실행 내용 | 기본 티어 |
|---|---|---|---|
| gif_hq (type: gif) | 이미지 또는 비디오 | 가로 480px, 15fps 애니메이션 GIF이며 팔레트 생성을 사용해 색상 품질을 높입니다. animated GIF. | Standard |
프레임 추출
| 프리셋 | 전송 대상 | 작업 실행 내용 | 기본 티어 |
|---|---|---|---|
| clip_candidates_v1 (type: frames) | 오디오 또는 오디오가 있는 비디오 | 기존 Whisper 또는 제공된 원본 시간 대본, 음성 경계와 키워드를 사용해 클립을 추천합니다. Premium 분석이며 점수는 인기도를 예측하지 않습니다. clip_candidates.json with candidates and reusable source-timed transcript. | Premium |
| contact_sheet_v1 (type: frames) | 비디오 | 번호가 매겨진 콘택트 시트 이미지와 각 타일을 원본 타임스탬프에 연결하는 contact_sheet.json을 생성합니다. numbered contact-sheet images, contact_sheet.json. | Standard |
| extract_frames_1 (type: frames) | 비디오 | 초당 1프레임으로 샘플링한 번호가 매겨진 JPG 프레임 시퀀스입니다. JPG frames at 1 fps. | Standard |
| extract_frames_5 (type: frames) | 비디오 | 초당 5프레임으로 샘플링한 번호가 매겨진 JPG 프레임 시퀀스입니다. JPG frames at 5 fps. | Standard |
| scene_detect_v1 (type: frames) | 비디오 | 장면 경계를 감지하고 샷마다 JPG 키프레임 한 장과 타임스탬프·점수가 담긴 scenes.txt 색인을 생성합니다. scene JPGs, scene timeline. | Standard |
| perceptual_hash_v1 (type: frames) | 비디오 | 비디오를 샘플링해 재업로드 및 유사 중복 감지용 64비트 지각 해시를 phash.json에 기록합니다. phash.json. | Standard |
스트리밍
| 프리셋 | 전송 대상 | 작업 실행 내용 | 기본 티어 |
|---|---|---|---|
| hls_ladder_v1 (type: hls) | 비디오 | 1080p와 720p H.264/AAC 변형, 마스터 재생목록, 6초 세그먼트로 구성된 HLS VOD 패키지입니다. HLS master playlist, variant playlists, media segments. | Standard |
| transmux_hls_fast (type: hls) | 비디오 | 호환 스트림을 재인코딩 없이 HLS VOD 패키지로 복사합니다. 폴백을 켜면 호환되지 않는 스트림은 hls_ladder_v1로 인코딩합니다. HLS master playlist, variant playlist, media segments. | Standard |
MPEG-DASH 스트리밍
| 프리셋 | 전송 대상 | 작업 실행 내용 | 기본 티어 |
|---|---|---|---|
| dash_ladder_v1 (type: dash) | 비디오 | 1080p와 720p H.264/AAC 표현, 표준 manifest.mpd, 조각화 MP4 세그먼트로 구성된 MPEG-DASH VOD 패키지입니다. DASH MPD, initialization segments, media segments. | Standard |
| transmux_dash_fast (type: dash) | 비디오 | 호환 스트림을 재인코딩 없이 MPEG-DASH로 복사합니다. 폴백을 켜면 호환되지 않는 스트림은 dash_ladder_v1로 인코딩합니다. DASH MPD, initialization segments, media segments. | Standard |
WebM 비디오
| 프리셋 | 전송 대상 | 작업 실행 내용 | 기본 티어 |
|---|---|---|---|
| webm_vp9_1080p (type: webm) | 비디오 | 최신 브라우저 재생을 위한 1080p VP9 + Opus WebM입니다. MP4 라벨이 아닌 실제 VP9 인코딩입니다. VP9 WebM. | Premium |
오디오
| 프리셋 | 전송 대상 | 작업 실행 내용 | 기본 티어 |
|---|---|---|---|
| audio_copy_fast (type: audio) | 오디오 또는 오디오가 있는 비디오 | 재인코딩 없이 원본 오디오 스트림을 복사합니다. 복사가 실패하고 폴백이 활성화되어 있으면 128kbps AAC로 기록합니다. audio file. | Standard |
| audio_aac_128k (type: audio) | 오디오 또는 오디오가 있는 비디오 | faststart가 적용된 M4A 파일의 128kbps AAC입니다. M4A audio. | Standard |
| audio_mp3_128k (type: audio) | 오디오 또는 오디오가 있는 비디오 | 128kbps MP3 파일입니다. MP3 audio. | Standard |
| audio_opus_96k (type: audio) | 오디오 또는 오디오가 있는 비디오 | 음성 전달에 적합한 96kbps Opus 파일입니다. Opus audio. | Standard |
| audio_loudnorm_aac_128k (type: audio) | 오디오 또는 오디오가 있는 비디오 | -16 LUFS를 목표로 정규화한 뒤 128kbps AAC로 기록합니다. normalized M4A audio, loudness metrics. | Standard |
| audio_trim_silence_aac_128k (type: audio) | 오디오 또는 오디오가 있는 비디오 | 앞뒤 무음을 제거한 뒤 128kbps AAC로 기록합니다. trimmed M4A audio. | Premium |
| audio_loudnorm_trim_aac_128k (type: audio) | 오디오 또는 오디오가 있는 비디오 | 경계의 무음을 잘라내고 -16 LUFS로 정규화한 뒤 128kbps AAC로 기록합니다. trimmed and normalized M4A audio, loudness metrics. | Premium |
| audio_whisper_prep (type: audio) | 오디오 또는 오디오가 있는 비디오 | Whisper, ASR 등 음성 파이프라인을 위해 준비된 16kHz 모노 PCM WAV입니다. WAV audio. | Standard |
이미지 파생본
| 프리셋 | 전송 대상 | 작업 실행 내용 | 기본 티어 |
|---|---|---|---|
| image_multi_v1 (type: image) | 이미지 또는 비디오 | fit, fill, cover, contain 중 선택한 방식으로 요청한 images 배열을 생성합니다. JPG와 WebP 렌디션은 max_bytes로 검증된 최대 용량을 설정하고 image_size_limits.json을 받을 수 있습니다. 티어는 포맷, 크기, 개수, 스마트 크롭, 배경 제거 여부에 따라 달라집니다. image renditions, image_size_limits.json when max_bytes is used, smart_crop.json when enabled. | Standard |
| image_animated_webp_v1 (type: image) | 비디오 | 비디오에서 너비, FPS, 시작 시점, 길이, 품질 및 반복 횟수를 설정할 수 있는 애니메이션 WebP를 생성합니다. animated WebP. | Premium |
| image_animated_apng_v1 (type: image) | 비디오 | 비디오에서 너비, FPS, 시작 시점, 길이 및 반복 횟수를 설정할 수 있는 무손실 애니메이션 PNG를 생성합니다. animated PNG. | Premium |
| image_placeholders_v1 (type: image) | 이미지 또는 비디오 | 이미지 또는 선택한 비디오 프레임에서 표준 호환 BlurHash와 ThumbHash, 소스 및 플레이스홀더 크기, 알파 인식 대표 색상, 바이트 제한 WebP LQIP를 생성합니다. placeholders.json, lqip.webp. | Standard |
분석 및 리포트
| 프리셋 | 전송 대상 | 작업 실행 내용 | 기본 티어 |
|---|---|---|---|
| compatibility_report_v1 (type: image) | 비디오 | 비디오를 버전이 지정된 웹, 모바일, 소셜 업로드 및 편집 프로필 5개에 대해 평가하고 규칙별 근거와 수정 프리셋을 제공합니다. compatibility_report.json. | Standard |
| media_report_v1 (type: image) | 오디오, 이미지 또는 비디오 | 트랜스코딩 없이 오디오, 비디오 또는 이미지 메타데이터를 검사하고 컨테이너와 스트림 정보 및 존재하는 경우 GOP, EXIF, GPS 정보가 포함된 media_report.json을 생성합니다. media_report.json. | Standard |
| code_detect_v1 (type: frames) | 이미지 또는 비디오 | 이미지, 비디오·애니메이션 프레임, 오디오에 포함된 커버 아트의 QR 코드와 바코드를 제한된 구간에서 스캔하고 codes.json 및 감지된 근거 프레임을 생성합니다. codes.json, evidence frames when codes are found. | Standard |
transmux_*_fast 프리셋은 원본 스트림이 MP4, HLS 또는 DASH와 호환될 때 화질이 바뀌는 인코딩을 피합니다. 복사가 실패하고 폴백이 켜져 있으면 해당 인코딩 프리셋을 사용합니다. 코덱과 렌디션 특성을 확정해야 한다면 인코딩 프리셋을 직접 사용하세요.한 번 업로드하고 제품에 필요한 포맷과 부가 결과물을 만드세요.
원본 확장자가 출력을 결정하지 않습니다. type과 preset이 실행할 레시피를 선택하므로, 업로드한 영상 하나로 재생용 영상, 오디오 전용 미디어, 자막, GIF 미리보기, 포스터, 프레임 시퀀스를 같은 비동기 작업에서 만들 수 있습니다.
| 원본 | 결과물 | 레시피 |
|---|---|---|
| JPG, PNG 또는 WebP | JPG, PNG, WebP 또는 AVIF 파생본 | image + image_multi_v1을 사용하고 images[].format, 크기, mode, quality를 지정합니다. JPG/WebP는 max_bytes와 min_quality로 검증된 최대 용량을 설정할 수 있습니다. |
| 비디오 | 웹 MP4, HLS, 소셜 영상 또는 편집 마스터 | 알맞은 mp4, hls 또는 social 프리셋을 선택합니다. |
| 비디오 | 애니메이션 GIF, 포스터 또는 JPG 프레임 시퀀스 | gif_hq, poster_frame_v1, extract_frames_1/5를 사용하거나 비디오 출력에 gif_preview를 추가합니다. |
| 비디오 또는 오디오 | M4A, MP3, Opus 또는 음성용 WAV | 해당하는 audio_* 프리셋을 선택합니다. 비디오 입력은 오디오 스트림이 추출됩니다. |
| 비디오 또는 음성 오디오 | SRT, WebVTT 또는 둘 다 | 오디오나 비디오 출력에 subtitles를 추가하고 srt, vtt 또는 both를 선택합니다. |
{
"source": "https://cdn.example.com/media/product-photo.png",
"metadata": {
"media_type": "image",
"asset_id": "product-photo-0426"
},
"outputs": [{
"type": "image",
"preset": "image_multi_v1",
"path_suffix": "converted",
"images": [
{ "width": 1600, "height": 1200, "mode": "fit", "format": "jpg", "quality": 88 },
{ "width": 1600, "height": 1200, "mode": "fit", "format": "webp", "quality": 82 }
]
}]
}{
"source": "https://cdn.example.com/media/interview.mov",
"outputs": [
{ "type": "mp4", "preset": "mp4_720p_h264_aac", "path_suffix": "web-video" },
{ "type": "audio", "preset": "audio_mp3_128k", "path_suffix": "audio-only" }
]
}{
"source": "https://cdn.example.com/media/interview.mp4",
"metadata": { "media_type": "video", "asset_id": "interview-0426" },
"outputs": [{
"type": "audio",
"preset": "audio_aac_128k",
"path_suffix": "audio-and-transcript",
"subtitles": {
"enabled": true,
"languages": ["auto"],
"format": "both",
"model": "ggml-base.bin",
"translate_to_english": false
}
}]
}{
"source": "https://cdn.example.com/media/trailer.mp4",
"metadata": { "media_type": "video", "asset_id": "trailer-0426" },
"outputs": [{
"type": "mp4",
"preset": "mp4_720p_h264_aac",
"path_suffix": "web",
"poster_time_sec": 4,
"poster_format": "jpg",
"gif_preview": {
"enabled": true,
"width": 480,
"fps": 10,
"start_time": 4,
"duration": 3
}
}]
}{
"source": "https://cdn.example.com/media/feature-film.mp4",
"metadata": { "media_type": "video", "asset_id": "stream-0426" },
"outputs": [{
"type": "hls",
"preset": "hls_ladder_v1",
"path_suffix": "stream"
}]
}{
"source": "https://cdn.example.com/media/clip.mp4",
"metadata": { "media_type": "video", "asset_id": "clip-0426" },
"outputs": [
{ "type": "gif", "preset": "gif_hq", "path_suffix": "animated-preview" },
{ "type": "frames", "preset": "extract_frames_1", "path_suffix": "sampled-frames" }
]
}hls_ladder_v1은 마스터 재생목록, 1080p와 720p H.264/AAC 변형 재생목록, 6초 미디어 세그먼트를 생성합니다. 보고된 마스터 재생목록 URL을 사용하거나 번들 전체를 함께 옮기세요.gif_hq는 480px, 15fps의 완전한 기본 GIF를 만듭니다. gif_preview는 다른 비디오 출력에 길이가 짧고 시간이 명시된 GIF를 추가합니다. 프레임 프리셋은 초당 1개 또는 5개의 번호가 매겨진 JPG 시퀀스를 반환합니다.알아두면 좋은 옵션
audio_aac_128k는 M4A를, audio_mp3_128k는 MP3를, audio_opus_96k는 Opus를, audio_whisper_prep는 16kHz 모노 WAV를 반환합니다. subtitles.format에는 srt, vtt 또는 both를 지정합니다. 포스터 이미지 한 장만 필요하면 type: mp4와 preset: poster_frame_v1, 원하는 poster_time_sec를 함께 보내세요.
클립 분석, 검토, 렌더링
정확한 구간은 video_clip_v1으로 지정하거나 clip_candidates_v1으로 먼저 후보를 찾습니다. 두 프리셋 모두 기존 비동기 작업 API를 사용합니다.
수동 클립에는 모델이 필요 없습니다
video_clip_v1은 type: mp4와 Standard 처리를 사용합니다. 유한한 시작 ≥ 0, 길이 0.1–300초로 전체 구간이 원본 안에 있어야 합니다. original은 종횡비를 유지하며 긴 변 1920 px, 30 fps 이하입니다. vertical_blur는 720 × 1280, 최대 30 fps입니다. 제공된 자막 굽기를 포함한 렌더링은 Whisper를 실행하지 않습니다.
{
"source": "https://cdn.example.com/interview.mp4",
"outputs": [{
"type": "mp4",
"preset": "video_clip_v1",
"clip": {
"start_time_sec": 0,
"duration_sec": 30,
"layout": "original",
"burn_captions": false
}
}]
}선택적 후보 분석
clip_candidates_v1은 type: frames와 Premium 처리이며 대본을 제공해도 동일합니다. clip_analysis.transcript가 없거나 비어 있으면 기존 Whisper가 실행되고, 대본이 있으면 건너뜁니다. 대본 업로드는 선택 사항입니다. 최소/최대 길이는 1–300초, max_candidates는 1–20입니다. 최소 길이는 최대 길이와 정확한 원본 길이를 넘을 수 없습니다. 작업당 분석 출력은 하나, 분석 원본은 최대 6시간입니다. 점수는 음성 경계와 키워드 일치를 나타내며 인기도 예측이 아닙니다.
{
"source": "https://cdn.example.com/interview.mp4",
"outputs": [{
"type": "frames",
"preset": "clip_candidates_v1",
"clip_analysis": {
"min_duration_sec": 15,
"max_duration_sec": 60,
"max_candidates": 5,
"keywords": ["deployment"]
}
}]
}렌더링 전에 검토하세요
COMPLETED 후 GET /v1/jobs/{job_id}/clip-candidates에서 대본과 수정 가능한 후보 구간을 받습니다. 분석 결과는 영상이나 JavaScript가 아닌 clip_candidates.json입니다. 동일한 원본으로 선택 구간을 새 video_clip_v1 작업에 제출하세요. candidates가 비어 있어도 정상입니다. empty_reason은 no_speech, no_keyword_match, no_matching_ranges, source_too_short(이전 리포트 판독용)입니다. 후보가 있으면 null이며 이전 리포트에는 필드가 없을 수 있습니다. 후보가 없어도 비어 있지 않은 대본은 재사용할 수 있습니다.
원본 시간 기준 자막 첨부와 굽기
REST/Python의 clip.transcript는 {start_time_sec, end_time_sec, text}, Node SDK는 {startTimeSec, endTimeSec, text} 배열을 받습니다. 파일 URL은 받지 않습니다. 전체 원본 시간을 유지하면 엔진이 구간을 자르고 클립 시간으로 변환합니다. burn_captions: true는 클립과 겹치는 텍스트가 필요합니다. Studio 업로드/클라우드는 SRT, VTT, JSON(≤ 1 MiB)을 지원하며 드롭다운 선택 즉시 첨부됩니다. 유효한 겹치는 대본이 있으면 체크박스가 활성화됩니다. 익명 Sandbox는 로컬 대본을 지원하고 클라우드는 로그인이 필요합니다. 후보 대본 첨부는 선택적 기존 대본 재사용 항목에 있습니다. 번들은 MP4, 포스터와 겹치는 SRT/VTT를 포함합니다.
{
"source": "https://cdn.example.com/interview.mp4",
"outputs": [{
"type": "mp4",
"preset": "video_clip_v1",
"clip": {
"start_time_sec": 20,
"duration_sec": 15,
"layout": "vertical_blur",
"burn_captions": true,
"transcript": [
{"start_time_sec": 20, "end_time_sec": 35, "text": "Example speech."}
]
}
}]
}제한과 사용 가능 범위
대본: 최대 2,000개 구간, 구간당 UTF-8 2,000바이트, 총 256 KiB. 시작은 유한하며 순서대로, 끝 > 시작이고 원본 안에 있어야 합니다. 키워드: 최대 20개, 각 UTF-8 100바이트. 클리핑 제어 전체: 512 KiB 이하. 별도 subtitles, watermark와 무관한 재정의는 렌더링에서 거부됩니다. 최종 등급은 승인된 예상값이 결정합니다. 공개 Sandbox는 지정 샘플과 Standard만 지원하며 Premium 분석은 적격 Workspace가 필요합니다.
SDK 및 CLI 1.4.0 출시 준비
이 클리핑 SDK/CLI 예제는 일치하는 1.4.0 패키지를 게시하고 설치한 후 사용합니다. 게시는 아직 대기 중입니다. Node는 jobs.getClipCandidates()와 emptyReason, Python은 jobs.get_clip_candidates()와 empty_reason을 사용합니다. CLI --clip-transcript는 로컬 SRT, VTT, JSON(≤ 1 MiB)을 받습니다. 해당 플래그와 --clip-captions 없는 수동 클립에는 대본이 필요 없습니다.
# Automatic transcription and candidate suggestions (Premium).
mediaruntime run ./interview.mp4 --preset clip_candidates_v1 \
--clip-min-duration 15 --clip-max-duration 60 --clip-count 5 \
--clip-keyword deployment --download ./analysis.zip
# Inspect clip_candidates.json in the downloaded ZIP and choose a range.
# A supplied source-timed transcript burns captions without invoking Whisper.
mediaruntime run ./interview.mp4 --preset video_clip_v1 \
--clip-start 20 --clip-duration 15 --clip-layout vertical_blur \
--clip-transcript ./source.srt --clip-captions --download ./clip.zip
# Manual clipping without captions or any transcription model.
mediaruntime run ./interview.mp4 --preset video_clip_v1 \
--clip-start 0 --clip-duration 10 --download ./manual.zipimport { MediaRuntime } from "@mediaruntime/node";
const media = new MediaRuntime({ apiKey: process.env.MEDIARUNTIME_API_KEY });
// Retain this stable source reference with the plan. Do not save an expiring URL.
const source = "https://your-cdn.example/episode.mp4";
const analysis = await media.jobs.create({ source, outputs: [{
type: "frames", preset: "clip_candidates_v1",
clipAnalysis: { minDurationSec: 15, maxDurationSec: 60, maxCandidates: 5 },
}] });
const analyzed = await analysis.wait();
if (analyzed.status !== "COMPLETED") throw new Error(`Analysis ${analyzed.status}`);
const plan = await media.jobs.getClipCandidates(analysis.id);
// Present candidates for human review. A score is a heuristic, not predicted engagement.
const selected = plan.candidates[0];
if (!selected) {
console.log("No suggestions:", plan.emptyReason ?? "No reason in this older report");
// Stop this automatic render path; plan.transcript remains usable for manual clips.
process.exit(0);
}
const render = await media.jobs.create({ source, outputs: [{
type: "mp4", preset: "video_clip_v1", clip: {
startTimeSec: selected.startTimeSec,
durationSec: selected.durationSec,
layout: "vertical_blur", // or original
burnCaptions: true,
// Keep SOURCE timestamps. The engine intersects and rebases cues for this render.
transcript: plan.transcript.filter((segment) =>
segment.endTimeSec > selected.startTimeSec &&
segment.startTimeSec < selected.startTimeSec + selected.durationSec),
},
}] });
const rendered = await render.wait();
if (rendered.status !== "COMPLETED") throw new Error(`Render ${rendered.status}`);
console.log(rendered.bundle.downloadUrl); // Short-lived URL for the complete ZIP.프리셋으로 시작하고 꼭 필요한 것만 재정의하세요.
프리셋은 요청을 읽기 쉽게 유지하고 엔진에 안정적인 기준을 제공합니다. 렌디션, 자막, 미리보기, 코덱, 비트레이트 옵션은 제품에 필요할 때만 명시적으로 추가하세요.
Web MP4
- Source
- Video with a decodable video stream; audio is optional
- Artifacts
- 720p H.264/AAC MP4 and a JPG poster
// Source: Video with a decodable video stream; audio is optional
// Alias: video.web
// Preset: mp4_720p_h264_aac
// Artifacts: 720p H.264/AAC MP4 and a JPG poster
// Tier: Standard
// Testing: use job.wait(); production: persist job.id and consume the signed webhook
// npm install @mediaruntime/node
import { MediaRuntime } from "@mediaruntime/node";
const media = new MediaRuntime();
const job = await media.jobs.create({
"source": "https://cdn.example.com/media/source.mov",
"metadata": {
"asset_id": "asset_0426",
"recipe": "web-mp4"
},
"outputs": [
"video.web"
],
"idempotencyKey": "asset:web-mp4:v1"
});
console.log(job.id, job.status);HLS streaming
- Source
- Video with a decodable video stream; audio is optional
- Artifacts
- Master playlist, 1080p/720p variants, and six-second media segments
// Source: Video with a decodable video stream; audio is optional
// Alias: video.streaming
// Preset: hls_ladder_v1
// Artifacts: Master playlist, 1080p/720p variants, and six-second media segments
// Tier: Standard
// Testing: use job.wait(); production: persist job.id and consume the signed webhook
// npm install @mediaruntime/node
import { MediaRuntime } from "@mediaruntime/node";
const media = new MediaRuntime();
const job = await media.jobs.create({
"source": "https://cdn.example.com/media/feature.mp4",
"metadata": {
"asset_id": "asset_0426",
"recipe": "hls-streaming"
},
"outputs": [
"video.streaming"
],
"idempotencyKey": "asset:hls:v1"
});
console.log(job.id, job.status);Vertical social video
- Source
- Landscape, square, or portrait video
- Artifacts
- 1080×1920 H.264/AAC MP4 with a blurred 9:16 fill
// Source: Landscape, square, or portrait video
// Alias: video.social
// Preset: social_vertical_blur
// Artifacts: 1080×1920 H.264/AAC MP4 with a blurred 9:16 fill
// Tier: Premium
// Testing: use job.wait(); production: persist job.id and consume the signed webhook
// npm install @mediaruntime/node
import { MediaRuntime } from "@mediaruntime/node";
const media = new MediaRuntime();
const job = await media.jobs.create({
"source": "https://cdn.example.com/media/interview.mp4",
"metadata": {
"asset_id": "asset_0426",
"recipe": "vertical-social"
},
"outputs": [
"video.social"
],
"idempotencyKey": "asset:social:v1"
});
console.log(job.id, job.status);Responsive image derivatives
- Source
- JPG, PNG, WebP, or another supported still image
- Artifacts
- 1200×630 and 320×320 metadata-stripped WebP renditions
// Source: JPG, PNG, WebP, or another supported still image
// Alias: image.web
// Preset: image_multi_v1
// Artifacts: 1200×630 and 320×320 metadata-stripped WebP renditions
// Tier: Premium
// Testing: use job.wait(); production: persist job.id and consume the signed webhook
// npm install @mediaruntime/node
import { MediaRuntime } from "@mediaruntime/node";
const media = new MediaRuntime();
const job = await media.jobs.create({
"source": "https://cdn.example.com/media/product.png",
"metadata": {
"asset_id": "asset_0426",
"recipe": "image-derivatives"
},
"outputs": [
"image.web"
],
"idempotencyKey": "asset:image-derivatives:v1"
});
console.log(job.id, job.status);Audio plus transcript
- Source
- Audio, or video containing a decodable audio stream
- Artifacts
- 128 kbps AAC/M4A plus SRT and WebVTT transcripts
// Source: Audio, or video containing a decodable audio stream
// Alias: audio.transcription
// Preset: audio_aac_128k
// Artifacts: 128 kbps AAC/M4A plus SRT and WebVTT transcripts
// Tier: Standard
// Testing: use job.wait(); production: persist job.id and consume the signed webhook
// npm install @mediaruntime/node
import { MediaRuntime } from "@mediaruntime/node";
const media = new MediaRuntime();
const job = await media.jobs.create({
"source": "https://cdn.example.com/media/interview.mp4",
"metadata": {
"asset_id": "asset_0426",
"recipe": "audio-transcript"
},
"outputs": [
"audio.transcription"
],
"idempotencyKey": "asset:audio-transcript:v1"
});
console.log(job.id, job.status);Moderation plus watermarking
- Source
- One image or video; this example uses video
- Artifacts
- Watermarked 720p MP4, JPG poster, and moderation evidence report
// Source: One image or video; this example uses video
// Alias: video.web
// Preset: mp4_720p_h264_aac
// Artifacts: Watermarked 720p MP4, JPG poster, and moderation evidence report
// Tier: Premium
// Testing: use job.wait(); production: persist job.id and consume the signed webhook
// npm install @mediaruntime/node
import { MediaRuntime } from "@mediaruntime/node";
const media = new MediaRuntime();
const job = await media.jobs.create({
"source": "https://cdn.example.com/media/upload.mp4",
"metadata": {
"asset_id": "asset_0426",
"recipe": "moderation-watermark"
},
"moderation": {
"enabled": true,
"mode": "report",
"checks": [
"sexual",
"violence",
"dangerous"
]
},
"watermark": {
"enabled": true
},
"outputs": [
"video.web"
],
"idempotencyKey": "asset:moderation-watermark:v1"
});
console.log(job.id, job.status);{ "watermark": { "enabled": true } }만 보내면 MediaRuntime이 서버에 저장된 로고를 사용합니다.복사한 요청 JSON이 아니라 전체 처리 정책을 버전으로 관리하세요.
호스팅 레시피는 출력, 콘텐츠 검토, 워터마크 정책을 계정 범위의 변경 불가능한 버전으로 저장합니다. 최신 활성 버전은 이름으로, 배포가 절대 바뀌면 안 될 때는 `name@version`으로 지정하세요.
# Discover the built-in and account recipes available to this key.
mediaruntime recipes list
# A hosted recipe may be pinned to one immutable version.
mediaruntime run ./launch.mp4 \
--recipe team-video@3 \
--download ./launch.zip
# The raw API accepts the same reference.
curl -sS -X POST "https://mediaruntime.com/v1/jobs" \
-H "X-API-Key: $MEDIARUNTIME_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"source": "https://cdn.example.com/media/launch.mp4",
"recipe": "web-video@1",
"metadata": { "asset_id": "launch-01" }
}'recipe 확인 정보와 SHA-256 다이제스트를 제공합니다.web-video@1, social-video@1, ai-transcription@1입니다.작업에 계층형 시각 안전성 분석을 추가하세요.
콘텐츠 검토는 이미지 또는 비디오 입력 하나에 계층형 파이프라인을 사용합니다. report 모드는 근거를 반환하고 처리를 계속합니다. block 모드는 엔진 실행 전 fail-closed 게이트로 동작하여 block 또는 review 판정이면 작업을 거부하고 allow일 때만 계속합니다.
{
"source": "https://cdn.example.com/upload.mp4",
"metadata": { "media_type": "video", "asset_id": "asset_0426" },
"moderation": {
"enabled": true,
"mode": "report",
"checks": ["sexual", "violence", "dangerous"]
},
"outputs": [{
"type": "mp4",
"preset": "mp4_720p_h264_aac"
}]
}{
"moderation": {
"requested": {
"enabled": true,
"mode": "report",
"checks": ["sexual", "violence", "dangerous"],
"media_type": "video",
"phase": "phase1_video_report"
},
"result": {
"ok": true,
"media_type": "video",
"verdict": "review",
"flagged_checks": ["violence"],
"scores": {
"violence": { "yes": 0.82, "no": 0.18 }
},
"decisions": {
"violence": { "decision": "review", "raw_decision": "review" }
},
"evidence": {
"frames_sampled": 8,
"frames_flagged": [{
"frame_index": 3,
"timestamp_sec": 20,
"verdict": "review",
"flagged_checks": ["violence"]
}]
},
"video": {
"frame_interval_sec": 10,
"max_frames": 24
}
}
},
"meta": {
"moderation_result": {
"url": "https://storage.googleapis.com/.../moderation_result.json"
}
},
"usage": {
"breakdown": { "moderation_units": 120 }
}
}| 항목 | 값 | 설명 |
|---|---|---|
| 플랜 | Premium | 계정이 Premium이거나 자동 업그레이드가 허용되지 않으면 API가 403을 반환합니다. |
| 모드 | report 또는 block | report는 관찰용입니다. block은 엔진 실행 전에 block/review 판정을 거부합니다. |
| 검사 | sexual, violence, dangerous | 1~3개의 검사를 보냅니다. checks를 생략하면 세 가지 모두 선택됩니다. |
| 입력 | 이미지 또는 비디오 한 개 | 오디오 전용 입력, 배치, 샌드박스 검토는 거부됩니다. |
| 비디오 샘플링 | 고정 간격, 제한된 프레임 수 | 간격과 상한은 서비스가 결정합니다. 실제 값은 result.video와 result.evidence에서 확인하세요. |
| 판정 | allow, review 또는 block 신호 | report 모드는 실행을 차단하지 않습니다. block 모드는 fail-closed로 동작하여 allow만 계속하고 review 또는 block은 REJECTED로 종료합니다. |
| 산출물 | meta/moderation_result.json | 출력 ZIP에 포함되며, 가능한 경우 meta.moderation_result.url로도 제공됩니다. |
| 결제 | 분석한 프레임 단위 | 프레임당 1회 추론입니다. 이미지는 1프레임, 동영상은 최대 24프레임까지 샘플링합니다. result.evidence.frames_sampled를 기준으로 usage.breakdown.moderation_units에 정산됩니다. |
| 단독 실행 | outputs를 비울 수 있음 | outputs: []로 보내면 트랜스코딩 없이 검토만 수행합니다. 검토 비용만 청구됩니다. |
report를 사용하세요. 트랜스코딩 전에 MediaRuntime이 거부해야 한다면 block을 사용합니다. 거부된 작업에는 출력 번들이 없고 검토 단위만 청구됩니다. 분류 판정은 오류 가능성이 있으므로 필요하면 검토 및 이의 제기 경로를 유지하세요.이벤트를 신뢰하기 전에 원본 바이트를 검증하세요.
최종 이벤트는 최소 한 번 전달되며 순서는 보장되지 않습니다. HMAC-SHA256을 검증하고, 오래된 타임스탬프를 거부하고, event_id로 중복을 제거하고, 빠르게 응답한 뒤 무거운 작업은 큐로 넘기세요.
{
"event_id": "webhook_evt_job_1320c28b72104811b075a26a99496cf6",
"job_id": "job_1320c28b72104811b075a26a99496cf6",
"account_id": "acc_xxx",
"status": "COMPLETED",
"completedAt": "2026-08-09T02:41:23Z",
"billing": { "status": "PAID", "estimatedUnits": 31 },
"usage": { "units_total": 31, "breakdown": {} },
"delivery": {
"mode": "PULL",
"retentionDays": 7,
"expiresAt": "2026-08-16T02:41:23Z",
"bundle": {
"type": "zip",
"filename": "outputs.zip",
"download": {
"url": "https://mediaruntime.com/v1/jobs/job_1320c28b72104811b075a26a99496cf6/bundle?token=...",
"expiresAt": "2026-08-16T02:41:23Z"
}
}
},
"meta": {
"engine_result_url": "https://storage.googleapis.com/...",
"outputs_root_gs": "gs://.../jobs/acc_xxx/job_.../outputs",
"request_metadata": {
"producer": "my-api",
"entity_id": "video_01J8Y4",
"media_type": "video"
}
}
}delivery.bundle.download.url에서 전체 ZIP을 내려받거나, meta.engine_result_url을 조회해 개별 출력과 산출물 경로를 확인하세요.import express from "express";
import { MediaRuntime } from "@mediaruntime/node";
const media = new MediaRuntime();
const app = express();
// Register this route before any express.json() middleware.
app.post(
"/webhooks/mediaruntime",
express.raw({ type: "application/json" }),
media.webhooks.express(async (event, _req, res) => {
// Persist and deduplicate event.id before acknowledging.
console.log(event.id, event.jobId, event.status);
res.sendStatus(204);
}),
);전달 규칙
- deliver_webhook: false로 제출하면 배치 완료를 포함한 웹훅 전달 없이 SDK 폴링을 사용할 수 있습니다. retry-webhook으로 이를 변경할 수 없으며 HTTP 409가 반환됩니다.
- 서명 검증과 영속적인 큐 적재/중복 제거가 끝난 뒤에만 2xx를 반환하세요.
- event_id를 멱등성 키로 사용하세요.
- meta.request_metadata를 사용하면 별도의 조회 테이블 없이 엔터티를 찾을 수 있습니다.
- delivery.expiresAt 이전에 보관된 출력을 내려받으세요.
- FAILED 또는 REJECTED 이벤트에는 error.code/message가 있으며 사용할 수 있는 번들은 없습니다. PARTIAL 배치는 error.code가 BATCH_PARTIAL이며, 각 하위 작업의 상태와 성공한 번들은 delivery.items에서 확인합니다.
웹훅을 쓰기 어려울 때는 폴링하세요.
작업을 제출하면 job_id가 즉시 반환됩니다. 완료를 가장 빠르게 알 수 있는 방법은 여전히 웹훅이지만, 로컬 개발이나 공개 엔드포인트가 없는 환경, 정산 대조, 고객 문의 확인에는 폴링을 사용할 수 있습니다.
curl -sS "https://mediaruntime.com/v1/jobs/$JOB_ID" \
-H "X-API-Key: $MEDIARUNTIME_API_KEY"# Newest first. Filter by status and page with the cursor.
curl -sS "https://mediaruntime.com/v1/jobs?status=COMPLETED&limit=25" \
-H "X-API-Key: $MEDIARUNTIME_API_KEY"
# Next page: pass the previous response's next_cursor
curl -sS "https://mediaruntime.com/v1/jobs?limit=25&cursor=$NEXT_CURSOR" \
-H "X-API-Key: $MEDIARUNTIME_API_KEY"{
"job_id": "job_2ee8db582cdf4a2fafb49d52218b3159",
"status": "COMPLETED",
"tier": {
"requested": "premium",
"required": "standard",
"effective": "premium",
"billed": "standard",
"reasons": []
},
"usage": { "units_total": 4 },
"billing": {
"status": "PAID",
"currency": "USD",
"unit_price_cents": 1,
"final_units": 4,
"final_amount_cents": 4
},
"bundle": {
"available": true,
"download_url": "https://mediaruntime.com/v1/jobs/job_2ee8.../bundle?token=...",
"expires_at": "2026-08-18T03:14:27Z",
"size_bytes": 13056793,
"retention_days": 7
},
"media": {
"format": "mov,mp4,m4a,3gp,3g2,mj2",
"duration_sec": 61.5,
"bit_rate": 8000000,
"video": {
"codec": "h264",
"profile": "High",
"width": 1080,
"height": 1920,
"encoded_width": 1920,
"encoded_height": 1080,
"fps": 29.97,
"rotation_deg": 90,
"is_rotated": true,
"orientation": "portrait"
},
"audio": { "codec": "aac", "sample_rate_hz": 48000, "channels": 2, "layout": "stereo" },
"streams": { "video": 1, "audio": 1, "other": 0 }
},
"metadata": { "asset_id": "asset_0426", "media_type": "video" },
"error": null,
"completed_at": "2026-08-11T03:14:53Z"
}requested는 작업을 제출한 API 키의 등급, required는 실제로 필요한 등급, effective는 실행된 레인, billed는 실제 청구된 등급입니다. 프리미엄 키로 표준 작업을 실행하면 requested: premium이지만 billed: standard가 됩니다. 키가 아니라 수행된 작업 기준으로 청구됩니다.media는 제출 시점에 MediaRuntime이 입력 파일을 분석한 결과이며, 작업 수락 여부를 결정하는 것과 동일한 분석 결과입니다. 호환되지 않는 조합으로 작업이 REJECTED된 이유를 여기서 확인할 수 있습니다. 이미지 출력에 MP3를 보내면 streams.video: 0으로 표시되고, 프레임 출력에 정지 이미지를 보내면 duration_sec이 없습니다. video.width/height는 회전이 적용된 표시 기준 값이므로, 세로로 촬영한 휴대폰 영상은 encoded_width/encoded_height가 1920x1080이어도 1080x1920으로 표시됩니다. 모든 필드는 선택적이며, 값이 없다는 것은 0이 아니라 분석에서 보고되지 않았다는 뜻입니다.GET /v1/jobs/{job_id}/moderation은 검토 결과만 반환하므로, 판정을 폴링하는 클라이언트가 매번 청구 및 번들 정보까지 받아올 필요가 없습니다. verdict, 항목별 decision과 confidence, 에스컬레이션 likelihoods를 제공합니다. review_only로 표시된 항목은 참고용입니다. review 판정을 유발할 수는 있지만 단독으로 차단하지는 않습니다. 작업은 있으나 검토를 요청하지 않은 경우 **404를 반환합니다.** 빈 성공 응답은 "검토했으나 문제 없음"과 구분되지 않기 때문입니다. 각 판정의 임계값은 공개되지 않습니다.GET /v1/jobs/{job_id}/media-report는 번들을 내려받지 않고 media_report_v1 문서를 반환합니다. 보통은 report에 인라인으로 포함되지만, 리포트가 매우 큰 경우 인라인으로 저장되지 않아 report가 null이 되고 download_url로 받을 수 있으므로 두 경우를 모두 처리하세요. 리포트가 없는 작업은 404를 반환합니다.GET /v1/jobs/{job_id}/compatibility-report는 ZIP을 내려받지 않고 버전이 지정된 compatibility_report_v1 문서를 반환합니다. 보수적인 프로필 5개, 예상값과 실제값의 규칙별 근거, 비호환 프로필을 수정할 기존 프리셋을 포함합니다. 이는 모든 기기의 완전한 인증이 아닌 실행 가능한 지침입니다. 인라인 report와 download_url을 모두 처리하세요. 프리셋을 요청하지 않은 작업은 404를 반환합니다.GET /v1/jobs/{job_id}/codes는 ZIP을 내려받지 않고 제한된 code_detect_v1 스캔 결과를 반환합니다. 이미지, 비디오 및 애니메이션 시각 입력과 커버 아트가 포함된 오디오를 지원합니다. 순수 오디오는 명확한 안내와 함께 거부됩니다. 최대 12개 샘플 프레임과 프레임당 16개의 고유 코드를 유지합니다. 모든 decoded_text는 신뢰할 수 없는 텍스트로 취급하고 HTML로 렌더링하거나 URL을 자동으로 열지 마세요. 근거 프레임은 ZIP의 번들 경로로 참조됩니다.| 필드 | 타입 | 설명 |
|---|---|---|
| status | string | QUEUED, PROCESSING, COMPLETED, FAILED, REJECTED 또는 배치 전용 PARTIAL 중 하나입니다. |
| tier | object | requested / required / effective / billed와, 프리미엄이 필요했던 경우 reasons가 포함됩니다. |
| usage.units_total | integer | 해당 작업의 청구 단위입니다. |
| billing | object | 통화, 단가, 예상 및 최종 단위와 금액입니다. |
| bundle.download_url | string | 작업 범위로 제한된 만료형 번들 URL입니다. 단일 작업 조회에서만 반환됩니다. |
| media | object | 제출 시점에 분석한 입력 파일의 실제 정보입니다. 이전 작업에서는 null입니다. |
| media.video.width/height | integer | 회전이 이미 적용된 표시 기준 해상도입니다. |
| media.duration_sec | number | 타임라인이 없는 정지 이미지에서는 값이 없습니다. |
| metadata | object | 제출한 metadata 객체를 그대로 반환합니다. |
| error | string | FAILED, REJECTED 또는 배치 전용 PARTIAL일 때 값이 있으며, 그 외에는 null입니다. |
폴링 규칙
- 가능하면 웹훅을 사용하고, 받을 수 없을 때만 폴링하세요.
- offset이 아니라 next_cursor로 페이지를 넘기세요. 작업이 갱신되면 순서가 바뀝니다.
- 소유하지 않은 작업 ID는 존재하지 않는 ID와 동일하게 404를 반환합니다.
- 목록 응답에는 번들 URL이 없습니다. 다운로드하려면 단일 작업을 조회하세요.
- 폴링 간격을 두세요. 종료 상태는 더 이상 바뀌지 않습니다.
선불 충전, 사용한 만큼 결제, 실제 사용량 기준 정산.
카드를 등록하고 지갑을 충전하면 정기 구독 없이 작업을 제출할 수 있습니다. MediaRuntime은 실행 전에 예상 금액을 예약하고, 작업이 최종 상태에 도달하면 최종 금액을 정산합니다.
| 플랜 | 시작 사용 요금 | 최소 충전액 | 기본 자동 충전 |
|---|---|---|---|
| Standard Pay-As-You-Go | $0.02부터 | $5.00 | 잔액 $2.00 시점에 $5.00 |
| Premium Pay-As-You-Go | $0.05부터 | $20.00 | 잔액 $5.00 시점에 $20.00 |
billing 및 usage 필드를 사용하세요.지갑 규칙
- 사용 가능 잔액은 지갑 잔액에서 실행 중인 작업에 예약된 금액을 뺀 값입니다.
- 사용 가능 잔액이 부족하면 실행 전에 HTTP 402를 반환합니다.
- 자동 충전은 선택 사항이며 등록된 카드가 필요합니다.
- 업그레이드가 허용되지 않은 상태에서 Premium 전용 요청을 보내면 403을 반환합니다.
전송 실패는 재시도하고, 잘못된 요청은 재시도하지 마세요.
모든 응답에는 X-Request-Id가 포함됩니다. 오류는 호환성을 위해 detail/message를 유지하면서 정규화된 error 객체를 추가합니다. 요청 ID, 코드, 상태만 기록하고 API 키, 서명 URL, 요청 본문은 기록하지 마세요.
| 상태 코드 | 코드 | 의미 | 연동에서 해야 할 일 |
|---|---|---|---|
| 400 | invalid_request | 요청이 논리적으로 잘못되었거나 추정기가 거부했습니다. | 요청을 수정하고 그대로 재시도하지 마세요. |
| 401 | authentication_error | API 키가 유효하지 않거나 만료 또는 폐기되었습니다. | 키를 수정하거나 교체하세요. |
| 402 | billing_required | 계정, 지갑 또는 결제 사전 점검이 작업 비용을 감당할 수 없습니다. | 먼저 지갑을 충전하거나 결제 문제를 해결하세요. |
| 403 | permission_denied | 플랜, 역할 또는 기능 제한이 요청을 허용하지 않습니다. | 플랜이나 요청을 변경하세요. |
| 404 | not_found | 소유한 리소스가 없거나 소유자 범위 때문에 숨겨졌습니다. | 식별자를 수정하고 그대로 재시도하지 마세요. |
| 409 | idempotency_in_progress / conflict | 같은 키의 작업이 진행 중이거나 활성 작업과 충돌합니다. | error.retryable이 true일 때만 재시도하세요. |
| 410 | gone | 수명이 짧은 토큰이 만료되었습니다. | 새 결과나 토큰을 받으세요. |
| 413 | request_too_large | HTTP 요청 본문이 2MiB를 초과합니다. | 미디어는 별도로 업로드하고 URL만 보내세요. |
| 422 | validation_error / idempotency_conflict / unprocessable_entity | 검증에 실패했거나 멱등성 키가 다른 본문에 재사용되었습니다. | 지정된 필드나 키를 수정하세요. |
| 429 | rate_limited | 계정 또는 키에 속도 제한이 적용되었습니다. | Retry-After가 있으면 해당 시간만큼 기다리고, 없으면 상한이 있는 지수 백오프와 지터를 적용하세요. |
| 500 | internal_error | 게이트웨이에서 예기치 않은 오류가 발생했습니다. | 백오프로 안전하게 재시도하세요. |
| 502 | upstream_error | 일시적인 플랫폼 의존성 오류입니다. | 백오프로 안전하게 재시도하세요. |
| 503 | service_unavailable | 의존성 또는 실행 레인을 사용할 수 없습니다. | 백오프로 안전하게 재시도하세요. |
{
"error": {
"code": "billing_required",
"message": "Insufficient wallet balance for this job",
"status": 402,
"retryable": false,
"request_id": "req_7fa01eec9b6248a5a7be2d60ff4bb978",
"details": null
},
"request_id": "req_7fa01eec9b6248a5a7be2d60ff4bb978",
"detail": "Insufficient wallet balance for this job"
}대부분의 연동에 필요한 최소한의 표면.
아래 서버 간 엔드포인트는 모두 X-API-Key를 사용합니다. 토큰이 포함된 번들 URL만 예외이며, 자체적으로 수명이 짧고 작업 범위로 제한된 자격 증명을 갖습니다.
| 메서드 | 경로 | 용도 |
|---|---|---|
| POST | /v1/upload-url | 가져올 수 있는 미디어 URL이 없을 때 선택적으로 15분 유효 업로드 대상을 생성합니다. |
| POST | /v1/jobs | 단일 입력 또는 배치 미디어 작업을 큐에 등록합니다. |
| GET | /v1/jobs/{job_id} | 단일 작업의 상태, 등급 결정, 사용량, 청구, 번들 링크입니다. |
| GET | /v1/jobs | 최신순 작업 목록입니다. ?status= 필터와 커서 페이징을 지원합니다. |
| GET | /v1/jobs/{job_id}/moderation | 단일 작업의 콘텐츠 검토 결과입니다. 검토를 요청하지 않은 작업은 404를 반환합니다. |
| GET | /v1/jobs/{job_id}/clip-candidates | 렌더링 전에 검토하세요 |
| GET | /v1/jobs/{job_id}/media-report | 단일 작업의 포렌식 미디어 리포트입니다. media_report_v1을 요청하지 않은 작업은 404를 반환합니다. |
| GET | /v1/jobs/{job_id}/compatibility-report | 단일 작업의 버전 지정 호환성 판정입니다. compatibility_report_v1을 요청하지 않은 작업은 404를 반환합니다. |
| GET | /v1/jobs/{job_id}/codes | 제한된 QR/바코드 감지 및 근거 프레임 참조입니다. code_detect_v1을 요청하지 않은 작업은 404를 반환합니다. |
| GET | /v1/jobs/{job_id}/bundle?token=... | 작업 범위 토큰으로 번들을 받습니다. API 키가 필요하지 않습니다. |
| POST | /v1/jobs/{job_id}/retry-webhook | 본인 소유 작업의 최종 웹훅을 다시 전송합니다. |
| POST | /v1/account/watermark-logo/upload-url | 계정 PNG 로고용 업로드 대상을 생성합니다. |
| POST | /v1/account/watermark-logo/confirm | 로고와 배치 설정을 확정합니다. |
버전이 지정된 OpenAPI 3.1 문서를 클라이언트 생성, 요청 검증 및 계약 검토에 사용하세요. 지원되는 공개 API만 포함하며 API 키가 필요하지 않습니다.