Quick Start - Node.js
Get started with Importly in your Node.js application with server-side processing and webhook handling.
Setup
Install the required dependencies:
bash1npm install axios express
1. Get Your API Key
- Create an Account at Importly.io
- Find your API Key via the api key page
- Set it as an environment variable:
bash1# .env2IMPORTLY_API_KEY=your_api_key_here3PORT=30004WEBHOOK_URL=https://your-domain.com/webhook/importly5IMPORTLY_API_URL=https://api.importly.io
2. Create Importly Client
Create a robust Importly client with error handling:
javascript1// lib/importly.js2const axios = require("axios");34class ImportlyClient {5 constructor(apiKey) {6 this.apiKey = apiKey;7 this.baseURL = process.env.IMPORTLY_API_URL;8 this.client = axios.create({9 baseURL: this.baseURL,10 headers: {11 Authorization: `Bearer ${apiKey}`,12 "Content-Type": "application/json",13 },14 timeout: 30000,15 });16 }1718 async importMedia(url, options = {}) {19 const {20 includeVideo = true,21 includeAudio = true,22 videoQuality = "1080p",23 audioQuality = "medium",24 webhookUrl,25 } = options;2627 try {28 const response = await this.client.post("/import", {29 url,30 includeVideo,31 includeAudio,32 videoQuality,33 audioQuality,34 webhookUrl,35 });3637 return response.data;38 } catch (error) {39 throw this.handleError(error);40 }41 }4243 async getMetadata(url, webhookUrl = null) {44 try {45 const params = { url };46 if (webhookUrl) params.webhookUrl = webhookUrl;4748 const response = await this.client.get("/metadata", { params });49 return response.data;50 } catch (error) {51 throw this.handleError(error);52 }53 }5455 async checkImportStatus(jobId) {56 try {57 const response = await this.client.get(`/import/status?id=${jobId}`);58 return response.data;59 } catch (error) {60 throw this.handleError(error);61 }62 }6364 async checkMetadataStatus(jobId) {65 try {66 const response = await this.client.get(`/metadata/status?id=${jobId}`);67 return response.data;68 } catch (error) {69 throw this.handleError(error);70 }71 }7273 async waitForCompletion(74 id,75 type = "import",76 maxWaitTime = 300000,77 pollInterval = 500078 ) {79 const startTime = Date.now();80 const checkStatus =81 type === "import"82 ? this.checkImportStatus.bind(this)83 : this.checkMetadataStatus.bind(this);8485 while (Date.now() - startTime < maxWaitTime) {86 try {87 const result = await checkStatus(id);88 const { status } = result.data;8990 if (status === "completed") {91 return result;92 } else if (status === "failed" || status === "cancelled") {93 throw new Error(94 `${type} ${status}: ${result.data.error || "Unknown error"}`95 );96 }9798 // Wait before next poll99 await new Promise((resolve) => setTimeout(resolve, pollInterval));100 } catch (error) {101 if (error.response?.status === 404) {102 throw new Error(`${type} not found`);103 }104 throw error;105 }106 }107108 throw new Error(`${type} timed out after ${maxWaitTime}ms`);109 }110111 handleError(error) {112 if (error.response) {113 const { status, data } = error.response;114 const message = data?.message || data?.error || "API request failed";115116 switch (status) {117 case 401:118 return new Error("Invalid API key");119 case 402:120 return new Error("Insufficient credits");121 case 429:122 return new Error("Rate limit exceeded");123 case 400:124 return new Error(`Bad request: ${message}`);125 default:126 return new Error(`API error (${status}): ${message}`);127 }128 } else if (error.request) {129 return new Error("Network error: Unable to reach Importly API");130 } else {131 return new Error(`Request error: ${error.message}`);132 }133 }134}135136module.exports = ImportlyClient;
3. Express Server with Webhook Support
Create an Express server to handle imports and webhooks:
javascript1// server.js2const express = require("express");3const ImportlyClient = require("./lib/importly");45const app = express();6const port = process.env.PORT || 3000;78// Middleware9app.use(express.json());10app.use(express.urlencoded({ extended: true }));1112// Initialize Importly client13const importly = new ImportlyClient(process.env.IMPORTLY_API_KEY);1415// In-memory storage (use database in production)16const imports = new Map();17const metadata = new Map();1819// Import endpoint20app.post("/import", async (req, res) => {21 try {22 const { url, videoQuality = "1080p", audioQuality = "medium" } = req.body;2324 if (!url) {25 return res.status(400).json({ error: "URL is required" });26 }2728 const result = await importly.importMedia(url, {29 videoQuality,30 audioQuality,31 webhookUrl: process.env.WEBHOOK_URL,32 });3334 const jobId = result.data.jobId;3536 // Store import info37 imports.set(jobId, {38 id: jobId,39 url,40 status: "queued",41 createdAt: new Date().toISOString(),42 videoQuality,43 audioQuality,44 });4546 res.json({47 success: true,48 jobId,49 status: "queued",50 message: "Import started successfully",51 });52 } catch (error) {53 console.error("Import error:", error);54 res.status(500).json({ error: error.message });55 }56});5758// Metadata endpoint59app.post("/metadata", async (req, res) => {60 try {61 const { url } = req.body;6263 if (!url) {64 return res.status(400).json({ error: "URL is required" });65 }6667 const result = await importly.getMetadata(url, process.env.WEBHOOK_URL);68 const jobId = result.data.jobId;6970 // Store metadata info71 metadata.set(jobId, {72 id: jobId,73 url,74 status: "queued",75 createdAt: new Date().toISOString(),76 });7778 res.json({79 success: true,80 jobId,81 status: "queued",82 message: "Metadata request started successfully",83 });84 } catch (error) {85 console.error("Metadata error:", error);86 res.status(500).json({ error: error.message });87 }88});8990// Status endpoints91app.get("/import/:id/status", async (req, res) => {92 try {93 const { id } = req.params;9495 // Try local storage first96 const localImport = imports.get(id);97 if (localImport && localImport.status === "completed") {98 return res.json({ success: true, data: localImport });99 }100101 // Check with Importly API102 const result = await importly.checkImportStatus(id);103104 // Update local storage105 if (imports.has(id)) {106 imports.set(id, { ...imports.get(id), ...result.data });107 }108109 res.json(result);110 } catch (error) {111 console.error("Status check error:", error);112 res.status(500).json({ error: error.message });113 }114});115116app.get("/metadata/:id/status", async (req, res) => {117 try {118 const { id } = req.params;119120 const localMetadata = metadata.get(id);121 if (localMetadata && localMetadata.status === "completed") {122 return res.json({ success: true, data: localMetadata });123 }124125 const result = await importly.checkMetadataStatus(id);126127 if (metadata.has(id)) {128 metadata.set(id, { ...metadata.get(id), ...result.data });129 }130131 res.json(result);132 } catch (error) {133 console.error("Metadata status check error:", error);134 res.status(500).json({ error: error.message });135 }136});137138// Webhook endpoint139app.post("/webhook/importly", (req, res) => {140 try {141 const { type, data } = req.body;142143 console.log("Received webhook:", { type, data });144145 switch (type) {146 case "import.completed":147 handleImportCompleted(data);148 break;149 case "import.failed":150 handleImportFailed(data);151 break;152 case "metadata.completed":153 handleMetadataCompleted(data);154 break;155 case "metadata.failed":156 handleMetadataFailed(data);157 break;158 default:159 console.log("Unknown webhook type:", type);160 }161162 res.json({ received: true });163 } catch (error) {164 console.error("Webhook error:", error);165 res.status(500).json({ error: "Webhook processing failed" });166 }167});168169// Webhook handlers170function handleImportCompleted(data) {171 const { jobId, result } = data;172173 if (imports.has(jobId)) {174 imports.set(jobId, {175 ...imports.get(jobId),176 status: "completed",177 result,178 completedAt: new Date().toISOString(),179 });180 }181182 console.log("Import completed:", jobId);183184 // Add your custom logic here:185 // - Send notifications186 // - Update database187 // - Process the media file188 // - Trigger downstream workflows189}190191function handleImportFailed(data) {192 const { jobId, error } = data;193194 if (imports.has(jobId)) {195 imports.set(jobId, {196 ...imports.get(jobId),197 status: "failed",198 error,199 failedAt: new Date().toISOString(),200 });201 }202203 console.error("Import failed:", jobId, error);204}205206function handleMetadataCompleted(data) {207 const { jobId, result } = data;208209 if (metadata.has(jobId)) {210 metadata.set(jobId, {211 ...metadata.get(jobId),212 status: "completed",213 result,214 completedAt: new Date().toISOString(),215 });216 }217218 console.log("Metadata completed:", jobId);219}220221function handleMetadataFailed(data) {222 const { jobId, error } = data;223224 if (metadata.has(jobId)) {225 metadata.set(jobId, {226 ...metadata.get(jobId),227 status: "failed",228 error,229 failedAt: new Date().toISOString(),230 });231 }232233 console.error("Metadata failed:", jobId, error);234}235236// List endpoints for debugging237app.get("/imports", (req, res) => {238 const allImports = Array.from(imports.values()).sort(239 (a, b) => new Date(b.createdAt) - new Date(a.createdAt)240 );241242 res.json({ imports: allImports });243});244245app.get("/metadata", (req, res) => {246 const allMetadata = Array.from(metadata.values()).sort(247 (a, b) => new Date(b.createdAt) - new Date(a.createdAt)248 );249250 res.json({ metadata: allMetadata });251});252253// Health check254app.get("/health", (req, res) => {255 res.json({256 status: "ok",257 timestamp: new Date().toISOString(),258 imports: imports.size,259 metadata: metadata.size,260 });261});262263app.listen(port, () => {264 console.log(`Server running on port ${port}`);265 console.log(`Webhook URL: ${process.env.WEBHOOK_URL}`);266});
4. CLI Tool (Optional)
Create a command-line tool for easy imports:
javascript1// cli.js2#!/usr/bin/env node34const ImportlyClient = require('./lib/importly')56async function main() {7 const args = process.argv.slice(2)89 if (args.length === 0) {10 console.log('Usage: node cli.js <url> [quality]')11 process.exit(1)12 }1314 const url = args[0]15 const quality = args[1] || '1080p'1617 const importly = new ImportlyClient(process.env.IMPORTLY_API_KEY)1819 try {20 console.log(`Starting import for: ${url}`)21 console.log(`Quality: ${quality}`)2223 const result = await importly.importMedia(url, { videoQuality: quality })24 const jobId = result.data.jobId2526 console.log(`Import started with ID: ${jobId}`)27 console.log('Waiting for completion...')2829 const completed = await importly.waitForCompletion(jobId, 'import')3031 console.log('Import completed!')32 console.log('Media URL:', completed.data.result.mediaUrl)33 console.log('Credits used:', completed.data.result.creditsUsed)34 console.log('Duration:', completed.data.result.duration + 's')3536 } catch (error) {37 console.error('Error:', error.message)38 process.exit(1)39 }40}4142if (require.main === module) {43 main()44}
Make it executable:
bash1chmod +x cli.js
Use it:
bash1node cli.js "https://example.com/video" "1080p"
5. Running the Server
Start your server:
bash1# Development2npm run dev34# Production5npm start
Test the endpoints:
bash1# Start an import2curl -X POST http://localhost:3000/import \3 -H "Content-Type: application/json" \4 -d '{"url": "https://example.com/video", "videoQuality": "1080p"}'56# Check status7curl http://localhost:3000/import/YOUR_IMPORT_ID/status
Why Node.js Server-Side?
Server-side processing is ideal when you need:
- ✅ Secure API key storage - Never expose keys to clients
- ✅ Background processing - Imports run independently of user sessions
- ✅ Webhook reliability - Server is always available to receive notifications
- ✅ Database integration - Easy to store and query import history
- ✅ Batch processing - Handle multiple imports efficiently
Best Practices
- Use a database instead of in-memory storage for production
- Implement webhook signature verification for security
- Add request logging and monitoring
- Use environment variables for all configuration
- Implement rate limiting to protect your server
- Add proper error handling and retry logic
- Use a job queue (like Bull/Redis) for heavy workloads
Production Considerations
- Database: Use PostgreSQL, MongoDB, or similar for persistence
- Queue: Implement Redis/Bull for background job processing
- Monitoring: Add logging with Winston, metrics with Prometheus
- Security: Implement authentication, rate limiting, input validation
- Deployment: Use PM2, Docker, or serverless platforms
Complete Example
Check out our complete Node.js example on GitHub for a full implementation with database integration and advanced features.