The real problem
Why this is harder than it looks
Vimeo's API is REST-based, versioned (currently v3.4), and reasonably well-documented. Most developers assume a working prototype means a working integration. The problems emerge when you try to run this for real users in a multi-tenant product.
Vimeo's OAuth scopes are granular and strictly enforced at the endpoint level. The scopes — public, private, create, edit, delete, interact, and more — are not additive in the way developers expect. Requesting public does not unlock private video access; you must explicitly add private or calls to those endpoints will silently return empty results rather than an error. Upload access is an entirely separate capability that must be manually requested and approved through the Vimeo Developer Portal for each app — it is not enabled by default. If your agent needs to upload video on behalf of users and you haven't explicitly been granted this access, those calls will fail in ways that are confusing to diagnose.
Then there's Vimeo's rate limiting model. The API enforces a rolling 15-minute window, and the allowed request volume is tied directly to the account plan of the authenticated user — not just your app. A user on a free plan has dramatically lower limits than one on a Pro or Business plan. In a multi-tenant product where you're calling the API on behalf of many users with different plan tiers, building rate-limit logic that accounts for per-user ceiling differences is genuinely non-trivial. Exceeding the limit without the fields filter applied drops the effective rate limit to 100 requests per window, regardless of plan.
Add the standard multi-tenant requirements on top: per-user token isolation, proactive token refresh before expiry, and revocation detection when users disconnect your app. Scalekit handles all of this — scope configuration, token storage, refresh, and revocation — so your agent code only needs to name a tool and pass parameters.
Capabilities
What your agent can do with Vimeo
Once connected, your agent has 31 pre-built tools covering video management, content organization, and platform interactions:
- Manage videos end-to-end: retrieve, edit metadata, delete, search public videos, and fetch tags — including privacy settings, content ratings, and embed controls
- Organize content into folders and showcases: create folders and showcases, add videos to them, and list their contents — with support for password protection and branding on showcases
- Interact with channels and users: list channels, get videos from a specific channel, look up any user's public profile and videos, and follow users
- Track engagement: like videos, add to Watch Later, post comments, and list liked videos or comments on any video
- Manage webhooks: register, list, and delete webhook endpoints for real-time Vimeo event notifications
Setup context
What we're building
This guide connects a video assistant agent to Vimeo — helping users manage their video libraries, organize content into showcases and folders, and track engagement without leaving your product.
🤖
Example agent
Video library assistant managing Vimeo content, showcases, and engagement on behalf of each user
🔐
Auth model
B2B SaaS — each user connects their own Vimeo account. identifier = your user ID
Setup
1 Setup: One SDK, One credential
Install the Scalekit SDK. The only credential your application manages is the Scalekit API key — no Vimeo secrets, no user tokens, nothing belonging to your users.
pip install scalekit-sdk-python
npm install @scalekit-sdk/node
import scalekit.client
import os
from dotenv import load_dotenv
load_dotenv()
scalekit = scalekit.client.ScalekitClient(
client_id=os.getenv("SCALEKIT_CLIENT_ID"),
client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"),
env_url=os.getenv("SCALEKIT_ENV_URL"),
)
actions = scalekit.actions
import { ScalekitClient } from '@scalekit-sdk/node';
import 'dotenv/config';
const scalekit = new ScalekitClient(
process.env.SCALEKIT_ENV_URL,
process.env.SCALEKIT_CLIENT_ID,
process.env.SCALEKIT_CLIENT_SECRET
);
const actions = scalekit.actions;
Already have credentials?
Connected Accounts
2 Per-User Auth: Creating connected accounts
Each user gets their own Connected Account, giving them a dedicated auth context. The identifier is any unique string from your system — a UUID, email, whatever you use internally.
response = actions.get_or_create_connected_account(
connection_name="vimeo",
identifier="user_vimeo_456" # your internal user ID
)
connected_account = response.connected_account
print(f"Status: {connected_account.status}")
# Status: PENDING — user hasn't authorized yet
const response = await actions.getOrCreateConnectedAccount({
connectionName: "vimeo",
identifier: "user_vimeo_456" // your internal user ID
});
const connectedAccount = response.connectedAccount;
console.log(`Status: ${connectedAccount.status}`);
// Status: PENDING — user hasn't authorized yet
This call is idempotent — safe to call on every session start. Returns the existing account if one already exists.
Authorization Flow
3 The authorization flow
The user authorizes your agent once. Scalekit generates the OAuth URL with correct scopes, PKCE challenge, and redirect handling pre-configured. After approval, you never see the token.
if connected_account.status != "ACTIVE":
link = actions.get_authorization_link(
connection_name="vimeo",
identifier="user_vimeo_456"
)
# Redirect user → Vimeo's native OAuth consent screen
# Scalekit captures the token on callback
return redirect(link.link)
if (connectedAccount.status !== "ACTIVE") {
const { link } = await actions.getAuthorizationLink({
connectionName: "vimeo",
identifier: "user_vimeo_456"
});
// Redirect user → Vimeo's native OAuth consent screen
// Scalekit captures the token on callback
return redirect(link);
}
Token management is automatic
After the user approves, Scalekit stores encrypted tokens and the connected account moves to ACTIVE. Access
tokens refresh before expiry. If a user revokes access from their Vimeo app settings, the account moves to REVOKED
— no silent failures. Check account.status before critical operations.
BYOC is required for Vimeo
Vimeo requires you to register your own app and supply your own Client ID and Client Secret. Create your app
at developer.vimeo.com, paste the Scalekit redirect URI as your callback URL, then enter your credentials in
the Scalekit dashboard. Token management stays fully handled after that one-time setup.
Calling Vimeo
4 Calling Vimeo: What your agent writes
With the connected account active, your agent calls Vimeo actions using execute_tool. Name the tool, pass parameters. Scalekit handles token retrieval, request construction, and response parsing.
List the authenticated user's videos
Fetch all videos uploaded by the connected user. Use sort and filter to narrow results — useful as a starting point before editing or organizing content.
result = actions.execute_tool(
identifier="user_vimeo_456",
tool_name="vimeo_my_videos_list",
tool_input={
"sort": "date",
"direction": "desc",
"per_page": 25
}
)
# Returns array of video objects with uri, name, description, privacy, stats
const result = await actions.executeTool({
identifier: "user_vimeo_456",
toolName: "vimeo_my_videos_list",
toolInput: {
"sort": "date",
"direction": "desc",
"per_page": 25
}
});
// Returns array of video objects with uri, name, description, privacy, stats
Edit video metadata
Update a video's title, description, privacy settings, or content rating. Only the fields you supply are changed — all others remain untouched. Requires the edit scope.
result = actions.execute_tool(
identifier="user_vimeo_456",
tool_name="vimeo_video_edit",
tool_input={
"video_id": "987654321",
"name": "Q3 Product Demo — Final Cut",
"description": "Updated demo reel for Q3 2026 launch.",
"privacy_view": "anybody",
"privacy_embed": "public",
"privacy_download": False
}
)
const result = await actions.executeTool({
identifier: "user_vimeo_456",
toolName: "vimeo_video_edit",
toolInput: {
"video_id": "987654321",
"name": "Q3 Product Demo — Final Cut",
"description": "Updated demo reel for Q3 2026 launch.",
"privacy_view": "anybody",
"privacy_embed": "public",
"privacy_download": false
}
});
Create a showcase and add a video
Create a showcase (formerly album) for organizing videos, then add a specific video to it. Showcases support password protection and custom branding. Requires the create and edit scopes.
# 1. Create the showcase
showcase = actions.execute_tool(
identifier="user_vimeo_456",
tool_name="vimeo_showcase_create",
tool_input={
"name": "Client Demos — Acme Corp",
"description": "Curated demos for Acme Corp onboarding",
"privacy": "password",
"password": "acme2026",
"sort": "added_first"
}
)
# Returns showcase ID, URI, and metadata
# 2. Add a video to the showcase
result = actions.execute_tool(
identifier="user_vimeo_456",
tool_name="vimeo_showcase_video_add",
tool_input={
"album_id": showcase["uri"].split("/")[-1],
"video_id": "987654321"
}
)
// 1. Create the showcase
const showcase = await actions.executeTool({
identifier: "user_vimeo_456",
toolName: "vimeo_showcase_create",
toolInput: {
"name": "Client Demos — Acme Corp",
"description": "Curated demos for Acme Corp onboarding",
"privacy": "password",
"password": "acme2026",
"sort": "added_first"
}
});
// Returns showcase ID, URI, and metadata
// 2. Add a video to the showcase
const result = await actions.executeTool({
identifier: "user_vimeo_456",
toolName: "vimeo_showcase_video_add",
toolInput: {
"album_id": showcase.uri.split("/").pop(),
"video_id": "987654321"
}
});
Register a webhook for video events
Subscribe to real-time Vimeo events — useful for triggering downstream workflows when a video finishes transcoding or its privacy setting changes. Requires the private scope.
result = actions.execute_tool(
identifier="user_vimeo_456",
tool_name="vimeo_webhook_create",
tool_input={
"url": "https://your-app.com/webhooks/vimeo",
"event_types": [
"video.upload.complete",
"video.transcode.complete",
"video.privacy.change"
]
}
)
# Returns webhook ID and confirmation of registered event types
const result = await actions.executeTool({
identifier: "user_vimeo_456",
toolName: "vimeo_webhook_create",
toolInput: {
"url": "https://your-app.com/webhooks/vimeo",
"event_types": [
"video.upload.complete",
"video.transcode.complete",
"video.privacy.change"
]
}
});
// Returns webhook ID and confirmation of registered event types
Framework wiring
5 Wiring into your agent framework
Scalekit integrates directly with LangChain. The agent decides what to call; Scalekit handles auth on every invocation. No token plumbing in your agent logic.
from langchain_anthropic import ChatAnthropic
from langchain.agents import AgentExecutor, create_tool_calling_agent
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from scalekit.langchain import get_tools
vimeo_tools = get_tools(
connection_name="vimeo",
identifier="user_vimeo_456"
)
prompt = ChatPromptTemplate.from_messages([
("system", "You are a video library assistant. Use the available tools to help the user manage their Vimeo videos, showcases, and folders."),
MessagesPlaceholder("chat_history", optional=True),
("human", "{input}"),
MessagesPlaceholder("agent_scratchpad"),
])
agent = create_tool_calling_agent(ChatAnthropic(model="claude-sonnet-4-6"), vimeo_tools, prompt)
result = AgentExecutor(agent=agent, tools=vimeo_tools).invoke({
"input": "Find all my videos uploaded this month and organize them into a new showcase called 'March 2026'"
})
import { ChatAnthropic } from "@langchain/anthropic";
import { AgentExecutor, createToolCallingAgent } from "langchain/agents";
import { ChatPromptTemplate, MessagesPlaceholder } from "@langchain/core/prompts";
import { getTools } from "@scalekit-sdk/langchain";
const vimeoTools = getTools({
connectionName: "vimeo",
identifier: "user_vimeo_456"
});
const prompt = ChatPromptTemplate.fromMessages([
["system", "You are a video library assistant. Use the available tools to help the user manage their Vimeo videos, showcases, and folders."],
new MessagesPlaceholder("chat_history", true),
["human", "{input}"],
new MessagesPlaceholder("agent_scratchpad"),
]);
const agent = await createToolCallingAgent({
llm: new ChatAnthropic({ model: "claude-sonnet-4-6" }),
tools: vimeoTools,
prompt
});
const result = await AgentExecutor.fromAgentAndTools({
agent,
tools: vimeoTools
}).invoke({
input: "Find all my videos uploaded this month and organize them into a new showcase called 'March 2026'"
});
Other frameworks supported
Tool reference
All 31 Vimeo tools
Grouped by capability. Your agent calls tools by name — no API wrappers to write.
List all videos uploaded by the authenticated user with sort, filter, and pagination support. Requires private scope
Retrieve detailed metadata, privacy settings, stats, and embed details for a specific video by ID
Update a video's title, description, privacy settings, content rating, license, and embed controls. Requires edit scope
Permanently delete a video. Irreversible. Requires delete scope and ownership of the video
Search public Vimeo videos by keyword with sort, filter, and pagination. Requires public scope
Retrieve all tags applied to a specific video. Requires public scope
List all public videos uploaded by any specified Vimeo user. Requires public scope
Create a showcase with name, privacy, password protection, branding color, and sort defaults. Requires create scope
List all showcases owned by the authenticated user with pagination. Requires private scope
Add a video to a showcase. Requires edit scope and ownership of both the showcase and video
vimeo_showcase_videos_list
List all videos in a specific showcase with sort and pagination. Requires private scope
Create a new folder (project) for organizing private video content. Supports nested folders via parent_folder_uri. Requires create scope
List all folders owned by the authenticated user with search, sort, and pagination. Requires private scope
Move or add a video into a folder. Requires edit scope
List all videos inside a specific folder with filter, sort, and pagination. Requires private scope
List public Vimeo channels or channels the authenticated user follows or manages. Requires public scope
vimeo_channel_videos_list
Retrieve all videos in a specific channel by channel ID or slug. Supports filter, sort, and search. Requires public scope
Retrieve the authenticated user's profile including account type, bio, location, stats, and links
Retrieve public profile information for any Vimeo user by user ID or username. Requires public scope
Follow a Vimeo user on behalf of the authenticated user. Requires interact scope
List all Vimeo users the authenticated user is following with filter, sort, and pagination. Requires private scope
Like a video on behalf of the authenticated user. Requires interact scope
List all videos liked by the authenticated user with filter, sort, and pagination. Requires private scope
Post a comment on a video on behalf of the authenticated user. Requires interact scope
vimeo_video_comments_list
Retrieve all comments on a specific video with pagination. Requires public scope
Add a video to the authenticated user's Watch Later queue. Requires interact scope
Retrieve all videos in the Watch Later queue with filter, sort, and pagination. Requires private scope
Retrieve all top-level Vimeo content categories (e.g., Animation, Documentary, Music) with sort and pagination. Requires public scope
Register a webhook endpoint for real-time event notifications. Supports video upload, transcode, privacy change, and comment events. Requires private scope
List all webhooks registered for the authenticated Vimeo application with pagination. Requires private scope
Delete a registered webhook so it no longer receives event notifications. Requires private scope
Connector notes
Vimeo-specific behavior
Upload access must be explicitly requested
Upload capability is not enabled by default on Vimeo OAuth apps — it is a separately gated permission you must
request through the Vimeo Developer Portal under your app's General Information settings. If your agent needs to
upload video on behalf of users and you have not been granted upload access, those calls will fail with a permissions
error. Request it early in your development cycle; approval is not instant.
Rate limits vary by the authenticated user's plan
Vimeo's API rate limits are tied to the account plan of the user whose token is being used, not just your app.
A free-tier user has a significantly lower request ceiling than a Pro or Business user. In a multi-tenant product
where users have mixed plans, build rate-limit handling that respects per-user limits. The API returns
x-ratelimit-remaining and x-ratelimit-reset headers — use them. Failing to include the fields filter parameter
also caps your effective rate limit to 100 requests regardless of plan.
private scope is required to access non-public content
Requesting only the public scope will not surface private videos, folders, or Watch Later content — those
endpoints require the private scope explicitly. This is a common source of empty results that look like
authorization success but are actually a scope gap. Ensure the scope set configured in the Scalekit dashboard
matches what your agent's actual tool usage requires.
Infrastructure decision
Why not build this yourself
The Vimeo OAuth flow is documented. Token storage isn't technically hard. But here's what you're actually signing up for:
PROBLEM 01
Upload access is a separately gated permission requiring manual approval per app — not just an OAuth scope you declare, and not something you can discover until calls fail in production
PROBLEM 02
Rate limits are per-user-plan, not per-app — building limit handling that correctly adapts to each user's ceiling across a multi-tenant system requires deliberate design
PROBLEM 03
Scope gaps surface as silent empty results rather than errors — private scope omission means private video and folder tools return nothing, with no indication that auth succeeded but access was denied
PROBLEM 04
Per-user token isolation, proactive refresh before expiry, and revocation detection when users disconnect your app from Vimeo settings — each is manageable alone, together they are weeks of plumbing
That's one connector. Your agent product will eventually need Salesforce, Slack, Gmail, Notion, and whatever else your customers ask for. Each has its own OAuth quirks and failure modes.
Scalekit maintains every connector. You maintain none of them.