Peako Studio API Reference
Complete reference for Peako Studio API. Base URL: https://peako.shin0x.space
All requests require authentication via X-API-Key header or peako_session cookie.
Auth
Sign In with Google
GET /auth/google
Initiates OAuth 2.0 flow with PKCE protection.
Response: 302 redirect to Google consent screen
Example:
curl https://peako.shin0x.space/auth/google
# Redirects to: https://accounts.google.com/o/oauth2/v2/auth?...
Google Callback
GET /auth/google/callback?code=CODE&state=STATE
Completes OAuth flow after user consent.
Response: 302 redirect to dashboard + sets peako_session cookie
Example:
curl https://peako.shin0x.space/auth/google/callback?code=...&state=...
# Sets httpOnly cookie, redirects to /dashboard
Logout
POST /auth/logout
Clears session.
Response: 302 redirect to /login
Example:
curl -X POST -b "peako_session=TOKEN" https://peako.shin0x.space/auth/logout
Templates (Project/Design State)
Get Template
GET /api/templates/:id
Fetch a template's design state.
Response:
{
"id": "template-123",
"name": "My Video",
"compositionState": {
"tracks": [...],
"duration": 10,
"fps": 30,
"width": 1920,
"height": 1080
},
"createdAt": 1708852843000,
"updatedAt": 1708852890000
}
Create Template
POST /api/templates
Create a new template.
Request:
{
"name": "My Video",
"compositionState": {
"tracks": [],
"duration": 10,
"fps": 30,
"width": 1920,
"height": 1080
}
}
Response:
{
"id": "template-123",
"name": "My Video",
"createdAt": 1708852843000
}
Update Template
PUT /api/templates/:id
Save template design state.
Request:
{
"name": "Updated Name",
"compositionState": { ... }
}
Response:
{
"id": "template-123",
"updatedAt": 1708852890000
}
List Templates
GET /api/templates
Get all templates for current user.
Response:
{
"templates": [
{
"id": "template-1",
"name": "Video 1",
"updatedAt": 1708852890000
},
{
"id": "template-2",
"name": "Video 2",
"updatedAt": 1708852800000
}
]
}
Render Jobs
Submit Render Job
POST /api/templates/:id/render
Queue a template for rendering. Returns immediately with job ID.
Request:
{
"quality": "1080p",
"format": "mp4",
"delivery": "async"
}
Params:
quality—"hd"(720p),"1080p"(default),"4k"format—"mp4"(default),"webm","mov"delivery—"response"(binary),"storage"(CDN),"async"(poll)
Response (202 - Queued):
{
"jobId": "job-550e8400-e29b-41d4-a716",
"status": "queued"
}
Get Job Status
GET /api/job/:jobId
Poll rendering job status.
Response:
{
"jobId": "job-550e8400-e29b-41d4-a716",
"status": "queued|active|done|failed",
"progress": 45,
"outputUrl": "https://cdn.example.com/output.mp4",
"error": null,
"createdAt": 1708852843000,
"updatedAt": 1708852890000
}
Statuses:
queued— waiting for workeractive— rendering nowdone— complete, outputUrl readyfailed— error occurred
Cancel Job
DELETE /api/job/:jobId
Cancel a queued job (cannot cancel active/done/failed).
Response:
{
"jobId": "job-550e8400-e29b-41d4-a716",
"status": "cancelled"
}
Assets
Upload Asset
POST /api/assets/upload
Upload video, audio, or image file.
Request (Multipart):
curl -X POST \
-F "file=@video.mp4" \
-H "X-API-Key: YOUR_KEY" \
https://peako.shin0x.space/api/assets/upload
Response:
{
"url": "https://cdn.example.com/assets/video.mp4",
"filename": "video.mp4",
"mimeType": "video/mp4",
"size": 5242880,
"duration": 120
}
Accepted types: video/mp4, video/webm, video/quicktime, audio/mpeg, audio/wav, image/jpeg, image/png, image/gif
Video Operations
All operations support two input models:
Multipart (local file):
curl -X POST \
-F "file=@video.mp4" \
-F "start=5" \
-F "duration=10" \
https://peako.shin0x.space/api/OPERATION
JSON (URL):
{
"src_url": "https://cdn.example.com/video.mp4",
"start": 5,
"duration": 10
}
Trim
POST /api/trim
Trim video to time range.
Params:
start— start time in secondsduration— clip length in secondsoutput_format—"mp4"(default),"webm","mov"delivery—"response","storage","async"(default:"response")
Example Request (JSON):
{
"src_url": "https://example.com/video.mp4",
"start": 10,
"duration": 30,
"delivery": "async"
}
Example Response:
{
"jobId": "job-123",
"status": "queued"
}
Merge
POST /api/merge
Concatenate multiple video clips.
Request:
{
"clips": [
{ "src_url": "https://example.com/clip1.mp4" },
{ "src_url": "https://example.com/clip2.mp4" }
],
"delivery": "async"
}
Response:
{
"jobId": "job-123",
"status": "queued"
}
Mute
POST /api/mute
Silence audio or a region of video.
Request:
{
"src_url": "https://example.com/video.mp4",
"muteStart": 5,
"muteDuration": 3,
"delivery": "async"
}
Add Audio
POST /api/add-audio
Mix audio track or background music into video.
Request (JSON):
{
"video_src_url": "https://example.com/video.mp4",
"audio_src_url": "https://example.com/audio.mp3",
"volume": 0.8,
"delivery": "async"
}
Params:
volume— 0.0 (mute) to 1.0 (full), default: 1.0
Subtitle
POST /api/subtitle
Burn SRT subtitles into video.
Request:
{
"src_url": "https://example.com/video.mp4",
"srt_url": "https://example.com/captions.srt",
"font_size": 24,
"color": "#FFFFFF",
"position": "bottom",
"delivery": "async"
}
Params:
font_size— text size (default: 24)color— hex color (default:"#FFFFFF")position—"top","center","bottom"(default:"bottom")
Blur
POST /api/blur
Blur regions (faces, text, etc.) in video.
Request:
{
"src_url": "https://example.com/video.mp4",
"regions": [
{
"x": 100,
"y": 100,
"width": 200,
"height": 200,
"blurRadius": 20,
"startTime": 0,
"endTime": 5
}
],
"delivery": "async"
}
Region params:
x,y,width,height— pixel coordinatesblurRadius— 1–50 (default: 15)startTime,endTime— seconds
Speed Ramp
POST /api/speed-ramp
Apply speed keyframes (slow-mo to speed-up).
Request:
{
"src_url": "https://example.com/video.mp4",
"startSpeed": 0.5,
"endSpeed": 2.0,
"delivery": "async"
}
Params:
startSpeed— 0.1–4.0 (default: 1.0)endSpeed— 0.1–4.0 (default: 1.0)
Speed-ramp is linear only — cannot do piecewise keypoints. Uses FFmpeg, not Remotion.
Effects & Transitions
Get Effects Catalog
GET /api/effects/catalog
List all available effects.
Response:
{
"effects": [
{
"id": "fade-in",
"name": "Fade In",
"category": "appear",
"description": "Fades clip up from black",
"preview": "https://cdn.example.com/fade-in.png",
"params": [
{
"name": "duration",
"type": "number",
"default": 0.5,
"min": 0.1,
"max": 5.0
}
]
}
]
}
Get Transitions Catalog
GET /api/transitions/catalog
List all available transitions.
Response:
{
"transitions": [
{
"id": "fade",
"name": "Fade",
"description": "Fade from one clip to next",
"preview": "https://cdn.example.com/fade.png",
"duration": [0.2, 2.0]
}
]
}
System
Health Check
GET /health
Check API status.
Response:
{
"status": "ok",
"qsv": true,
"workers": 4,
"queued": 2,
"redis": true
}
Fields:
status—"ok"or"degraded"qsv— Hardware video encoding availableworkers— Active job workersqueued— Jobs waitingredis— Redis connection OK
User Account
Get Current User
GET /auth/me
Get authenticated user info.
Response:
{
"id": "user-123",
"email": "user@example.com",
"createdAt": 1708852843000
}
Rotate API Key
POST /auth/key/rotate
Invalidate current key, generate new one.
Response:
{
"apiKey": "pk_live_NEW_KEY_HERE",
"rotatedAt": 1708852890000
}
Error Codes
| Code | Status | Meaning |
|---|---|---|
AUTH_REQUIRED | 401 | Missing or invalid API key/session |
INVALID_REQUEST | 400 | Bad params or malformed body |
FILE_TOO_LARGE | 413 | File exceeds size limit (2GB) |
NOT_FOUND | 404 | Resource not found |
CONFLICT | 409 | Cannot perform in current state |
RENDER_FAILED | 500 | Video operation error |
INTERNAL_ERROR | 500 | Unexpected error |
Rate Limiting
- Global: 100 requests/minute
- Per User: 20 concurrent jobs
- Per IP: 1000 requests/hour
Check response headers:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1708852890
Deprecation
Old v1 routes still work but are deprecated (sunset 2026-07-01):
POST /api/v1/trim → use POST /api/trim
POST /api/v1/merge → use POST /api/merge
POST /api/v1/mute → use POST /api/mute
etc.
All v1 responses include deprecation headers.