DEV Community

Cover image for Turn Any TikTok Video into a Blog Post (Automated Content Repurposing)
Olamide Olaniyan
Olamide Olaniyan

Posted on

Turn Any TikTok Video into a Blog Post (Automated Content Repurposing)

You spent 2 hours making a TikTok video. It did well. 50K views, solid comments.

Now that same insight is trapped in a 60-second video that the algorithm'll bury in 48 hours.

What if you could turn every TikTok into a blog post automatically? Same message, different format, different audience, permanent SEO value.

That's what we're building.

The Pipeline

TikTok Video URL
  → Extract transcript (SociaVault API)
  → Extract video metadata (title, stats, hashtags)
  → Feed to GPT-4 with a structured prompt
  → Get back a formatted blog post
  → Save as Markdown
Enter fullscreen mode Exit fullscreen mode

One command. Video URL in, blog post out.

The Stack

  • Node.js – runtime
  • SociaVault API – extract TikTok transcripts and video metadata
  • OpenAI API – transform transcript into structured blog content
  • Markdown – output format (works with any CMS)

Setup

mkdir tiktok-to-blog && cd tiktok-to-blog
npm init -y
npm install axios dotenv slugify
Enter fullscreen mode Exit fullscreen mode
# .env
SOCIAVAULT_API_KEY=your_key
OPENAI_API_KEY=your_openai_key
OUTPUT_DIR=./posts
Enter fullscreen mode Exit fullscreen mode

Step 1: Extract the Transcript

TikTok videos have auto-generated captions. SociaVault can pull them:

// extract.js
const axios = require('axios');

const api = axios.create({
  baseURL: 'https://api.sociavault.com/v1/scrape',
  headers: { 'x-api-key': process.env.SOCIAVAULT_API_KEY },
});

async function extractTikTokData(videoUrl) {
  // Get video info (title, stats, hashtags)
  const { data: videoInfo } = await api.get(`/tiktok/video-info?url=${encodeURIComponent(videoUrl)}`);
  const video = videoInfo.data || videoInfo;

  // Get transcript
  let transcript = '';
  try {
    const { data: transcriptData } = await api.get(`/tiktok/transcript?url=${encodeURIComponent(videoUrl)}`);
    const segments = transcriptData.data || transcriptData.segments || [];

    // Combine segments into readable text
    transcript = segments
      .map(s => s.text)
      .join(' ')
      .replace(/\s+/g, ' ')
      .trim();
  } catch (err) {
    console.warn('No transcript available — video may not have captions');
  }

  return {
    title: video.desc || video.title || '',
    transcript,
    author: video.author?.uniqueId || video.author?.nickname || 'unknown',
    likes: video.diggCount || video.likesCount || 0,
    views: video.playCount || video.viewCount || 0,
    comments: video.commentCount || video.commentsCount || 0,
    shares: video.shareCount || 0,
    hashtags: (video.challenges || []).map(c => c.title),
    url: videoUrl,
    coverUrl: video.coverUrl || video.cover || '',
    duration: video.duration || 0,
  };
}

module.exports = { extractTikTokData };
Enter fullscreen mode Exit fullscreen mode

Step 2: The Prompt Engineering

This is where quality comes from. A bad prompt gives you generic AI slop. A good prompt produces something that reads like a person wrote it.

// transform.js
const axios = require('axios');

async function transformToPost(videoData) {
  const prompt = `You are a content writer converting a TikTok video into a blog post. 

Here's the source material:

VIDEO TITLE/CAPTION: ${videoData.title}
TRANSCRIPT: ${videoData.transcript || '(no transcript available — use the title and hashtags to infer the topic)'}
HASHTAGS: ${videoData.hashtags.join(', ') || 'none'}
ENGAGEMENT: ${videoData.views.toLocaleString()} views, ${videoData.likes.toLocaleString()} likes, ${videoData.comments.toLocaleString()} comments

RULES:
1. Write a 600-900 word blog post based on the video content
2. Use a conversational, direct tone — like you're explaining to a friend
3. Start with a hook that grabs attention (not "In this article..." or "Today we're going to...")
4. Break it into clear sections with H2 headers
5. Add practical takeaways or action items
6. Include specific numbers or examples where the transcript mentions them
7. End with a brief conclusion or call-to-action
8. Do NOT mention that this was originally a TikTok video
9. Do NOT start with "Have you ever..." 
10. Output as Markdown

FORMAT:
---
title: "[SEO-friendly title, 50-60 characters]"
description: "[Meta description, 150-160 characters]"
tags: [3-5 relevant tags as comma-separated list]
---

[Blog post content in Markdown]`;

  const { data } = await axios.post(
    'https://api.openai.com/v1/chat/completions',
    {
      model: 'gpt-4o',
      messages: [
        { role: 'system', content: 'You are an expert content writer who turns video transcripts into engaging blog posts.' },
        { role: 'user', content: prompt },
      ],
      max_tokens: 2000,
      temperature: 0.7,
    },
    { headers: { Authorization: `Bearer ${process.env.OPENAI_API_KEY}` } }
  );

  return data.choices[0].message.content;
}

module.exports = { transformToPost };
Enter fullscreen mode Exit fullscreen mode

Step 3: Save as Markdown

// save.js
const fs = require('fs');
const path = require('path');
const slugify = require('slugify');

function savePost(markdownContent, videoData, outputDir) {
  // Extract title from frontmatter
  const titleMatch = markdownContent.match(/title:\s*(.+)/);
  const title = titleMatch ? titleMatch[1].trim().replace(/['"]/g, '') : 'untitled';

  const slug = slugify(title, { lower: true, strict: true });
  const filename = `${slug}.md`;
  const filepath = path.join(outputDir, filename);

  // Append source attribution at the bottom
  const attribution = `\n\n---\n\n*Originally inspired by content from [@${videoData.author}](${videoData.url}). ${videoData.views.toLocaleString()} views on TikTok.*\n`;

  const fullContent = markdownContent + attribution;

  // Create output directory if it doesn't exist
  fs.mkdirSync(outputDir, { recursive: true });
  fs.writeFileSync(filepath, fullContent, 'utf-8');

  return { filepath, slug, title };
}

module.exports = { savePost };
Enter fullscreen mode Exit fullscreen mode

Step 4: Wire It All Together

// index.js
require('dotenv').config();
const { extractTikTokData } = require('./extract');
const { transformToPost } = require('./transform');
const { savePost } = require('./save');

async function convert(videoUrl) {
  console.log('Extracting video data...');
  const videoData = await extractTikTokData(videoUrl);

  if (!videoData.transcript && !videoData.title) {
    console.error('No transcript or title found. Cannot convert.');
    process.exit(1);
  }

  console.log(`Video: "${videoData.title.slice(0, 60)}..."`);
  console.log(`Transcript: ${videoData.transcript ? videoData.transcript.length + ' chars' : 'none (using title + hashtags)'}`);
  console.log(`Stats: ${videoData.views.toLocaleString()} views, ${videoData.likes.toLocaleString()} likes`);
  console.log('\nGenerating blog post...');

  const markdown = await transformToPost(videoData);

  const outputDir = process.env.OUTPUT_DIR || './posts';
  const result = savePost(markdown, videoData, outputDir);

  console.log(`\n✅ Blog post saved to: ${result.filepath}`);
  console.log(`   Title: ${result.title}`);
  console.log(`   Slug: ${result.slug}`);
}

const url = process.argv[2];
if (!url) {
  console.error('Usage: node index.js <tiktok-video-url>');
  process.exit(1);
}

convert(url);
Enter fullscreen mode Exit fullscreen mode

Usage

node index.js "https://www.tiktok.com/@creator/video/7234567890"
Enter fullscreen mode Exit fullscreen mode

Output:

Extracting video data...
Video: "3 things I wish I knew before starting a SaaS..."
Transcript: 847 chars
Stats: 52,000 views, 4,200 likes

Generating blog post...

✅ Blog post saved to: ./posts/3-things-i-wish-i-knew-before-starting-a-saas.md
   Title: 3 Things I Wish I Knew Before Starting a SaaS
   Slug: 3-things-i-wish-i-knew-before-starting-a-saas
Enter fullscreen mode Exit fullscreen mode

Batch Mode

Got a whole channel of TikToks? Convert them all:

// batch.js
require('dotenv').config();
const axios = require('axios');
const { extractTikTokData } = require('./extract');
const { transformToPost } = require('./transform');
const { savePost } = require('./save');

const api = axios.create({
  baseURL: 'https://api.sociavault.com/v1/scrape',
  headers: { 'x-api-key': process.env.SOCIAVAULT_API_KEY },
});

async function batchConvert(username, count = 10) {
  // Fetch recent videos
  const { data } = await api.get(`/tiktok/profile-videos?username=${username}&limit=${count}`);
  const videos = data.data || data.posts || [];

  console.log(`Found ${videos.length} videos from @${username}\n`);

  for (let i = 0; i < videos.length; i++) {
    const video = videos[i];
    const url = video.webVideoUrl || `https://www.tiktok.com/@${username}/video/${video.id}`;

    console.log(`[${i + 1}/${videos.length}] Converting: ${(video.desc || '').slice(0, 50)}...`);

    try {
      const videoData = await extractTikTokData(url);
      const markdown = await transformToPost(videoData);
      const result = savePost(markdown, videoData, process.env.OUTPUT_DIR || './posts');
      console.log(`  ✅ Saved: ${result.filepath}`);
    } catch (err) {
      console.error(`  ❌ Failed: ${err.message}`);
    }

    // Rate limit: one conversion every 5 seconds
    await new Promise(r => setTimeout(r, 5000));
  }

  console.log('\nBatch conversion complete.');
}

const username = process.argv[2];
const count = parseInt(process.argv[3]) || 10;

if (!username) {
  console.error('Usage: node batch.js <username> [count]');
  process.exit(1);
}

batchConvert(username, count);
Enter fullscreen mode Exit fullscreen mode
# Convert last 20 TikToks from a creator
node batch.js creator_username 20
Enter fullscreen mode Exit fullscreen mode

That gives you 20 blog posts in about 2 minutes. Each one is unique, SEO-ready, and based on content that already proved it resonates (the video performed well for a reason).

Quality Control

The output isn't publish-ready — it's a solid first draft. You should:

  1. Read it once — fix any weird AI phrasing (5 minutes per post)
  2. Add internal links — to your other content
  3. Add images — screenshot from the video or relevant stock photos
  4. Check the title — make sure it's click-worthy and SEO-friendly

A 60-second TikTok becomes a 700-word blog post in under a minute of compute time and 5 minutes of editing. That's 10x more efficient than writing from scratch.

The ROI

One TikTok video = 48 hours of algorithmic reach.

One blog post = years of organic search traffic.

Same insight, two formats, completely different lifespan.

Read the Full Guide

TikTok to Blog Post Automation → SociaVault Blog


Extract transcripts, metadata, and analytics from TikTok videos with SociaVault — one API for TikTok, Instagram, YouTube, and more. No login required.

Discussion

Are you repurposing your video content into other formats? What's your workflow? I find that the video → text pipeline is the most underutilized content strategy.

javascript #contentcreation #automation #webdev #ai

Top comments (0)