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 getBasicMetadata(url, webhookUrl = null) {56 try {57 const params = { url };58 if (webhookUrl) params.webhookUrl = webhookUrl;5960 const response = await this.client.get("/metadata/basic", { params });61 return response.data;62 } catch (error) {63 throw this.handleError(error);64 }65 }6667 async checkImportStatus(jobId) {68 try {69 const response = await this.client.get(`/import/status?id=${jobId}`);70 return response.data;71 } catch (error) {72 throw this.handleError(error);73 }74 }7576 async checkMetadataStatus(jobId) {77 try {78 const response = await this.client.get(`/metadata/status?id=${jobId}`);79 return response.data;80 } catch (error) {81 throw this.handleError(error);82 }83 }8485 async checkBasicMetadataStatus(jobId) {86 try {87 const response = await this.client.get(`/metadata/basic/status?id=${jobId}`);88 return response.data;89 } catch (error) {90 throw this.handleError(error);91 }92 }9394 async waitForCompletion(95 id,96 type = "import",97 maxWaitTime = 300000,98 pollInterval = 500099 ) {100 const startTime = Date.now();101 const checkStatus =102 type === "import"103 ? this.checkImportStatus.bind(this)104 : this.checkMetadataStatus.bind(this);105106 while (Date.now() - startTime < maxWaitTime) {107 try {108 const result = await checkStatus(id);109 const { status } = result.data;110111 if (status === "completed") {112 return result;113 } else if (status === "failed" || status === "cancelled") {114 throw new Error(115 `${type} ${status}: ${result.data.error || "Unknown error"}`116 );117 }118119 // Wait before next poll120 await new Promise((resolve) => setTimeout(resolve, pollInterval));121 } catch (error) {122 if (error.response?.status === 404) {123 throw new Error(`${type} not found`);124 }125 throw error;126 }127 }128129 throw new Error(`${type} timed out after ${maxWaitTime}ms`);130 }131132 handleError(error) {133 if (error.response) {134 const { status, data } = error.response;135 const message = data?.message || data?.error || "API request failed";136137 switch (status) {138 case 401:139 return new Error("Invalid API key");140 case 402:141 return new Error("Insufficient credits");142 case 429:143 return new Error("Rate limit exceeded");144 case 400:145 return new Error(`Bad request: ${message}`);146 default:147 return new Error(`API error (${status}): ${message}`);148 }149 } else if (error.request) {150 return new Error("Network error: Unable to reach Importly API");151 } else {152 return new Error(`Request error: ${error.message}`);153 }154 }155}156157module.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 case "basic-metadata.completed":159 handleBasicMetadataCompleted(data);160 break;161 case "basic-metadata.failed":162 handleBasicMetadataFailed(data);163 break;164 default:165 console.log("Unknown webhook type:", type);166 }167168 res.json({ received: true });169 } catch (error) {170 console.error("Webhook error:", error);171 res.status(500).json({ error: "Webhook processing failed" });172 }173});174175// Webhook handlers176function handleImportCompleted(data) {177 const { jobId, result } = data;178179 if (imports.has(jobId)) {180 imports.set(jobId, {181 ...imports.get(jobId),182 status: "completed",183 result,184 completedAt: new Date().toISOString(),185 });186 }187188 console.log("Import completed:", jobId);189190 // Add your custom logic here:191 // - Send notifications192 // - Update database193 // - Process the media file194 // - Trigger downstream workflows195}196197function handleImportFailed(data) {198 const { jobId, error } = data;199200 if (imports.has(jobId)) {201 imports.set(jobId, {202 ...imports.get(jobId),203 status: "failed",204 error,205 failedAt: new Date().toISOString(),206 });207 }208209 console.error("Import failed:", jobId, error);210}211212function handleMetadataCompleted(data) {213 const { jobId, result } = data;214215 if (metadata.has(jobId)) {216 metadata.set(jobId, {217 ...metadata.get(jobId),218 status: "completed",219 result,220 completedAt: new Date().toISOString(),221 });222 }223224 console.log("Metadata completed:", jobId);225}226227function handleMetadataFailed(data) {228 const { jobId, error } = data;229230 if (metadata.has(jobId)) {231 metadata.set(jobId, {232 ...metadata.get(jobId),233 status: "failed",234 error,235 failedAt: new Date().toISOString(),236 });237 }238239 console.error("Metadata failed:", jobId, error);240}241242function handleBasicMetadataCompleted(data) {243 const { jobId, result } = data;244 console.log("Basic metadata completed:", jobId, result);245 // result contains: { title, duration, thumbnail }246}247248function handleBasicMetadataFailed(data) {249 const { jobId, error } = data;250 console.error("Basic metadata failed:", jobId, error);251}252253// List endpoints for debugging254app.get("/imports", (req, res) => {255 const allImports = Array.from(imports.values()).sort(256 (a, b) => new Date(b.createdAt) - new Date(a.createdAt)257 );258259 res.json({ imports: allImports });260});261262app.get("/metadata", (req, res) => {263 const allMetadata = Array.from(metadata.values()).sort(264 (a, b) => new Date(b.createdAt) - new Date(a.createdAt)265 );266267 res.json({ metadata: allMetadata });268});269270// Health check271app.get("/health", (req, res) => {272 res.json({273 status: "ok",274 timestamp: new Date().toISOString(),275 imports: imports.size,276 metadata: metadata.size,277 });278});279280app.listen(port, () => {281 console.log(`Server running on port ${port}`);282 console.log(`Webhook URL: ${process.env.WEBHOOK_URL}`);283});
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.