Public proxy URL
GET /v1/{projectKey}/{src}.{format} — the URL pattern that powers every embed.
Public proxy URL
This isn't a typical REST endpoint — it's a URL pattern. Every Motioness embed (<img>, <video>, OG tags, RSS, MDX) is just a hand-crafted URL on motioness.com.
textGET https://motioness.com/v1/{projectKey}/{srcWithoutScheme}.{format}[?params]
Auth
Two modes per project. Pick one:
- Allowlist — source URL plain text, origin must match
allowed_origins. - Signed — source URL base64url-encoded, request signed with HMAC-SHA256.
See allowlist vs signed.
Path
| Segment | Description |
|---|---|
projectKey | Public project key (pk_…) |
srcWithoutScheme | Source URL with https:// stripped (allowlist) or base64url-encoded (signed) |
format | .mp4 for video, .jpg for poster |
Query parameters
| Param | Type | Default | Effect |
|---|---|---|---|
motion | subtle / medium / kinetic | project default | Motion intensity |
mode | loop / animate-in / animate-on-hover | project default | Playback shape |
dur | int 2..8 | project default | Output length (seconds) |
prompt | string ≤500 | — | Override brand-aware prompt |
seed | int | — | Deterministic seed |
aspect | 1:1 / 16:9 / 9:16 / preserve | preserve | Output aspect (seedance only) |
idem | string | — | Force distinct asset from same source |
wait_ms | int 0..10000 | 0 | Long-poll deadline |
sig | hex | — | HMAC signature (signed mode only) |
exp | unix seconds | — | Signature expiry (signed mode only) |
Responses
200 (mp4 ready, served from R2 / edge cache)
textHTTP/1.1 200 OKContent-Type: video/mp4X-Motioness-Asset-Id: a25b330a71e2bc6bX-Motioness-Stage: readyX-Motioness-Loop: nativeX-Request-Id: req_abc12345Cache-Control: public, max-age=31536000, immutablecf-cache-status: HIT
200 (jpg poster)
textHTTP/1.1 200 OKContent-Type: image/jpegCache-Control: public, max-age=31536000, immutable
202 (mp4 generating)
httpHTTP/1.1 202 AcceptedRetry-After: 30X-Motioness-Asset-Id: a25b…X-Motioness-Stage: queuedX-Motioness-Eta-Ms: 28000Content-Type: application/json{ "status": "pending", "asset_id": "a25b330a71e2bc6b", "eta_seconds": 28}
403 (forbidden)
json{ "error": "origin not in allowlist" }{ "error": "unknown project key" }{ "error": "invalid signature" }{ "error": "signing not configured or signature missing" }
415 / 413 (source rejected)
json{ "error": "unsupported type: image/svg+xml — png/jpeg/webp only" }{ "error": "file exceeds 10 MB limit" }
429 (quota)
json{ "error": "monthly limit reached", "upgrade_url": "https://motioness.com/pricing" }
502 (terminal failure)
json{ "error": "last generation failed", "asset_id": "a25b…", "hint": "POST /api/v1/regenerate/{asset_id}"}
SSE companion
For live status, the public proxy also exposes:
textGET /v1/{projectKey}/events/{assetId}
See SSE streaming.
Why this isn't in the OpenAPI spec
The proxy URL has variable path segments (the source URL itself is part of the path) and a polymorphic response (mp4 bytes vs JSON). OpenAPI's paths model doesn't express that cleanly. We document it manually here and keep the structured REST API in the auto-generated reference.