How to Build an Influencer Database from Scratch with JavaScript
You're running an influencer marketing agency. Or building a SaaS tool. Or just trying to find creators for your brand.
Either way, you need a database of influencers. Not a spreadsheet you manually update. A real, automated system that:
- Discovers new creators automatically
- Tracks follower growth over time
- Stores engagement metrics across platforms
- Lets you search and filter by niche, size, and performance
The problem? Influencer databases like Modash cost $200-1,000/month. CreatorIQ requires enterprise contracts. And manually tracking creators in Google Sheets makes you want to quit marketing entirely.
In this tutorial, I'll show you how to build your own influencer database using JavaScript, SQLite, and the SociaVault API. By the end, you'll have a system that rivals tools costing thousands per year.
Need data from multiple platforms? Learn how to scrape social media data reliably.
What We're Building
An influencer database system that:
- Collects profiles from TikTok, Instagram, and YouTube
- Stores data in a local SQLite database
- Tracks metrics over time (followers, engagement, posts)
- Searches and filters by criteria you define
- Exports lists for outreach campaigns
Tech Stack:
- Node.js
- SQLite (via better-sqlite3)
- SociaVault API for data collection
- Express for a simple web interface
Step 1: Project Setup
mkdir influencer-database
cd influencer-database
npm init -y
npm install axios better-sqlite3 express dotenv
Create .env:
SOCIAVAULT_API_KEY=your_api_key_here
PORT=3000
Step 2: Database Schema
Create database.js:
const Database = require('better-sqlite3');
const db = new Database('influencers.db');
// Initialize tables
db.exec(`
CREATE TABLE IF NOT EXISTS influencers (
id INTEGER PRIMARY KEY AUTOINCREMENT,
platform TEXT NOT NULL,
username TEXT NOT NULL,
display_name TEXT,
bio TEXT,
profile_url TEXT,
profile_image TEXT,
followers INTEGER DEFAULT 0,
following INTEGER DEFAULT 0,
posts_count INTEGER DEFAULT 0,
engagement_rate REAL DEFAULT 0,
avg_views INTEGER DEFAULT 0,
avg_likes INTEGER DEFAULT 0,
avg_comments INTEGER DEFAULT 0,
niche TEXT,
email TEXT,
country TEXT,
verified INTEGER DEFAULT 0,
tier TEXT,
last_updated TEXT,
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
UNIQUE(platform, username)
);
CREATE TABLE IF NOT EXISTS metrics_history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
influencer_id INTEGER,
followers INTEGER,
engagement_rate REAL,
avg_views INTEGER,
recorded_at TEXT DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (influencer_id) REFERENCES influencers(id)
);
CREATE TABLE IF NOT EXISTS posts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
influencer_id INTEGER,
platform TEXT,
post_id TEXT,
post_url TEXT,
caption TEXT,
views INTEGER DEFAULT 0,
likes INTEGER DEFAULT 0,
comments INTEGER DEFAULT 0,
shares INTEGER DEFAULT 0,
posted_at TEXT,
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (influencer_id) REFERENCES influencers(id),
UNIQUE(platform, post_id)
);
CREATE TABLE IF NOT EXISTS lists (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
description TEXT,
created_at TEXT DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS list_members (
list_id INTEGER,
influencer_id INTEGER,
added_at TEXT DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (list_id, influencer_id),
FOREIGN KEY (list_id) REFERENCES lists(id),
FOREIGN KEY (influencer_id) REFERENCES influencers(id)
);
CREATE INDEX IF NOT EXISTS idx_influencers_platform ON influencers(platform);
CREATE INDEX IF NOT EXISTS idx_influencers_followers ON influencers(followers);
CREATE INDEX IF NOT EXISTS idx_influencers_niche ON influencers(niche);
CREATE INDEX IF NOT EXISTS idx_influencers_tier ON influencers(tier);
`);
module.exports = db;
Step 3: Data Collection Module
Create collector.js:
require('dotenv').config();
const axios = require('axios');
const db = require('./database');
const API_BASE = 'https://api.sociavault.com/v1/scrape';
const headers = { 'Authorization': `Bearer ${process.env.SOCIAVAULT_API_KEY}` };
// Determine influencer tier based on followers
function getTier(followers) {
if (followers >= 1000000) return 'mega';
if (followers >= 100000) return 'macro';
if (followers >= 10000) return 'micro';
if (followers >= 1000) return 'nano';
return 'rising';
}
// Calculate engagement rate
function calculateEngagement(avgLikes, avgComments, followers) {
if (!followers || followers === 0) return 0;
return ((avgLikes + avgComments) / followers * 100).toFixed(2);
}
// ========== TikTok Collector ==========
async function collectTikTokProfile(username) {
try {
// Get profile
const profileRes = await axios.get(`${API_BASE}/tiktok/profile`, {
params: { username },
headers
});
const profile = profileRes.data.data;
// Get recent videos for engagement calculation
const videosRes = await axios.get(`${API_BASE}/tiktok/videos`, {
params: { username, count: 30 },
headers
});
const videos = videosRes.data.data || [];
// Calculate averages
const avgViews = videos.length > 0
? Math.round(videos.reduce((sum, v) => sum + (v.playCount || 0), 0) / videos.length)
: 0;
const avgLikes = videos.length > 0
? Math.round(videos.reduce((sum, v) => sum + (v.diggCount || 0), 0) / videos.length)
: 0;
const avgComments = videos.length > 0
? Math.round(videos.reduce((sum, v) => sum + (v.commentCount || 0), 0) / videos.length)
: 0;
const followers = profile.followerCount || profile.stats?.followerCount || 0;
const engagementRate = calculateEngagement(avgLikes, avgComments, followers);
const influencerData = {
platform: 'tiktok',
username: profile.uniqueId,
display_name: profile.nickname,
bio: profile.signature,
profile_url: `https://tiktok.com/@${profile.uniqueId}`,
profile_image: profile.avatarLarger || profile.avatarMedium,
followers: followers,
following: profile.followingCount || profile.stats?.followingCount || 0,
posts_count: profile.videoCount || profile.stats?.videoCount || 0,
engagement_rate: engagementRate,
avg_views: avgViews,
avg_likes: avgLikes,
avg_comments: avgComments,
verified: profile.verified ? 1 : 0,
tier: getTier(followers),
last_updated: new Date().toISOString()
};
// Upsert influencer
const stmt = db.prepare(`
INSERT INTO influencers (
platform, username, display_name, bio, profile_url, profile_image,
followers, following, posts_count, engagement_rate, avg_views,
avg_likes, avg_comments, verified, tier, last_updated
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT(platform, username) DO UPDATE SET
display_name = excluded.display_name,
bio = excluded.bio,
profile_image = excluded.profile_image,
followers = excluded.followers,
following = excluded.following,
posts_count = excluded.posts_count,
engagement_rate = excluded.engagement_rate,
avg_views = excluded.avg_views,
avg_likes = excluded.avg_likes,
avg_comments = excluded.avg_comments,
verified = excluded.verified,
tier = excluded.tier,
last_updated = excluded.last_updated
`);
const result = stmt.run(
influencerData.platform, influencerData.username, influencerData.display_name,
influencerData.bio, influencerData.profile_url, influencerData.profile_image,
influencerData.followers, influencerData.following, influencerData.posts_count,
influencerData.engagement_rate, influencerData.avg_views, influencerData.avg_likes,
influencerData.avg_comments, influencerData.verified, influencerData.tier,
influencerData.last_updated
);
// Get the influencer ID
const influencer = db.prepare(
'SELECT id FROM influencers WHERE platform = ? AND username = ?'
).get('tiktok', profile.uniqueId);
// Store metrics history
db.prepare(`
INSERT INTO metrics_history (influencer_id, followers, engagement_rate, avg_views)
VALUES (?, ?, ?, ?)
`).run(influencer.id, influencerData.followers, influencerData.engagement_rate, influencerData.avg_views);
// Store recent posts
const postStmt = db.prepare(`
INSERT INTO posts (influencer_id, platform, post_id, post_url, caption, views, likes, comments, shares, posted_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT(platform, post_id) DO UPDATE SET
views = excluded.views,
likes = excluded.likes,
comments = excluded.comments,
shares = excluded.shares
`);
for (const video of videos.slice(0, 10)) {
postStmt.run(
influencer.id,
'tiktok',
video.id,
`https://tiktok.com/@${profile.uniqueId}/video/${video.id}`,
video.desc?.substring(0, 500),
video.playCount || 0,
video.diggCount || 0,
video.commentCount || 0,
video.shareCount || 0,
video.createTime ? new Date(video.createTime * 1000).toISOString() : null
);
}
console.log(`✅ Collected TikTok: @${username} (${followers.toLocaleString()} followers)`);
return influencerData;
} catch (error) {
console.error(`❌ Error collecting TikTok @${username}:`, error.message);
return null;
}
}
// ========== Instagram Collector ==========
async function collectInstagramProfile(username) {
try {
const profileRes = await axios.get(`${API_BASE}/instagram/profile`, {
params: { username },
headers
});
const profile = profileRes.data.data;
// Get recent posts
const postsRes = await axios.get(`${API_BASE}/instagram/posts`, {
params: { username, count: 30 },
headers
});
const posts = postsRes.data.data || [];
const avgLikes = posts.length > 0
? Math.round(posts.reduce((sum, p) => sum + (p.like_count || p.likes || 0), 0) / posts.length)
: 0;
const avgComments = posts.length > 0
? Math.round(posts.reduce((sum, p) => sum + (p.comment_count || p.comments || 0), 0) / posts.length)
: 0;
const avgViews = posts.length > 0
? Math.round(posts.reduce((sum, p) => sum + (p.play_count || p.video_view_count || 0), 0) / posts.length)
: 0;
const followers = profile.follower_count || profile.followers || 0;
const engagementRate = calculateEngagement(avgLikes, avgComments, followers);
const influencerData = {
platform: 'instagram',
username: profile.username,
display_name: profile.full_name,
bio: profile.biography,
profile_url: `https://instagram.com/${profile.username}`,
profile_image: profile.profile_pic_url,
followers: followers,
following: profile.following_count || profile.following || 0,
posts_count: profile.media_count || profile.posts || 0,
engagement_rate: engagementRate,
avg_views: avgViews,
avg_likes: avgLikes,
avg_comments: avgComments,
verified: profile.is_verified ? 1 : 0,
tier: getTier(followers),
last_updated: new Date().toISOString()
};
// Upsert (same as TikTok)
const stmt = db.prepare(`
INSERT INTO influencers (
platform, username, display_name, bio, profile_url, profile_image,
followers, following, posts_count, engagement_rate, avg_views,
avg_likes, avg_comments, verified, tier, last_updated
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT(platform, username) DO UPDATE SET
display_name = excluded.display_name,
bio = excluded.bio,
profile_image = excluded.profile_image,
followers = excluded.followers,
following = excluded.following,
posts_count = excluded.posts_count,
engagement_rate = excluded.engagement_rate,
avg_views = excluded.avg_views,
avg_likes = excluded.avg_likes,
avg_comments = excluded.avg_comments,
verified = excluded.verified,
tier = excluded.tier,
last_updated = excluded.last_updated
`);
stmt.run(
influencerData.platform, influencerData.username, influencerData.display_name,
influencerData.bio, influencerData.profile_url, influencerData.profile_image,
influencerData.followers, influencerData.following, influencerData.posts_count,
influencerData.engagement_rate, influencerData.avg_views, influencerData.avg_likes,
influencerData.avg_comments, influencerData.verified, influencerData.tier,
influencerData.last_updated
);
console.log(`✅ Collected Instagram: @${username} (${followers.toLocaleString()} followers)`);
return influencerData;
} catch (error) {
console.error(`❌ Error collecting Instagram @${username}:`, error.message);
return null;
}
}
// ========== YouTube Collector ==========
async function collectYouTubeChannel(channelUrl) {
try {
const channelRes = await axios.get(`${API_BASE}/youtube/channel`, {
params: { url: channelUrl },
headers
});
const channel = channelRes.data.data;
const subscribers = channel.subscriberCount || channel.subscribers || 0;
const username = channel.customUrl || channel.handle || channel.channelId;
const influencerData = {
platform: 'youtube',
username: username,
display_name: channel.title || channel.name,
bio: channel.description?.substring(0, 500),
profile_url: channelUrl,
profile_image: channel.thumbnail || channel.avatar,
followers: subscribers,
following: 0,
posts_count: channel.videoCount || 0,
engagement_rate: 0,
avg_views: channel.averageViews || 0,
avg_likes: 0,
avg_comments: 0,
verified: channel.isVerified ? 1 : 0,
tier: getTier(subscribers),
last_updated: new Date().toISOString()
};
const stmt = db.prepare(`
INSERT INTO influencers (
platform, username, display_name, bio, profile_url, profile_image,
followers, following, posts_count, engagement_rate, avg_views,
avg_likes, avg_comments, verified, tier, last_updated
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT(platform, username) DO UPDATE SET
display_name = excluded.display_name,
bio = excluded.bio,
profile_image = excluded.profile_image,
followers = excluded.followers,
posts_count = excluded.posts_count,
avg_views = excluded.avg_views,
verified = excluded.verified,
tier = excluded.tier,
last_updated = excluded.last_updated
`);
stmt.run(
influencerData.platform, influencerData.username, influencerData.display_name,
influencerData.bio, influencerData.profile_url, influencerData.profile_image,
influencerData.followers, influencerData.following, influencerData.posts_count,
influencerData.engagement_rate, influencerData.avg_views, influencerData.avg_likes,
influencerData.avg_comments, influencerData.verified, influencerData.tier,
influencerData.last_updated
);
console.log(`✅ Collected YouTube: ${channel.title} (${subscribers.toLocaleString()} subscribers)`);
return influencerData;
} catch (error) {
console.error(`❌ Error collecting YouTube:`, error.message);
return null;
}
}
// ========== Bulk Collection ==========
async function collectBatch(usernames, platform, delayMs = 1000) {
const results = [];
for (const username of usernames) {
let result;
switch (platform) {
case 'tiktok':
result = await collectTikTokProfile(username);
break;
case 'instagram':
result = await collectInstagramProfile(username);
break;
case 'youtube':
result = await collectYouTubeChannel(username);
break;
}
if (result) results.push(result);
// Rate limiting
await new Promise(resolve => setTimeout(resolve, delayMs));
}
return results;
}
module.exports = {
collectTikTokProfile,
collectInstagramProfile,
collectYouTubeChannel,
collectBatch
};
Step 4: Search & Filter Module
Create search.js:
const db = require('./database');
function searchInfluencers(filters = {}) {
let query = 'SELECT * FROM influencers WHERE 1=1';
const params = [];
if (filters.platform) {
query += ' AND platform = ?';
params.push(filters.platform);
}
if (filters.minFollowers) {
query += ' AND followers >= ?';
params.push(filters.minFollowers);
}
if (filters.maxFollowers) {
query += ' AND followers <= ?';
params.push(filters.maxFollowers);
}
if (filters.tier) {
query += ' AND tier = ?';
params.push(filters.tier);
}
if (filters.minEngagement) {
query += ' AND engagement_rate >= ?';
params.push(filters.minEngagement);
}
if (filters.niche) {
query += ' AND niche LIKE ?';
params.push(`%${filters.niche}%`);
}
if (filters.keyword) {
query += ' AND (bio LIKE ? OR display_name LIKE ? OR username LIKE ?)';
params.push(`%${filters.keyword}%`, `%${filters.keyword}%`, `%${filters.keyword}%`);
}
if (filters.verified !== undefined) {
query += ' AND verified = ?';
params.push(filters.verified ? 1 : 0);
}
// Sorting
const sortColumn = filters.sortBy || 'followers';
const sortOrder = filters.sortOrder || 'DESC';
query += ` ORDER BY ${sortColumn} ${sortOrder}`;
// Pagination
const limit = filters.limit || 50;
const offset = filters.offset || 0;
query += ' LIMIT ? OFFSET ?';
params.push(limit, offset);
return db.prepare(query).all(...params);
}
function getInfluencerStats() {
const stats = db.prepare(`
SELECT
COUNT(*) as total,
COUNT(CASE WHEN platform = 'tiktok' THEN 1 END) as tiktok,
COUNT(CASE WHEN platform = 'instagram' THEN 1 END) as instagram,
COUNT(CASE WHEN platform = 'youtube' THEN 1 END) as youtube,
COUNT(CASE WHEN tier = 'mega' THEN 1 END) as mega,
COUNT(CASE WHEN tier = 'macro' THEN 1 END) as macro,
COUNT(CASE WHEN tier = 'micro' THEN 1 END) as micro,
COUNT(CASE WHEN tier = 'nano' THEN 1 END) as nano,
AVG(engagement_rate) as avg_engagement,
AVG(followers) as avg_followers
FROM influencers
`).get();
return stats;
}
function getGrowthHistory(influencerId, days = 30) {
return db.prepare(`
SELECT * FROM metrics_history
WHERE influencer_id = ?
AND recorded_at >= datetime('now', '-${days} days')
ORDER BY recorded_at ASC
`).all(influencerId);
}
function exportToCSV(filters = {}) {
const influencers = searchInfluencers({ ...filters, limit: 10000 });
const headers = [
'Platform', 'Username', 'Display Name', 'Followers', 'Engagement Rate',
'Avg Views', 'Avg Likes', 'Posts', 'Tier', 'Verified', 'Bio', 'Profile URL'
];
const rows = influencers.map(i => [
i.platform,
i.username,
i.display_name,
i.followers,
i.engagement_rate,
i.avg_views,
i.avg_likes,
i.posts_count,
i.tier,
i.verified ? 'Yes' : 'No',
`"${(i.bio || '').replace(/"/g, '""')}"`,
i.profile_url
]);
return [headers.join(','), ...rows.map(r => r.join(','))].join('\n');
}
module.exports = {
searchInfluencers,
getInfluencerStats,
getGrowthHistory,
exportToCSV
};
Step 5: Web Interface
Create server.js:
require('dotenv').config();
const express = require('express');
const db = require('./database');
const { collectTikTokProfile, collectInstagramProfile, collectYouTubeChannel } = require('./collector');
const { searchInfluencers, getInfluencerStats, exportToCSV } = require('./search');
const app = express();
app.use(express.json());
// Dashboard
app.get('/', (req, res) => {
const stats = getInfluencerStats();
res.send(`
<!DOCTYPE html>
<html>
<head>
<title>Influencer Database</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: -apple-system, sans-serif; background: #0f172a; color: #e2e8f0; padding: 20px; }
.container { max-width: 1400px; margin: 0 auto; }
h1 { margin-bottom: 20px; }
.stats { display: grid; grid-template-columns: repeat(5, 1fr); gap: 15px; margin-bottom: 30px; }
.stat { background: #1e293b; padding: 20px; border-radius: 12px; }
.stat-value { font-size: 2rem; font-weight: bold; color: #3b82f6; }
.stat-label { color: #94a3b8; font-size: 0.875rem; }
.filters { background: #1e293b; padding: 20px; border-radius: 12px; margin-bottom: 20px; }
.filter-row { display: flex; gap: 10px; flex-wrap: wrap; }
input, select { padding: 10px; border: 1px solid #334155; border-radius: 6px; background: #0f172a; color: #fff; }
button { padding: 10px 20px; background: #3b82f6; color: white; border: none; border-radius: 6px; cursor: pointer; }
button:hover { background: #2563eb; }
table { width: 100%; border-collapse: collapse; background: #1e293b; border-radius: 8px; overflow: hidden; }
th, td { padding: 12px; text-align: left; border-bottom: 1px solid #334155; }
th { background: #0f172a; }
.tier { padding: 4px 8px; border-radius: 4px; font-size: 0.75rem; }
.tier-mega { background: #7c3aed; }
.tier-macro { background: #3b82f6; }
.tier-micro { background: #10b981; }
.tier-nano { background: #f59e0b; }
.add-form { background: #1e293b; padding: 20px; border-radius: 12px; margin-bottom: 20px; }
</style>
</head>
<body>
<div class="container">
<h1>📊 Influencer Database</h1>
<div class="stats">
<div class="stat"><div class="stat-value">${stats.total}</div><div class="stat-label">Total Influencers</div></div>
<div class="stat"><div class="stat-value">${stats.tiktok}</div><div class="stat-label">TikTok</div></div>
<div class="stat"><div class="stat-value">${stats.instagram}</div><div class="stat-label">Instagram</div></div>
<div class="stat"><div class="stat-value">${stats.youtube}</div><div class="stat-label">YouTube</div></div>
<div class="stat"><div class="stat-value">${(stats.avg_engagement || 0).toFixed(2)}%</div><div class="stat-label">Avg Engagement</div></div>
</div>
<div class="add-form">
<h3 style="margin-bottom: 15px;">➕ Add Influencer</h3>
<div class="filter-row">
<select id="addPlatform">
<option value="tiktok">TikTok</option>
<option value="instagram">Instagram</option>
<option value="youtube">YouTube</option>
</select>
<input type="text" id="addUsername" placeholder="Username or URL" style="flex: 1;">
<button onclick="addInfluencer()">Add</button>
</div>
</div>
<div class="filters">
<h3 style="margin-bottom: 15px;">🔍 Search & Filter</h3>
<div class="filter-row">
<select id="filterPlatform">
<option value="">All Platforms</option>
<option value="tiktok">TikTok</option>
<option value="instagram">Instagram</option>
<option value="youtube">YouTube</option>
</select>
<select id="filterTier">
<option value="">All Tiers</option>
<option value="mega">Mega (1M+)</option>
<option value="macro">Macro (100K+)</option>
<option value="micro">Micro (10K+)</option>
<option value="nano">Nano (1K+)</option>
</select>
<input type="text" id="filterKeyword" placeholder="Search bio or name...">
<input type="number" id="minEngagement" placeholder="Min Engagement %" style="width: 150px;">
<button onclick="applyFilters()">Search</button>
<button onclick="exportCSV()" style="background: #10b981;">Export CSV</button>
</div>
</div>
<table id="resultsTable">
<thead>
<tr>
<th>Platform</th>
<th>Username</th>
<th>Followers</th>
<th>Engagement</th>
<th>Avg Views</th>
<th>Tier</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="resultsBody"></tbody>
</table>
</div>
<script>
async function loadInfluencers(filters = {}) {
const params = new URLSearchParams(filters);
const res = await fetch('/api/influencers?' + params);
const data = await res.json();
document.getElementById('resultsBody').innerHTML = data.map(i => \`
<tr>
<td>\${i.platform}</td>
<td><a href="\${i.profile_url}" target="_blank" style="color: #3b82f6;">@\${i.username}</a></td>
<td>\${formatNumber(i.followers)}</td>
<td>\${i.engagement_rate}%</td>
<td>\${formatNumber(i.avg_views)}</td>
<td><span class="tier tier-\${i.tier}">\${i.tier.toUpperCase()}</span></td>
<td><button onclick="refreshInfluencer('\${i.platform}', '\${i.username}')" style="padding: 5px 10px; font-size: 12px;">🔄</button></td>
</tr>
\`).join('');
}
async function addInfluencer() {
const platform = document.getElementById('addPlatform').value;
const username = document.getElementById('addUsername').value;
await fetch('/api/collect', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ platform, username })
});
document.getElementById('addUsername').value = '';
loadInfluencers();
}
async function refreshInfluencer(platform, username) {
await fetch('/api/collect', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ platform, username })
});
loadInfluencers();
}
function applyFilters() {
const filters = {
platform: document.getElementById('filterPlatform').value,
tier: document.getElementById('filterTier').value,
keyword: document.getElementById('filterKeyword').value,
minEngagement: document.getElementById('minEngagement').value
};
loadInfluencers(filters);
}
async function exportCSV() {
const filters = {
platform: document.getElementById('filterPlatform').value,
tier: document.getElementById('filterTier').value
};
const params = new URLSearchParams(filters);
window.location.href = '/api/export?' + params;
}
function formatNumber(n) {
if (n >= 1000000) return (n/1000000).toFixed(1) + 'M';
if (n >= 1000) return (n/1000).toFixed(1) + 'K';
return n || 0;
}
loadInfluencers();
</script>
</body>
</html>
`);
});
// API: Search influencers
app.get('/api/influencers', (req, res) => {
const results = searchInfluencers(req.query);
res.json(results);
});
// API: Collect influencer
app.post('/api/collect', async (req, res) => {
const { platform, username } = req.body;
let result;
switch (platform) {
case 'tiktok':
result = await collectTikTokProfile(username);
break;
case 'instagram':
result = await collectInstagramProfile(username);
break;
case 'youtube':
result = await collectYouTubeChannel(username);
break;
}
res.json({ success: !!result, data: result });
});
// API: Export CSV
app.get('/api/export', (req, res) => {
const csv = exportToCSV(req.query);
res.setHeader('Content-Type', 'text/csv');
res.setHeader('Content-Disposition', 'attachment; filename=influencers.csv');
res.send(csv);
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`\n📊 Influencer Database running at http://localhost:${PORT}\n`);
});
Step 6: Run It
node server.js
Open http://localhost:3000 and start building your database!
Sample Output
After adding a few influencers:
📊 Influencer Database Stats
═══════════════════════════════════════════════════════
Total Influencers: 47
├── TikTok: 23
├── Instagram: 18
├── YouTube: 6
By Tier:
├── Mega (1M+): 3
├── Macro (100K-1M): 12
├── Micro (10K-100K): 24
├── Nano (1K-10K): 8
Average Engagement: 4.7%
Top Performers:
1. @charlidamelio (TikTok) - 155M followers, 8.2% engagement
2. @khaby.lame (TikTok) - 162M followers, 6.1% engagement
3. @mrbeast (YouTube) - 245M subscribers
What You Just Built
Influencer database tools cost serious money:
- Modash: $200-1,000/month
- CreatorIQ: Enterprise pricing ($$$)
- Upfluence: $795+/month
Your version does the same thing for API costs only.
Next Steps
- Add niche detection using bio keywords
- Build email finder integration
- Create automated discovery from hashtags
- Add competitor influencer tracking
Related: Learn how to find micro-influencers and vet for fake followers.
Ready to start building?
Get your free API key at sociavault.com and build the influencer database you've always wanted.
Found this helpful?
Share it with others who might benefit
Ready to Try SociaVault?
Start extracting social media data with our powerful API. No credit card required.