Vimeo OTT
"Vimeo OTT API: video-on-demand platform, subscription management, customer auth, content libraries, embed players, analytics, webhook events"
Vimeo OTT (formerly VHX) is a white-label video monetization platform that lets you build subscription and transactional video-on-demand services under your own brand. Unlike Vimeo's standard platform, OTT is designed for content owners who want to sell access to video libraries via subscriptions (SVOD), rentals, or purchases (TVOD). The API follows REST conventions with OAuth2 authentication. The core data model revolves around Sites (your branded destination), Products (subscription plans or individual purchases), Customers (authenticated viewers), and Videos organized into Collections. Design your integration around customer lifecycle management and entitlement checks — most API calls involve verifying what a customer has purchased and serving them the appropriate content. ## Key Points - Always verify customer entitlements server-side before serving embed URLs or player tokens. - Use webhook events for subscription lifecycle tracking instead of polling customer records. - Organize content into collections with meaningful types (series for episodic, category for browsing). - Store the Vimeo OTT customer ID alongside your own user records for quick lookups. - Use the product API to manage multiple subscription tiers or one-time purchase options. - Implement proper pagination when listing videos, customers, or collections — responses are paginated by default. - Do not rely on client-side entitlement checks; always verify access on the server before generating embed tokens. - Do not hardcode product IDs across your app; fetch and cache them so pricing changes don't require code deployments. - Do not expose your OTT API key to the client; all API calls should be proxied through your server. - Do not ignore webhook delivery failures; configure retry logic and a dead-letter queue for missed events. - Do not assume all customers have active subscriptions; always check the `plan` field or product entitlements before granting access. ## Quick Example ``` VIMEO_OTT_API_KEY=your-api-key VIMEO_OTT_SITE_ID=your-site-id ```
skilldb get video-services-skills/Vimeo OTTFull skill: 288 linesVimeo OTT
Core Philosophy
Vimeo OTT (formerly VHX) is a white-label video monetization platform that lets you build subscription and transactional video-on-demand services under your own brand. Unlike Vimeo's standard platform, OTT is designed for content owners who want to sell access to video libraries via subscriptions (SVOD), rentals, or purchases (TVOD). The API follows REST conventions with OAuth2 authentication. The core data model revolves around Sites (your branded destination), Products (subscription plans or individual purchases), Customers (authenticated viewers), and Videos organized into Collections. Design your integration around customer lifecycle management and entitlement checks — most API calls involve verifying what a customer has purchased and serving them the appropriate content.
Setup
Vimeo OTT uses OAuth2 Bearer tokens. Obtain your API key from your OTT admin dashboard:
const OTT_API_KEY = process.env.VIMEO_OTT_API_KEY!;
const OTT_BASE_URL = "https://api.vhx.tv";
async function ottRequest<T>(path: string, options: RequestInit = {}): Promise<T> {
const res = await fetch(`${OTT_BASE_URL}${path}`, {
...options,
headers: {
Authorization: `Bearer ${OTT_API_KEY}`,
"Content-Type": "application/json",
...options.headers,
},
});
if (!res.ok) {
const error = await res.text();
throw new Error(`Vimeo OTT API error ${res.status}: ${error}`);
}
return res.json();
}
Environment variables required:
VIMEO_OTT_API_KEY=your-api-key
VIMEO_OTT_SITE_ID=your-site-id
Key Techniques
Customer Management
interface OttCustomer {
id: number;
email: string;
name: string;
created_at: string;
plan: { id: number; name: string } | null;
_links: Record<string, { href: string }>;
}
// Create a customer account
async function createCustomer(email: string, name: string, password: string): Promise<OttCustomer> {
return ottRequest<OttCustomer>("/customers", {
method: "POST",
body: JSON.stringify({
email,
name,
password,
product: `https://api.vhx.tv/products/${process.env.VIMEO_OTT_PRODUCT_ID}`,
}),
});
}
// Retrieve a customer and their entitlements
async function getCustomer(customerId: number): Promise<OttCustomer> {
return ottRequest<OttCustomer>(`/customers/${customerId}`);
}
// List all customers with pagination
async function listCustomers(page = 1, perPage = 25) {
return ottRequest<{ _embedded: { customers: OttCustomer[] }; total: number }>(
`/customers?page=${page}&per_page=${perPage}`
);
}
Product and Subscription Management
interface OttProduct {
id: number;
name: string;
price: { cents: number; currency: string };
product_type: "subscription" | "purchase" | "rental";
is_active: boolean;
}
async function listProducts(): Promise<OttProduct[]> {
const result = await ottRequest<{ _embedded: { products: OttProduct[] } }>("/products");
return result._embedded.products;
}
// Grant a customer access to a product (e.g., after external payment)
async function addProductToCustomer(customerId: number, productId: number) {
return ottRequest(`/customers/${customerId}/products`, {
method: "PUT",
body: JSON.stringify({
product: `https://api.vhx.tv/products/${productId}`,
}),
});
}
// Revoke access
async function removeProductFromCustomer(customerId: number, productId: number) {
return ottRequest(`/customers/${customerId}/products`, {
method: "DELETE",
body: JSON.stringify({
product: `https://api.vhx.tv/products/${productId}`,
}),
});
}
Video and Collection Management
interface OttVideo {
id: number;
title: string;
description: string;
duration: { seconds: number; formatted: string };
thumbnail: { small: string; medium: string; large: string };
status: string;
created_at: string;
_links: Record<string, { href: string }>;
}
interface OttCollection {
id: number;
name: string;
description: string;
items_count: number;
collection_type: "series" | "movie" | "category" | "playlist";
}
// List videos
async function listVideos(page = 1): Promise<OttVideo[]> {
const result = await ottRequest<{ _embedded: { videos: OttVideo[] } }>(
`/videos?page=${page}&per_page=25`
);
return result._embedded.videos;
}
// List collections (series, categories)
async function listCollections(): Promise<OttCollection[]> {
const result = await ottRequest<{ _embedded: { collections: OttCollection[] } }>(
"/collections"
);
return result._embedded.collections;
}
// Get items in a collection
async function getCollectionItems(collectionId: number): Promise<OttVideo[]> {
const result = await ottRequest<{ _embedded: { items: OttVideo[] } }>(
`/collections/${collectionId}/items`
);
return result._embedded.items;
}
Checking Customer Entitlements
// Verify a customer has access to a specific video before serving it
async function checkVideoAccess(customerId: number, videoId: number): Promise<boolean> {
try {
const watchlist = await ottRequest<{ _embedded: { videos: OttVideo[] } }>(
`/customers/${customerId}/watchlist?video=${videoId}`
);
return watchlist._embedded.videos.length > 0;
} catch {
return false;
}
}
// Get customer's authorized videos
async function getCustomerVideos(customerId: number) {
return ottRequest<{ _embedded: { videos: OttVideo[] } }>(
`/customers/${customerId}/watchlist`
);
}
Embedding the Player
// Generate an authenticated embed URL for a customer
function getEmbedUrl(videoId: number): string {
return `https://embed.vhx.tv/videos/${videoId}`;
}
// React component for embedded player
interface OttPlayerProps {
videoId: number;
customerToken: string;
}
function OttPlayer({ videoId, customerToken }: OttPlayerProps) {
const src = `https://embed.vhx.tv/videos/${videoId}?token=${customerToken}`;
return (
<iframe
src={src}
width="100%"
style={{ aspectRatio: "16/9", border: "none" }}
allowFullScreen
allow="autoplay; fullscreen"
referrerPolicy="origin"
/>
);
}
Webhook Events
interface OttWebhookEvent {
event: string;
data: {
customer_id?: number;
product_id?: number;
video_id?: number;
[key: string]: unknown;
};
}
async function handleOttWebhook(req: Request): Promise<Response> {
const event: OttWebhookEvent = await req.json();
switch (event.event) {
case "customer.created":
await onNewCustomer(event.data.customer_id!);
break;
case "customer.product.created":
// Customer subscribed or purchased
await onSubscription(event.data.customer_id!, event.data.product_id!);
break;
case "customer.product.deleted":
// Subscription canceled or access revoked
await onCancellation(event.data.customer_id!, event.data.product_id!);
break;
case "video.created":
await onVideoAdded(event.data.video_id!);
break;
}
return new Response("OK", { status: 200 });
}
Analytics
async function getVideoAnalytics(videoId: number, from: string, to: string) {
return ottRequest(
`/analytics?video_id=${videoId}&from=${from}&to=${to}&type=traffic`
);
}
async function getSiteOverview() {
return ottRequest("/analytics?type=subscribers");
}
Best Practices
- Always verify customer entitlements server-side before serving embed URLs or player tokens.
- Use webhook events for subscription lifecycle tracking instead of polling customer records.
- Organize content into collections with meaningful types (series for episodic, category for browsing).
- Store the Vimeo OTT customer ID alongside your own user records for quick lookups.
- Use the product API to manage multiple subscription tiers or one-time purchase options.
- Implement proper pagination when listing videos, customers, or collections — responses are paginated by default.
Anti-Patterns
- Do not rely on client-side entitlement checks; always verify access on the server before generating embed tokens.
- Do not hardcode product IDs across your app; fetch and cache them so pricing changes don't require code deployments.
- Do not expose your OTT API key to the client; all API calls should be proxied through your server.
- Do not ignore webhook delivery failures; configure retry logic and a dead-letter queue for missed events.
- Do not assume all customers have active subscriptions; always check the
planfield or product entitlements before granting access.
Install this skill directly: skilldb add video-services-skills
Related Skills
Amazon IVS
"Amazon Interactive Video Service: low-latency live streaming, real-time stages, chat, stream recording, channel management, viewer analytics"
Api.video
"api.video: video hosting API, upload, live streaming, player customization, analytics, webhooks"
Bunny Stream
"Bunny.net Stream: video hosting, HLS delivery, direct uploads, video collections, thumbnail generation, token authentication, webhook events"
Cloudflare Stream
"Cloudflare Stream: video hosting, adaptive streaming, direct upload, watermarks, signed URLs, Workers integration"
LiveKit
"LiveKit: real-time video/audio, WebRTC, rooms, tracks, screen sharing, recording, React components"
Mux Video
"Mux: video hosting, HLS streaming, upload API, playback URLs, thumbnails, analytics, Mux Player, Next.js integration"