Node.js SDK
Install
npm install multi-upload-toolRequires Node.js 18+. Zero dependencies.
Quick Start
import { MultiUploadClient } from 'multi-upload-tool';
const client = new MultiUploadClient({ apiToken: 'your-api-token' });
// Upload a video to one account
const upload = await client.uploads.upload({
accountId: 123,
filePath: '/path/to/video.mp4',
title: 'My Video',
description: 'Check this out!',
});
console.log(upload);Use environment variables in production:
const client = new MultiUploadClient({
apiToken: process.env.MULTI_UPLOAD_API_TOKEN,
});CommonJS usage:
const { MultiUploadClient } = require('multi-upload-tool');Uploads
Upload a video
const upload = await client.uploads.upload({
accountId: 123,
filePath: '/path/to/video.mp4',
title: 'My Video',
description: 'Check this out!',
tags: ['marketing', 'launch'], // Converted to comma-separated string
scheduledFor: '2025-03-01T14:00:00Z', // ISO 8601 for scheduling
});Upload an image carousel
Pass an array of file paths. The SDK auto-detects images vs videos by extension.
const upload = await client.uploads.upload({
accountId: 456,
filePath: [
'/path/to/photo1.jpg',
'/path/to/photo2.jpg',
'/path/to/photo3.jpg',
],
title: 'Summer Collection',
});Bulk upload to multiple accounts
Pass an array of account IDs to upload the same content to all of them at once.
const accounts = await client.accounts.list({ platform: 'tiktok', status: 'active' });
const accountIds = accounts.map(a => a.id);
const upload = await client.uploads.upload({
accountId: accountIds,
filePath: '/path/to/video.mp4',
title: 'Broadcast Video',
});Sync mode
By default, uploads are async (return immediately). Set asyncMode: false to wait for completion:
const upload = await client.uploads.upload({
accountId: 123,
filePath: '/path/to/video.mp4',
asyncMode: false, // Blocks until done
});YouTube-specific options
const upload = await client.uploads.upload({
accountId: 789,
filePath: '/path/to/video.mp4',
title: 'My YouTube Video',
privacyStatus: 'unlisted', // 'private', 'unlisted', or 'public'
categoryId: '22', // YouTube category
thumbnailPath: '/path/to/thumb.jpg',
tags: ['tech', 'tutorial'],
});Upload a Buffer
const fs = require('fs');
const buffer = fs.readFileSync('/path/to/video.mp4');
const upload = await client.uploads.upload({
accountId: 123,
filePath: buffer,
title: 'From Buffer',
});List and manage uploads
// List with filters and pagination
const uploads = await client.uploads.list({ page: 1, limit: 50, platform: 'tiktok', status: 'completed' });
// Get details
const upload = await client.uploads.get(456);
// Update
await client.uploads.update(456, { title: 'New Title', scheduledFor: '2025-04-01T10:00:00Z' });
// Check limits for an account
const limits = await client.uploads.limits(123);Accounts
// List all accounts
const accounts = await client.accounts.list();
// Filter by platform and status
const tiktok = await client.accounts.list({ platform: 'tiktok', status: 'active' });
// Get one account
const account = await client.accounts.get(123);
// Get LinkedIn Pages for a connected LinkedIn account
const pages = await client.accounts.linkedinPages(123);
// Disconnect an account
await client.accounts.delete(123);Platforms: tiktok, youtube, instagram, facebook, pinterest, linkedin, snapchat, etc.
Statuses: active, revoked, disconnected
Teams
// Get current team
const team = await client.teams.getCurrent();
// List members
const members = await client.teams.listMembers();
// Invite (roles: 'MEMBER' or 'ADMIN')
const invite = await client.teams.invite({ email: '[email protected]', role: 'MEMBER' });
// Update role
await client.teams.updateMemberRole(102, { role: 'ADMIN', isActive: true });
// Remove
await client.teams.removeMember(102);Short Links
// List
const links = await client.shortLinks.list();
// Create
const link = await client.shortLinks.create({
url: 'https://example.com/campaign',
slug: 'summer-2025',
title: 'Summer Campaign',
});
console.log(link.short_url);
// Get
const detail = await client.shortLinks.get(201);
// Update
await client.shortLinks.update(201, { destination: 'https://example.com/new', is_active: false });
// Delete
await client.shortLinks.delete(201);Webhooks
// List
const webhooks = await client.webhooks.list();
// Create
const webhook = await client.webhooks.create({
url: 'https://my-app.com/webhooks/uploads',
events: ['upload.completed', 'upload.failed'],
description: 'Upload notifications',
});
// Test
await client.webhooks.test(301);
// Delete
await client.webhooks.delete(301);Events: upload.started, upload.processing, upload.completed, upload.failed, upload.scheduled
Webhook payload
{
"id": "evt_123456",
"event": "upload.completed",
"timestamp": "2025-01-27T12:00:00Z",
"data": {
"upload_id": 456,
"account_id": 123,
"title": "My Video",
"status": "completed",
"platform": "tiktok",
"url": "https://www.tiktok.com/@user/video/123"
}
}Verify webhook signature (Express example)
const crypto = require('crypto');
function verifyWebhook(payload, signature, secret) {
const expected = crypto.createHmac('sha256', secret).update(payload).digest('hex');
return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}
app.post('/webhooks/uploads', (req, res) => {
const signature = req.headers['x-webhook-signature'];
if (!verifyWebhook(req.rawBody, signature, WEBHOOK_SECRET)) {
return res.status(401).json({ error: 'Invalid signature' });
}
const { event, data } = req.body;
console.log(`${event}: upload ${data.upload_id}`);
res.json({ ok: true });
});White Label
// List connection links
const links = await client.whitelabel.list();
// Create a connection link
const link = await client.whitelabel.create({
platform: 'tiktok', // 'tiktok' or 'youtube'
expiresInHours: 24,
logoUrl: 'https://...',
title: 'Connect Your Account',
redirectUrl: 'https://my-app.com/connected',
tags: ['campaign-a'],
});
// Delete
await client.whitelabel.delete(1);// Get boards for a connected Pinterest account
const boards = await client.pinterest.boards(123);Error Handling
const { MultiUploadClient, AuthenticationError, APIError } = require('multi-upload-tool');
try {
const upload = await client.uploads.upload({ ... });
} catch (err) {
if (err instanceof AuthenticationError) {
console.error('Invalid or expired API token');
} else if (err instanceof APIError) {
console.error(`API error ${err.statusCode}: ${err.message}`);
} else {
throw err;
}
}Exception hierarchy
| Error | When |
|---|---|
MultiUploadError | Base class for all SDK errors |
AuthenticationError | Invalid, expired, or revoked token (401) |
APIError | API returned an error (4xx/5xx). Has .statusCode and .response properties |
ValidationError | Input validation failed |
Common status codes
| Code | Meaning |
|---|---|
| 400 | Bad request / invalid parameters |
| 401 | Invalid API token |
| 403 | Permission denied |
| 404 | Resource not found |
| 429 | Rate limited — back off and retry |
| 500 | Server error — try again later |
Retry on rate limit
const { APIError } = require('multi-upload-tool');
async function withRetry(fn, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
return await fn();
} catch (err) {
if (err instanceof APIError && err.statusCode === 429) {
await new Promise(r => setTimeout(r, 2 ** i * 1000));
} else {
throw err;
}
}
}
}