To integrate Replit with Vimeo, register an OAuth 2.0 app in the Vimeo developer portal, store your access token in Replit Secrets, and call the Vimeo API from a server-side Node.js or Python backend. You can upload videos, manage metadata, retrieve embed codes, and customize the Vimeo player. Deploy your Replit server on Autoscale for video management tools and media-rich applications.
Why Integrate Vimeo with Replit?
Vimeo is the premier choice for developers and businesses that need professional video hosting with fine-grained privacy controls, customizable players, and a clean ad-free viewing experience. Unlike YouTube, Vimeo lets you restrict who can view or embed your videos, remove the Vimeo branding on paid plans, and control the player's colors and interface. Integrating Vimeo with Replit lets you automate video management workflows, build custom video galleries, and embed Vimeo content in applications you're building.
The Vimeo API gives you programmatic control over your entire video library: upload new videos, update titles and descriptions, set privacy settings, retrieve embed codes, and even generate video thumbnails. For content-heavy applications β online courses, portfolio sites, media archives, internal training platforms β having a Replit backend that communicates with Vimeo means you can automate what would otherwise be tedious manual work through the Vimeo dashboard.
Vimeo uses OAuth 2.0 for authentication. For personal or internal integrations, you can use a long-lived personal access token tied to your Vimeo account. For applications where multiple users connect their own Vimeo accounts, you implement the three-legged OAuth flow. Video uploads use the tus protocol (a resumable upload standard), which is important for handling large video files reliably β if an upload is interrupted, tus resumes from where it stopped rather than starting over.
Integration method
Vimeo integrates with Replit through OAuth 2.0 authentication and REST API calls from your server-side backend. You create a Vimeo developer app at developer.vimeo.com to obtain OAuth credentials and a personal access token, store it securely in Replit Secrets, and make authenticated requests from Express or Flask to manage videos, retrieve embed codes, and control player settings. File uploads use Vimeo's tus resumable upload protocol for reliable video ingestion.
Prerequisites
- A Vimeo account (Basic, Plus, Pro, or Business β API upload access requires a paid plan for most use cases)
- A Vimeo developer app created at developer.vimeo.com with a personal access token generated
- A Replit account with a Node.js or Python Repl created
- Node.js with axios and express installed, or Python with requests and flask installed in your Repl
- Basic understanding of OAuth 2.0 and REST APIs
Step-by-step guide
Create a Vimeo Developer App and Generate an Access Token
Create a Vimeo Developer App and Generate an Access Token
Go to developer.vimeo.com and sign in with your Vimeo account. Click 'Create App' in the top navigation. Fill in your app name (e.g., 'Replit Integration'), app description, and your app's URL (you can use your Replit URL or a placeholder like https://example.com for development). Under 'App Type', select 'Personal' if this is for your own account, or 'Web App' if you plan to support multiple users connecting their accounts via OAuth redirect. After creating the app, navigate to the Authentication tab. In the 'Personal Access Tokens' section, click 'Generate' to create a token. Select the scopes your integration needs: check 'Public' and 'Private' to access all your videos, 'Upload' if you plan to upload videos programmatically, 'Edit' to update video metadata, and 'Video Files' to access download links. Copy the generated access token immediately β it will only be shown once in full. Store it safely before closing the modal. Also note your App ID and Secret from the app page, which you'll need if you implement the full OAuth flow for multi-user support later.
Pro tip: Start with the minimum scopes your integration needs β you can always add more later by generating a new token with additional scopes. Over-permissioned tokens are a security risk if they're ever accidentally exposed.
Expected result: You have a Vimeo developer app with a personal access token that has the scopes needed for your integration.
Store Your Vimeo Access Token in Replit Secrets
Store Your Vimeo Access Token in Replit Secrets
Open your Repl and click the lock icon (π) in the left sidebar to open the Secrets panel. Add your Vimeo personal access token as a secret named VIMEO_ACCESS_TOKEN. If you're implementing multi-user OAuth later, also add VIMEO_CLIENT_ID and VIMEO_CLIENT_SECRET as separate secrets. Replit Secrets are encrypted at rest and injected as environment variables when your Repl starts β they're never visible in your source code, your Git history, or to other Replit users. In your Node.js code you access the token via process.env.VIMEO_ACCESS_TOKEN; in Python via os.environ['VIMEO_ACCESS_TOKEN']. After adding the secret, restart your Repl to ensure the environment variable loads. Replit's Secret Scanner continuously monitors your code files for accidentally hardcoded credentials β if you accidentally paste an API key into a code file, it will be flagged immediately.
Pro tip: Vimeo personal access tokens don't expire automatically, but if your token is compromised, go to developer.vimeo.com and revoke it immediately, then generate a new one and update your Replit Secret.
Expected result: VIMEO_ACCESS_TOKEN is visible in the Replit Secrets panel and accessible as an environment variable in your running Repl.
Build the Node.js Vimeo API Server
Build the Node.js Vimeo API Server
Create an Express server that acts as a secure proxy for Vimeo API calls. All requests to the Vimeo API are made server-side β your frontend never directly calls Vimeo or sees the access token. The Vimeo API base URL is https://api.vimeo.com and all requests need an Authorization header with 'bearer YOUR_TOKEN'. Install required packages by running npm install express axios in the Replit Shell. Key Vimeo API endpoints include /me/videos (list your videos), /videos/{video_id} (get video details), /me/videos with POST (initiate upload), and PATCH /videos/{video_id} (update metadata). The GET /videos/{video_id} response includes an embed object with HTML iframe code and customizable query parameters. You can customize the embedded player by appending parameters to the Vimeo video URL: autoplay=1, background=1 (for background video loops), loop=1, muted=1, color=hexcode (player color), title=0 (hide title), byline=0 (hide author), portrait=0 (hide avatar).
1const express = require('express');2const axios = require('axios');34const app = express();5app.use(express.json());67const VIMEO_TOKEN = process.env.VIMEO_ACCESS_TOKEN;8const VIMEO_BASE_URL = 'https://api.vimeo.com';910const vimeoClient = axios.create({11 baseURL: VIMEO_BASE_URL,12 headers: {13 'Authorization': `bearer ${VIMEO_TOKEN}`,14 'Content-Type': 'application/json',15 'Accept': 'application/vnd.vimeo.*+json;version=3.4'16 },17 timeout: 1500018});1920// List videos in account21app.get('/api/videos', async (req, res) => {22 const { per_page = 10, page = 1, sort = 'date' } = req.query;23 try {24 const response = await vimeoClient.get('/me/videos', {25 params: { per_page, page, sort, fields: 'uri,name,description,created_time,duration,privacy,embed,pictures' }26 });27 const videos = response.data.data.map(v => ({28 id: v.uri.replace('/videos/', ''),29 name: v.name,30 description: v.description,31 duration: v.duration,32 created_time: v.created_time,33 privacy: v.privacy.view,34 thumbnail: v.pictures?.sizes?.[3]?.link || null,35 embed_html: v.embed?.html || null36 }));37 res.json({ videos, total: response.data.total, page: response.data.page });38 } catch (error) {39 console.error('Vimeo API error:', error.response?.data || error.message);40 res.status(error.response?.status || 500).json({ error: 'Failed to fetch videos' });41 }42});4344// Get a single video with customized embed code45app.get('/api/videos/:id', async (req, res) => {46 const { color = '00adef', autoplay = 0, loop = 0, title = 1 } = req.query;47 try {48 const response = await vimeoClient.get(`/videos/${req.params.id}`);49 const video = response.data;50 const embedUrl = `https://player.vimeo.com/video/${req.params.id}?color=${color}&autoplay=${autoplay}&loop=${loop}&title=${title}&byline=0&portrait=0`;51 const embedHtml = `<iframe src="${embedUrl}" width="640" height="360" frameborder="0" allow="autoplay; fullscreen; picture-in-picture" allowfullscreen></iframe>`;52 res.json({53 id: req.params.id,54 name: video.name,55 description: video.description,56 duration: video.duration,57 privacy: video.privacy.view,58 embed_html: embedHtml,59 thumbnail: video.pictures?.sizes?.[3]?.link || null60 });61 } catch (error) {62 console.error('Vimeo API error:', error.response?.data || error.message);63 res.status(error.response?.status || 500).json({ error: 'Failed to fetch video' });64 }65});6667// Update video privacy settings68app.patch('/api/videos/:id/privacy', async (req, res) => {69 const { view, password, embed } = req.body;70 const privacySettings = { view };71 if (view === 'password' && password) privacySettings.password = password;72 if (embed) privacySettings.embed = embed;73 try {74 const response = await vimeoClient.patch(`/videos/${req.params.id}`, {75 privacy: privacySettings76 });77 res.json({ id: req.params.id, privacy: response.data.privacy });78 } catch (error) {79 console.error('Vimeo API error:', error.response?.data || error.message);80 res.status(error.response?.status || 500).json({ error: 'Failed to update privacy' });81 }82});8384app.listen(3000, '0.0.0.0', () => console.log('Vimeo API server running on port 3000'));Pro tip: Note that Vimeo uses 'bearer' (lowercase) in the Authorization header, not 'Bearer' (capital B). Some Vimeo API integrations fail because of this capitalization difference β the code example above uses the correct lowercase form.
Expected result: Your Express server starts and GET /api/videos returns a JSON array of your Vimeo videos with metadata and embed codes.
Build the Python Flask Alternative
Build the Python Flask Alternative
For Python-based Replit projects, the same Vimeo integration can be implemented with Flask and the requests library. Python is well-suited for video metadata processing and batch operations on large video libraries. Install the dependencies by running pip install flask requests in the Replit Shell. The Python implementation follows the same pattern: all Vimeo API calls go through the server-side code, the access token is read from environment variables, and the response data is cleaned up and returned as JSON. A useful Python-specific addition is a helper function for building customized embed URLs β this makes it easy to generate player iframes with different configuration options for different contexts (public gallery versus private team page).
1import os2import requests3from flask import Flask, jsonify, request45app = Flask(__name__)67VIMEO_TOKEN = os.environ['VIMEO_ACCESS_TOKEN']8VIMEO_BASE_URL = 'https://api.vimeo.com'910def vimeo_headers():11 return {12 'Authorization': f'bearer {VIMEO_TOKEN}',13 'Content-Type': 'application/json',14 'Accept': 'application/vnd.vimeo.*+json;version=3.4'15 }1617def build_embed_url(video_id, color='00adef', autoplay=0, loop=0, title=1):18 params = f'color={color}&autoplay={autoplay}&loop={loop}&title={title}&byline=0&portrait=0'19 return f'https://player.vimeo.com/video/{video_id}?{params}'2021def build_embed_html(video_id, **kwargs):22 url = build_embed_url(video_id, **kwargs)23 return f'<iframe src="{url}" width="640" height="360" frameborder="0" allow="autoplay; fullscreen; picture-in-picture" allowfullscreen></iframe>'2425@app.route('/api/videos')26def list_videos():27 per_page = request.args.get('per_page', 10)28 page = request.args.get('page', 1)29 try:30 response = requests.get(31 f'{VIMEO_BASE_URL}/me/videos',32 headers=vimeo_headers(),33 params={'per_page': per_page, 'page': page, 'sort': 'date',34 'fields': 'uri,name,description,created_time,duration,privacy,embed,pictures'},35 timeout=1536 )37 response.raise_for_status()38 data = response.json()39 videos = [{40 'id': v['uri'].replace('/videos/', ''),41 'name': v['name'],42 'description': v.get('description', ''),43 'duration': v['duration'],44 'privacy': v['privacy']['view'],45 'thumbnail': v.get('pictures', {}).get('sizes', [{}] * 4)[3].get('link'),46 'embed_html': v.get('embed', {}).get('html')47 } for v in data['data']]48 return jsonify({'videos': videos, 'total': data['total']})49 except requests.RequestException as e:50 return jsonify({'error': str(e)}), 5005152@app.route('/api/videos/<video_id>')53def get_video(video_id):54 color = request.args.get('color', '00adef')55 try:56 response = requests.get(57 f'{VIMEO_BASE_URL}/videos/{video_id}',58 headers=vimeo_headers(),59 timeout=1560 )61 response.raise_for_status()62 video = response.json()63 return jsonify({64 'id': video_id,65 'name': video['name'],66 'description': video.get('description', ''),67 'duration': video['duration'],68 'privacy': video['privacy']['view'],69 'embed_html': build_embed_html(video_id, color=color)70 })71 except requests.RequestException as e:72 return jsonify({'error': str(e)}), 5007374if __name__ == '__main__':75 app.run(host='0.0.0.0', port=3000)Pro tip: The fields parameter in the /me/videos request tells Vimeo to return only the specified fields, significantly reducing response size for video libraries with many entries. Always use the fields parameter for list endpoints to improve performance.
Expected result: Your Flask server starts and /api/videos returns your Vimeo video list with metadata and embed codes as JSON.
Implement Video Upload Using the Tus Protocol
Implement Video Upload Using the Tus Protocol
Uploading videos to Vimeo from a Replit backend requires using the tus resumable upload protocol rather than a simple multipart form POST. Vimeo requires this approach to reliably handle large video files. The process has three stages: first, create an upload slot by POSTing to /me/videos with video metadata and the file size; this returns an upload link and a Vimeo video URI. Second, use the tus protocol to stream the actual video bytes to the upload link. Third, verify the upload completed by checking the video's upload status. Install the tus-py-client (for Python) or tus-js-client (for Node.js) to handle the tus protocol. For simpler cases where you want to avoid the tus dependency, Vimeo also supports a streaming approach using the upload_approach: 'pull' parameter where you provide a URL and Vimeo fetches the video itself β useful when the video file is already hosted somewhere accessible.
1// Node.js: Simple video upload using Vimeo's 'pull' approach2// (Vimeo fetches the video from a public URL β simpler than tus)3async function uploadVideoFromUrl(videoUrl, title, description = '') {4 // Step 1: Create upload ticket with 'pull' approach5 const createResponse = await vimeoClient.post('/me/videos', {6 upload: {7 approach: 'pull',8 link: videoUrl9 },10 name: title,11 description: description,12 privacy: {13 view: 'nobody' // Start as private14 }15 });1617 const videoUri = createResponse.data.uri;18 const videoId = videoUri.replace('/videos/', '');19 console.log(`Upload initiated. Vimeo video ID: ${videoId}`);2021 // Step 2: Poll until upload is complete22 let status = 'in_progress';23 while (status === 'in_progress') {24 await new Promise(resolve => setTimeout(resolve, 5000)); // Wait 5 seconds25 const statusResponse = await vimeoClient.get(videoUri, {26 params: { fields: 'upload,transcode' }27 });28 status = statusResponse.data.upload?.status || 'error';29 console.log(`Upload status: ${status}`);30 }3132 if (status === 'complete') {33 console.log(`Video uploaded successfully: https://vimeo.com/${videoId}`);34 return videoId;35 } else {36 throw new Error(`Upload failed with status: ${status}`);37 }38}3940// Add upload endpoint to Express server41app.post('/api/videos/upload-from-url', async (req, res) => {42 const { url, title, description } = req.body;43 if (!url || !title) {44 return res.status(400).json({ error: 'url and title are required' });45 }46 try {47 const videoId = await uploadVideoFromUrl(url, title, description);48 res.json({ success: true, video_id: videoId, vimeo_url: `https://vimeo.com/${videoId}` });49 } catch (error) {50 res.status(500).json({ error: error.message });51 }52});Pro tip: The 'pull' upload approach (where Vimeo fetches the video from a URL you provide) is much simpler to implement than tus uploads and works well when your video files are already stored in a cloud service like AWS S3 or Backblaze B2. Use tus only when you need to upload files directly from user browsers or local servers.
Expected result: POST /api/videos/upload-from-url with a publicly accessible video URL initiates a Vimeo upload and returns the new video ID once complete.
Common use cases
Video Gallery with Custom Player
Build a backend that fetches a user's Vimeo video library and returns metadata plus embed codes for display in a custom gallery page. Your Replit server queries the Vimeo API for videos in a specific folder, retrieves the embed HTML for each, and passes customized player parameters to control autoplay, color, and privacy settings.
Build a video gallery endpoint that fetches all videos from a specific Vimeo folder, retrieves the embed code for each with custom player settings (no title, no byline, custom color), and returns a JSON array with the video title, description, thumbnail URL, and iframe embed code.
Copy this prompt to try it in Replit
Automated Video Upload Pipeline
Create a Replit backend that accepts video file uploads from your own users, uploads them to Vimeo using the tus protocol, sets the title and description, configures privacy settings, and returns the Vimeo video URL. This removes the need for users to manually log in to Vimeo and upload files themselves.
Build an upload endpoint that accepts a video file via multipart form data, initiates a tus upload to the Vimeo API, tracks the upload progress, and returns the final Vimeo video URL along with the embed code once the upload is complete and ready for playback.
Copy this prompt to try it in Replit
Video Privacy Management Dashboard
Build an internal tool that lists all videos in a Vimeo account, shows their current privacy settings, and lets you update them in bulk. For example, switch a batch of training videos from private to password-protected, or restrict embedding to specific domains. Your Replit backend handles all the API calls while a simple frontend provides the management UI.
Build an admin endpoint that lists all videos in a Vimeo account with their current privacy settings, and provides a PATCH endpoint to update a video's privacy setting to 'password', 'nobody', 'anybody', or 'disable', returning the updated video object after the change.
Copy this prompt to try it in Replit
Troubleshooting
API returns 401 Unauthorized with 'Your access token does not have the correct scope'
Cause: The personal access token you generated doesn't have the required scope for the endpoint you're calling. For example, trying to upload without the 'upload' scope, or accessing private videos without the 'private' scope.
Solution: Go to developer.vimeo.com, open your app's Authentication tab, and generate a new access token with all the scopes your integration needs (public, private, edit, upload, video_files). Update the VIMEO_ACCESS_TOKEN secret in Replit's Secrets panel (lock icon π) with the new token and restart your Repl.
Player embed shows an error 'This video does not exist' or shows a black screen
Cause: The video privacy setting is set to 'nobody' or 'password', or the embed privacy is set to restrict embedding to specific domains. The Vimeo video ID in the embed URL may also be incorrect.
Solution: Verify the video's privacy and embed settings in your Vimeo account, or use the PATCH /videos/{id} endpoint to update privacy settings. For domain-restricted embedding, add your Replit deployment domain to the allowed embed domains list in the Vimeo video settings.
1// Update video to allow embedding everywhere2const response = await vimeoClient.patch(`/videos/${videoId}`, {3 privacy: {4 view: 'anybody',5 embed: 'public'6 }7});API returns 403 Forbidden when trying to upload videos
Cause: Vimeo's API upload functionality requires a Vimeo Plus, Pro, or Business plan. Basic (free) accounts cannot upload via API. Additionally, upload scope must be included in your access token.
Solution: Upgrade your Vimeo account to at least a Plus plan to enable API uploads. Then generate a new access token that includes the 'upload' scope and update your VIMEO_ACCESS_TOKEN secret in Replit.
Rate limit errors (429 Too Many Requests) when making multiple API calls
Cause: Vimeo's API enforces rate limits: up to 100 requests per minute for most endpoints on standard plans. Batch operations that loop through many videos can exhaust this limit quickly.
Solution: Implement request throttling in your application. Add delays between API calls in batch operations and cache responses that don't change frequently (such as video metadata and embed codes) with a TTL of at least 1 hour.
1// Throttled batch requests2const sleep = (ms) => new Promise(r => setTimeout(r, ms));34async function batchUpdateVideos(videoIds, settings) {5 for (const id of videoIds) {6 await vimeoClient.patch(`/videos/${id}`, settings);7 await sleep(700); // Stay well under 100 req/min limit8 }9}Best practices
- Always store your Vimeo access token in Replit Secrets (lock icon π) β never include it in client-side code or commit it to your repository
- Use the fields parameter when listing videos to request only the data you need β this reduces response size and improves API performance for large video libraries
- Proxy all Vimeo API calls through your Replit server β never call the Vimeo API directly from browser JavaScript, which would expose your access token
- Cache video metadata and embed codes (which rarely change) for at least 1 hour to reduce API call frequency and stay within Vimeo's rate limits
- Start newly uploaded videos as private (privacy.view: 'nobody') and only make them public after you've verified the title, description, and thumbnail are correct
- Use Replit Autoscale deployment for video management backends and player proxy APIs β the on-demand scaling handles variable traffic without maintaining an always-on server
- Implement proper error handling for Vimeo's 429 rate limit responses with exponential backoff, especially in batch video update operations
- For multi-user applications where users connect their own Vimeo accounts, implement the full OAuth 2.0 authorization code flow and never share a single access token across multiple users
Alternatives
YouTube is the better choice when maximum audience reach and discoverability matter more than privacy controls and ad-free viewing.
Getty Images is the right choice when you need premium licensed stock video and images rather than a hosting platform for your own video content.
Shutterstock is the better option for sourcing licensed stock video footage at volume pricing rather than hosting original video content.
Frequently asked questions
How do I connect Replit to Vimeo?
Create a developer app at developer.vimeo.com, generate a personal access token with the required scopes, and store it in Replit Secrets as VIMEO_ACCESS_TOKEN. In your server code, include 'Authorization: bearer YOUR_TOKEN' (lowercase 'bearer') in all API request headers when calling https://api.vimeo.com/.
Does Replit work with Vimeo for free?
Replit is free for development. Vimeo's API access is available on Basic (free) accounts for reading public video data, but uploading via API and accessing private videos requires a paid Vimeo plan (Plus or higher). Check vimeo.com/upgrade for current plan features.
How do I store my Vimeo API key in Replit?
Click the lock icon (π) in the Replit left sidebar to open the Secrets panel. Add a new secret named VIMEO_ACCESS_TOKEN and paste your Vimeo personal access token as the value. Access it in Node.js with process.env.VIMEO_ACCESS_TOKEN or in Python with os.environ['VIMEO_ACCESS_TOKEN']. Restart your Repl after adding the secret.
How do I customize the Vimeo player embed in Replit?
Build the embed URL yourself by appending query parameters to the player URL: https://player.vimeo.com/video/VIDEO_ID?color=hexcode&autoplay=1&loop=1&title=0&byline=0&portrait=0. You can control the player color, autoplay behavior, loop mode, and whether the title, author name, and avatar are displayed. Wrap this in an iframe for embedding.
Can I upload videos to Vimeo from Replit?
Yes β Vimeo supports video uploads via API using either the tus resumable upload protocol (for direct file uploads) or the 'pull' approach where you provide a public URL and Vimeo fetches the file. The pull approach is simpler to implement and works well when your video is already stored in S3 or another cloud service. API uploads require a paid Vimeo account and the 'upload' scope in your access token.
Why is the Vimeo API returning 'bearer' authorization errors?
Vimeo requires 'bearer' in lowercase in the Authorization header: 'Authorization: bearer YOUR_TOKEN'. Using capital 'Bearer' causes authentication failures. This is different from many other APIs that accept either case. Double-check your header capitalization in the code examples above.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation