Overview
Follow these best practices to build reliable, efficient, and scalable applications with SlideVid’s API.Authentication & Security
Store API Keys Securely
Never commit API keys to version control
Copy
# .env (add to .gitignore)
SLIDEVID_API_KEY=your_secret_key_here
Copy
// ✅ Good - Use environment variables
const apiKey = process.env.SLIDEVID_API_KEY;
// ❌ Bad - Hardcoded
const apiKey = 'sk_live_abc123...';
Use Different Keys for Environments
Copy
# .env.development
SLIDEVID_API_KEY=sk_dev_...
# .env.production
SLIDEVID_API_KEY=sk_live_...
Rotate Keys Regularly
- Rotate API keys every 90 days
- Immediately rotate if compromised
- Use key rotation without downtime:
Copy
const PRIMARY_KEY = process.env.SLIDEVID_API_KEY_PRIMARY;
const BACKUP_KEY = process.env.SLIDEVID_API_KEY_BACKUP;
async function makeRequestWithFailover(endpoint, options) {
try {
return await makeRequest(endpoint, { ...options, apiKey: PRIMARY_KEY });
} catch (error) {
if (error.code === 'INVALID_API_KEY') {
// Failover to backup key
return await makeRequest(endpoint, { ...options, apiKey: BACKUP_KEY });
}
throw error;
}
}
Error Handling
Implement Comprehensive Error Handling
Copy
class SlideVidAPIClient {
async request(endpoint, options) {
try {
const response = await fetch(endpoint, options);
const data = await response.json();
if (!response.ok) {
throw new APIError(data.message, response.status, data.code);
}
return data;
} catch (error) {
if (error instanceof APIError) {
// Handle known API errors
this.handleAPIError(error);
} else if (error.name === 'TypeError') {
// Network error
throw new Error('Network connection failed');
} else {
// Unknown error
this.logError(error);
throw error;
}
}
}
handleAPIError(error) {
switch (error.code) {
case 'RATE_LIMIT_EXCEEDED':
// Wait and retry
break;
case 'INVALID_API_KEY':
// Alert admin
break;
case 'VALIDATION_ERROR':
// Fix request data
break;
default:
throw error;
}
}
}
Use Retry Logic with Exponential Backoff
Copy
async function retryWithBackoff(fn, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
// Don't retry on client errors (4xx except 429)
if (error.status >= 400 && error.status < 500 && error.status !== 429) {
throw error;
}
if (i === maxRetries - 1) throw error;
// Exponential backoff: 2^i * 1000ms
const delay = Math.pow(2, i) * 1000;
await new Promise(r => setTimeout(r, delay));
}
}
}
// Usage
const result = await retryWithBackoff(() =>
api.createVideo(videoData)
);
Performance Optimization
Cache Resources
Copy
class ResourceCache {
constructor(ttl = 3600000) { // 1 hour
this.cache = new Map();
this.ttl = ttl;
}
async get(key, fetchFn) {
const cached = this.cache.get(key);
if (cached && Date.now() - cached.timestamp < this.ttl) {
return cached.data;
}
const data = await fetchFn();
this.cache.set(key, { data, timestamp: Date.now() });
return data;
}
}
const cache = new ResourceCache();
// Cache avatars list
const avatars = await cache.get('avatars', () =>
api.getAvatars()
);
// Cache voices list
const voices = await cache.get('voices', () =>
api.getVoices()
);
Batch Operations
Copy
// ❌ Bad - Sequential requests
for (const recipient of recipients) {
await api.createVideo(recipient);
}
// ✅ Good - Batched with concurrency limit
async function batchProcess(items, batchSize = 5) {
for (let i = 0; i < items.length; i += batchSize) {
const batch = items.slice(i, i + batchSize);
await Promise.all(batch.map(item => api.createVideo(item)));
}
}
await batchProcess(recipients, 5);
Minimize API Calls
Copy
// ❌ Bad - Multiple calls for same data
const avatar1 = await api.getAvatar('avatar_123');
const avatar2 = await api.getAvatar('avatar_123'); // Duplicate!
// ✅ Good - Fetch once, reuse
const avatars = await api.getAvatars();
const avatar = avatars.find(a => a.id === 'avatar_123');
Webhook Implementation
Always Use Webhooks, Not Polling
Copy
// ❌ Bad - Polling
async function waitForVideo(projectId) {
while (true) {
const video = await api.getVideo(projectId);
if (video.status === 'completed') return video;
await new Promise(r => setTimeout(r, 10000)); // Wasteful!
}
}
// ✅ Good - Webhook
app.post('/webhook', (req, res) => {
const { projectId, status, video } = req.body;
if (status === 'completed') {
handleCompletedVideo(projectId, video);
}
res.status(200).send('OK');
});
Implement Idempotency
Copy
const processedWebhooks = new Set();
app.post('/webhook', (req, res) => {
const { projectId } = req.body;
// Prevent duplicate processing
if (processedWebhooks.has(projectId)) {
return res.status(200).send('Already processed');
}
processedWebhooks.add(projectId);
// Process webhook
processWebhook(req.body);
res.status(200).send('OK');
});
Return 200 Immediately
Copy
// ✅ Good - Respond fast, process async
app.post('/webhook', async (req, res) => {
// Respond immediately
res.status(200).send('OK');
// Process asynchronously
setImmediate(async () => {
try {
await processWebhook(req.body);
} catch (error) {
console.error('Webhook processing error:', error);
}
});
});
Rate Limiting
Monitor Rate Limits
Copy
class RateLimitTracker {
checkHeaders(response) {
const limit = response.headers.get('X-RateLimit-Limit');
const remaining = response.headers.get('X-RateLimit-Remaining');
const reset = response.headers.get('X-RateLimit-Reset');
console.log(`Rate limit: ${remaining}/${limit}, resets at ${new Date(reset * 1000)}`);
if (remaining < 10) {
console.warn('⚠️ Approaching rate limit!');
}
}
}
Implement Request Queuing
Copy
import PQueue from 'p-queue';
const queue = new PQueue({
concurrency: 5,
interval: 1000,
intervalCap: 10
});
async function queuedRequest(endpoint, options) {
return queue.add(() => fetch(endpoint, options));
}
Logging & Monitoring
Structured Logging
Copy
class APILogger {
log(level, message, meta = {}) {
console.log(JSON.stringify({
timestamp: new Date().toISOString(),
level,
message,
...meta
}));
}
info(message, meta) { this.log('info', message, meta); }
error(message, meta) { this.log('error', message, meta); }
warn(message, meta) { this.log('warn', message, meta); }
}
const logger = new APILogger();
// Usage
logger.info('Video created', {
projectId: 'proj_123',
duration: 1250,
status: 'success'
});
Track Key Metrics
Copy
class MetricsCollector {
constructor() {
this.metrics = {
requests: 0,
successes: 0,
failures: 0,
totalDuration: 0
};
}
async trackRequest(fn) {
this.metrics.requests++;
const start = Date.now();
try {
const result = await fn();
this.metrics.successes++;
return result;
} catch (error) {
this.metrics.failures++;
throw error;
} finally {
this.metrics.totalDuration += Date.now() - start;
}
}
getStats() {
return {
...this.metrics,
successRate: (this.metrics.successes / this.metrics.requests) * 100,
avgDuration: this.metrics.totalDuration / this.metrics.requests
};
}
}
const metrics = new MetricsCollector();
// Track API calls
await metrics.trackRequest(() => api.createVideo(data));
// Log stats
console.log(metrics.getStats());
Data Validation
Validate Before Sending
Copy
const videoSchema = {
type: { required: true, enum: ['class', 'ugc_ads'] },
script: { required: true, minLength: 10, maxLength: 5000 },
avatarId: { required: true, pattern: /^avatar_/ },
voiceId: { required: true, pattern: /^voice_/ }
};
function validateVideoData(data, schema) {
const errors = [];
Object.entries(schema).forEach(([field, rules]) => {
const value = data[field];
if (rules.required && !value) {
errors.push(`${field} is required`);
}
if (rules.enum && !rules.enum.includes(value)) {
errors.push(`${field} must be one of: ${rules.enum.join(', ')}`);
}
if (rules.minLength && value.length < rules.minLength) {
errors.push(`${field} must be at least ${rules.minLength} characters`);
}
if (rules.pattern && !rules.pattern.test(value)) {
errors.push(`${field} has invalid format`);
}
});
return { valid: errors.length === 0, errors };
}
// Validate before API call
const validation = validateVideoData(videoData, videoSchema);
if (!validation.valid) {
throw new Error(`Validation failed: ${validation.errors.join(', ')}`);
}
Testing
Unit Test API Client
Copy
// api-client.test.js
import { describe, it, expect, vi } from 'vitest';
import { SlideVidAPIClient } from './api-client';
describe('SlideVidAPIClient', () => {
it('should create video successfully', async () => {
const client = new SlideVidAPIClient('test_key');
global.fetch = vi.fn(() =>
Promise.resolve({
ok: true,
json: () => Promise.resolve({
success: true,
data: { projectId: 'proj_123' }
})
})
);
const result = await client.createVideo({
type: 'class',
script: 'Test script'
});
expect(result.projectId).toBe('proj_123');
});
it('should handle errors', async () => {
const client = new SlideVidAPIClient('test_key');
global.fetch = vi.fn(() =>
Promise.resolve({
ok: false,
status: 400,
json: () => Promise.resolve({
success: false,
message: 'Invalid request'
})
})
);
await expect(client.createVideo({})).rejects.toThrow('Invalid request');
});
});
Integration Testing
Copy
// integration.test.js
describe('Video Creation Flow', () => {
it('should create and track video', async () => {
const api = new SlideVidAPIClient(process.env.TEST_API_KEY);
// 1. Get resources
const avatars = await api.getAvatars();
expect(avatars.length).toBeGreaterThan(0);
// 2. Create video
const projectId = await api.createVideo({
type: 'class',
script: 'Integration test video',
avatarId: avatars[0].id
});
expect(projectId).toBeTruthy();
// 3. Verify video exists
const videos = await api.listVideos();
expect(videos.some(v => v.id === projectId)).toBe(true);
});
});
Code Organization
Create Reusable SDK
Copy
// slidevid-sdk.js
export class SlideVidSDK {
constructor(apiKey, options = {}) {
this.apiKey = apiKey;
this.baseUrl = options.baseUrl || 'https://api.slidevid.ai/v1';
this.cache = new ResourceCache();
this.logger = new APILogger();
}
// Resource methods
async getAvatars(type = 'all') { /* ... */ }
async getVoices(filters = {}) { /* ... */ }
async getMusic() { /* ... */ }
// Template methods
async listTemplates() { /* ... */ }
async getTemplate(id) { /* ... */ }
// Video methods
async createVideo(data) { /* ... */ }
async listVideos(filters) { /* ... */ }
// High-level helpers
async createFromScratch(script, options) { /* ... */ }
async generateFromTemplate(templateId, variables) { /* ... */ }
async bulkGenerate(templateId, recipients) { /* ... */ }
}
// Usage
import { SlideVidSDK } from './slidevid-sdk';
const slidevid = new SlideVidSDK(process.env.SLIDEVID_API_KEY);
await slidevid.createVideo({ /* ... */ });
Deployment
Environment Configuration
Copy
// config.js
const config = {
development: {
apiKey: process.env.SLIDEVID_DEV_KEY,
baseUrl: 'https://dev.tryslidevid.ai/api/v1',
webhookUrl: 'https://dev-tunnel.ngrok.io/webhook',
logLevel: 'debug'
},
staging: {
apiKey: process.env.SLIDEVID_STAGING_KEY,
baseUrl: 'https://staging.tryslidevid.ai/api/v1',
webhookUrl: 'https://staging.myapp.com/webhook',
logLevel: 'info'
},
production: {
apiKey: process.env.SLIDEVID_API_KEY,
baseUrl: 'https://api.slidevid.ai/v1',
webhookUrl: 'https://myapp.com/webhook',
logLevel: 'error'
}
};
export default config[process.env.NODE_ENV || 'development'];
Health Checks
Copy
// health-check.js
async function checkAPIHealth() {
try {
const response = await fetch('https://api.slidevid.ai/v1/avatar/list', {
headers: { 'x-api-key': process.env.SLIDEVID_API_KEY }
});
if (!response.ok) {
throw new Error(`API health check failed: ${response.status}`);
}
return { status: 'healthy', timestamp: new Date() };
} catch (error) {
return { status: 'unhealthy', error: error.message, timestamp: new Date() };
}
}
// Run health check every 5 minutes
setInterval(async () => {
const health = await checkAPIHealth();
console.log('API Health:', health);
if (health.status === 'unhealthy') {
// Alert ops team
alertOps('SlideVid API is unhealthy', health);
}
}, 5 * 60 * 1000);
Production Checklist
Before going to production:Security
- API keys stored in environment variables
- Different keys for dev/staging/production
- Webhook endpoints use HTTPS
- Webhook signature verification implemented
- Rate limiting tracked and handled
Reliability
- Comprehensive error handling
- Retry logic with exponential backoff
- Webhook idempotency implemented
- Webhook returns 200 immediately
- Failed requests logged and alerted
Performance
- Resources cached appropriately
- Batch operations used for bulk tasks
- Unnecessary API calls eliminated
- Request timeouts configured
Monitoring
- Structured logging implemented
- Key metrics tracked
- Health checks configured
- Error alerts set up
- Dashboard for monitoring
Testing
- Unit tests for API client
- Integration tests pass
- Webhook endpoint tested
- Error scenarios tested
- Load testing completed