DEV Community

Cover image for Build a Creator Discovery Engine with Node.js and SociaVault
Olamide Olaniyan
Olamide Olaniyan

Posted on

Build a Creator Discovery Engine with Node.js and SociaVault

Your client sells vegan protein powder. They need 50 micro-influencers in the fitness + vegan niche. Budget: $10K.

You open Instagram. Search "vegan fitness." Scroll through profiles one by one. Copy-paste usernames into a spreadsheet. Check engagement manually. Repeat 200 times.

That's 8 hours of your life you're not getting back.

I built a tool that finds, filters, and ranks creators by niche in minutes. It searches across platforms, scores relevance, and spits out a ranked list. Here's the entire thing.

The Stack

  • Node.js – runtime
  • SociaVault API – search creators, fetch profiles, get post data
  • Natural language matching – keyword relevance scoring

The Architecture

Search Query ("vegan fitness micro-influencer")
        ↓
  Platform Search (TikTok, Instagram, YouTube)
        ↓
  Fetch Full Profiles
        ↓
  Filter (followers, engagement, niche)
        ↓
  Rank by Relevance Score
        ↓
  Output: Ranked Creator List
Enter fullscreen mode Exit fullscreen mode

Step 1: Set Up the API Client

const axios = require('axios');

const API_BASE = 'https://api.sociavault.com/v1';
const API_KEY = process.env.SOCIAVAULT_API_KEY;

const api = axios.create({
  baseURL: API_BASE,
  headers: { 'x-api-key': API_KEY },
});

async function searchUsers(platform, keyword, limit = 50) {
  const { data } = await api.get(`/${platform}/search/users`, {
    params: { keyword, limit },
  });
  return data.users;
}

async function getProfile(platform, username) {
  const { data } = await api.get(`/${platform}/profile/${username}`);
  return data;
}

async function getRecentPosts(platform, username, limit = 20) {
  const { data } = await api.get(`/${platform}/posts/${username}`, {
    params: { limit },
  });
  return data.posts;
}
Enter fullscreen mode Exit fullscreen mode

Step 2: Multi-Platform Search

Don't limit yourself to one platform. A creator might be huge on TikTok but undiscovered on Instagram.

async function multiPlatformSearch(keywords, platforms = ['tiktok', 'instagram', 'youtube']) {
  const allResults = [];

  for (const platform of platforms) {
    for (const keyword of keywords) {
      try {
        const users = await searchUsers(platform, keyword, 30);
        allResults.push(
          ...users.map(user => ({
            ...user,
            platform,
            searchKeyword: keyword,
          }))
        );
      } catch (err) {
        console.error(`Search failed for ${keyword} on ${platform}:`, err.message);
      }
    }
  }

  // Deduplicate by username + platform
  const seen = new Set();
  return allResults.filter(user => {
    const key = `${user.platform}:${user.username}`;
    if (seen.has(key)) return false;
    seen.add(key);
    return true;
  });
}
Enter fullscreen mode Exit fullscreen mode

Usage:

const candidates = await multiPlatformSearch(
  ['vegan fitness', 'plant based athlete', 'vegan gym', 'vegan protein'],
  ['tiktok', 'instagram']
);

console.log(`Found ${candidates.length} candidates`);
// Found 187 candidates
Enter fullscreen mode Exit fullscreen mode

Step 3: Filter by Criteria

Most creators won't match your requirements. Filter aggressively.

function filterCandidates(candidates, criteria) {
  return candidates.filter(user => {
    // Follower range (micro-influencer: 10K-100K)
    if (user.followerCount < criteria.minFollowers) return false;
    if (user.followerCount > criteria.maxFollowers) return false;

    // Must have posted recently (active creator)
    if (user.lastPostDate) {
      const daysSincePost = (Date.now() - new Date(user.lastPostDate)) / (1000 * 60 * 60 * 24);
      if (daysSincePost > criteria.maxDaysSincePost) return false;
    }

    // Minimum post count (established creator)
    if (user.postCount < criteria.minPosts) return false;

    return true;
  });
}

// Filter to micro-influencers who posted within 14 days
const filtered = filterCandidates(candidates, {
  minFollowers: 10000,
  maxFollowers: 100000,
  maxDaysSincePost: 14,
  minPosts: 50,
});

console.log(`${filtered.length} candidates after filtering`);
// 63 candidates after filtering
Enter fullscreen mode Exit fullscreen mode

Step 4: Enrich with Full Profile Data

The search results give you basics. Now pull detailed profiles.

async function enrichCandidates(candidates) {
  const enriched = [];

  for (const candidate of candidates) {
    try {
      const profile = await getProfile(candidate.platform, candidate.username);
      const posts = await getRecentPosts(candidate.platform, candidate.username, 20);

      // Calculate engagement rate from actual posts
      const avgEngagement = posts.reduce((sum, post) => {
        return sum + ((post.likeCount + post.commentCount) / profile.followerCount) * 100;
      }, 0) / posts.length;

      enriched.push({
        username: candidate.username,
        platform: candidate.platform,
        followers: profile.followerCount,
        following: profile.followingCount,
        posts: profile.postCount,
        bio: profile.biography,
        avgEngagement: parseFloat(avgEngagement.toFixed(2)),
        avgLikes: Math.round(posts.reduce((s, p) => s + p.likeCount, 0) / posts.length),
        avgComments: Math.round(posts.reduce((s, p) => s + p.commentCount, 0) / posts.length),
        recentPostDates: posts.slice(0, 5).map(p => p.createdAt),
        topHashtags: extractTopHashtags(posts),
      });
    } catch (err) {
      console.error(`Failed to enrich ${candidate.username}:`, err.message);
    }
  }

  return enriched;
}

function extractTopHashtags(posts) {
  const hashtagCount = {};

  for (const post of posts) {
    const tags = post.caption?.match(/#\w+/g) || [];
    for (const tag of tags) {
      hashtagCount[tag.toLowerCase()] = (hashtagCount[tag.toLowerCase()] || 0) + 1;
    }
  }

  return Object.entries(hashtagCount)
    .sort((a, b) => b[1] - a[1])
    .slice(0, 10)
    .map(([tag]) => tag);
}
Enter fullscreen mode Exit fullscreen mode

Step 5: Score Niche Relevance

This is where the magic happens. How relevant is each creator to your specific niche?

function scoreNicheRelevance(creator, nicheKeywords) {
  let score = 0;

  // Check bio for keywords (high signal)
  const bioLower = (creator.bio || '').toLowerCase();
  for (const keyword of nicheKeywords) {
    if (bioLower.includes(keyword.toLowerCase())) {
      score += 20;
    }
  }

  // Check hashtags for niche relevance
  for (const tag of creator.topHashtags) {
    for (const keyword of nicheKeywords) {
      if (tag.includes(keyword.toLowerCase())) {
        score += 10;
      }
    }
  }

  // Engagement rate bonus (higher engagement = more relevant audience)
  if (creator.avgEngagement > 5) score += 15;
  else if (creator.avgEngagement > 3) score += 10;
  else if (creator.avgEngagement > 1.5) score += 5;

  // Follower sweet spot bonus (25K-75K is the micro-influencer sweet spot)
  if (creator.followers >= 25000 && creator.followers <= 75000) {
    score += 10;
  }

  return Math.min(100, score);
}

// Score all candidates
const nicheKeywords = ['vegan', 'plant based', 'fitness', 'gym', 'protein', 'workout', 'health'];

const scored = enrichedCandidates.map(creator => ({
  ...creator,
  relevanceScore: scoreNicheRelevance(creator, nicheKeywords),
}));
Enter fullscreen mode Exit fullscreen mode

Step 6: Rank and Output

function generateReport(creators) {
  // Sort by relevance score descending
  const ranked = creators.sort((a, b) => b.relevanceScore - a.relevanceScore);

  console.log('\n=== CREATOR DISCOVERY REPORT ===\n');
  console.log(`Total candidates analyzed: ${creators.length}`);
  console.log(`Top matches:\n`);

  ranked.slice(0, 20).forEach((creator, i) => {
    console.log(`${i + 1}. @${creator.username} (${creator.platform})`);
    console.log(`   Followers: ${creator.followers.toLocaleString()}`);
    console.log(`   Avg Engagement: ${creator.avgEngagement}%`);
    console.log(`   Relevance Score: ${creator.relevanceScore}/100`);
    console.log(`   Top Hashtags: ${creator.topHashtags.slice(0, 5).join(', ')}`);
    console.log('');
  });

  return ranked;
}
Enter fullscreen mode Exit fullscreen mode

Putting It All Together

async function discoverCreators(config) {
  console.log('🔍 Searching across platforms...');
  const candidates = await multiPlatformSearch(config.searchKeywords, config.platforms);
  console.log(`Found ${candidates.length} raw candidates`);

  console.log('📋 Filtering by criteria...');
  const filtered = filterCandidates(candidates, config.filters);
  console.log(`${filtered.length} candidates match criteria`);

  console.log('📊 Enriching profiles...');
  const enriched = await enrichCandidates(filtered);

  console.log('🎯 Scoring relevance...');
  const scored = enriched.map(creator => ({
    ...creator,
    relevanceScore: scoreNicheRelevance(creator, config.nicheKeywords),
  }));

  return generateReport(scored);
}

// Run it
discoverCreators({
  searchKeywords: ['vegan fitness', 'plant based athlete', 'vegan protein', 'vegan gym'],
  platforms: ['tiktok', 'instagram'],
  nicheKeywords: ['vegan', 'plant based', 'fitness', 'gym', 'protein', 'workout'],
  filters: {
    minFollowers: 10000,
    maxFollowers: 100000,
    maxDaysSincePost: 14,
    minPosts: 50,
  },
});
Enter fullscreen mode Exit fullscreen mode

Sample Output

=== CREATOR DISCOVERY REPORT ===

Total candidates analyzed: 63
Top matches:

1. @plantpoweredpaul (tiktok)
   Followers: 47,200
   Avg Engagement: 6.3%
   Relevance Score: 85/100
   Top Hashtags: #vegan, #veganfitness, #plantbased, #gymtok, #protein

2. @veganlifts_maria (instagram)
   Followers: 31,800
   Avg Engagement: 4.1%
   Relevance Score: 75/100
   Top Hashtags: #veganathlete, #plantbasedprotein, #fitnessmotivation

3. @greengains (tiktok)
   Followers: 68,400
   Avg Engagement: 5.7%
   Relevance Score: 70/100
   Top Hashtags: #veganfitness, #gymtok, #plantbasedmuscle
Enter fullscreen mode Exit fullscreen mode

8 hours of manual work → 3 minutes.

Read the Full Guide

This is a condensed version. The full guide covers:

  • Adding estimated cost-per-post calculations
  • Exporting to CSV/Google Sheets
  • Scheduling recurring discovery searches
  • Adding authenticity scoring with the SV-Score

Read the complete guide on SociaVault →


Building influencer marketing tools? SociaVault provides social media data APIs for TikTok, Instagram, YouTube, and 10+ platforms. Search users, fetch profiles, get posts and comments — all through one unified API.

Discussion

How do you find influencers for campaigns right now? Manual search? Platforms? Let me know what works (and what doesn't) 👇

webdev #api #nodejs #influencermarketing #javascript

Top comments (0)