Back to Blog
TikTok

Best Time to Post on TikTok: Find Your Audience's Real Peak Hours

December 8, 2025
6 min read
S
By SociaVault Team
TikTokAnalyticsPosting StrategyEngagement

Best Time to Post on TikTok: Find Your Audience's Real Peak Hours

Search "best time to post on TikTok" and you'll get a confident chart: Tuesday 9am, Thursday 7pm, and so on. The problem is that chart is an average of everybody — gamers, grandparents, K-pop stans, finance nerds — blended into one meaningless number. If your audience is night-owl gamers, your peak might be 1am. If they're parents, 9am is exactly when nobody's looking. The only chart that matters is the one built from a specific account's own data.

This guide builds a peak-hour analyzer: pull an account's recent videos, read each one's upload time and view count, and find which hours actually correlate with strong performance. Code in JavaScript and Python.

How it works (and an honest caveat)

The logic is simple: each video carries the time it was posted and how many views it got. Group videos by hour-of-day, average the views in each bucket, and the top buckets are your candidate posting windows.

The honest caveat: this is correlation, not a controlled experiment. A video's views depend on far more than post time — the hook, the sound, luck with the algorithm. And TikTok's create_time is a UTC timestamp, so you have to convert it to the audience's timezone before the hours mean anything. Treat the output as a strong hint to test, not gospel. With a decent sample (50+ videos) the pattern is usually real.

Step 1: Pull the videos

Use the paginated videos endpoint to grab a solid sample in one call.

const API_KEY = process.env.SOCIAVAULT_API_KEY;
const BASE = "https://api.sociavault.com/v1";

async function getVideos(handle, amount = 50) {
  const res = await fetch(
    `${BASE}/scrape/tiktok/videos-paginated?handle=${handle}&amount=${amount}`,
    { headers: { "x-api-key": API_KEY } },
  );
  const json = await res.json();
  if (!json.success) throw new Error(json.error);
  return json.data.aweme_list || [];
}

Each item in aweme_list has a create_time (Unix seconds, UTC) and a statistics object with play_count.

Step 2: Analyze by hour

function analyzePeakHours(videos, tzOffsetHours = 0) {
  const buckets = {}; // hour -> { views, count }

  for (const v of videos) {
    const stats = v.statistics || {};
    const views = stats.play_count || 0;
    // create_time is UTC seconds; shift to the audience timezone
    const date = new Date((v.create_time + tzOffsetHours * 3600) * 1000);
    const hour = date.getUTCHours();

    buckets[hour] ||= { views: 0, count: 0 };
    buckets[hour].views += views;
    buckets[hour].count += 1;
  }

  return Object.entries(buckets)
    .map(([hour, b]) => ({
      hour: +hour,
      avgViews: Math.round(b.views / b.count),
      posts: b.count,
    }))
    .sort((a, b) => b.avgViews - a.avgViews);
}

The tzOffsetHours argument is the honest bit — set it to your audience's offset from UTC (e.g. -5 for US Eastern) so the hours map to when real people are scrolling, not to UTC.

Step 3: Print the windows

async function findBestTimes(handle, tzOffset = -5) {
  const videos = await getVideos(handle);
  if (!videos.length) return console.log("No videos found.");

  const ranked = analyzePeakHours(videos, tzOffset);
  console.log(
    `\nšŸ•’ Best posting hours for @${handle} (UTC${tzOffset >= 0 ? "+" : ""}${tzOffset})`,
  );
  ranked.slice(0, 3).forEach((slot, i) => {
    const label = new Date(0, 0, 0, slot.hour).toLocaleTimeString("en-US", {
      hour: "numeric",
      hour12: true,
    });
    console.log(
      `#${i + 1}  ${label} — ${slot.avgViews.toLocaleString()} avg views (${slot.posts} posts)`,
    );
  });
}

findBestTimes("khaby.lame", -5);

The same in Python

import os, requests
from collections import defaultdict
from datetime import datetime, timezone, timedelta

API_KEY = os.environ["SOCIAVAULT_API_KEY"]
BASE = "https://api.sociavault.com/v1"

def best_hours(handle, tz_offset=-5, amount=50):
    r = requests.get(f"{BASE}/scrape/tiktok/videos-paginated",
                     params={"handle": handle, "amount": amount},
                     headers={"x-api-key": API_KEY}).json()
    videos = r.get("data", {}).get("aweme_list", [])
    buckets = defaultdict(lambda: {"views": 0, "count": 0})
    tz = timezone(timedelta(hours=tz_offset))
    for v in videos:
        views = (v.get("statistics") or {}).get("play_count", 0)
        hour = datetime.fromtimestamp(v["create_time"], tz).hour
        buckets[hour]["views"] += views
        buckets[hour]["count"] += 1
    ranked = sorted(
        ({"hour": h, "avg": b["views"] // max(b["count"], 1)} for h, b in buckets.items()),
        key=lambda x: -x["avg"])
    return ranked[:3]

print(best_hours("khaby.lame"))

Using it well

Three things make this genuinely useful rather than just a neat script:

  1. Analyze your own account for your real posting schedule — your audience is what matters. (You can run it on competitors too, to see when their audience is active.)
  2. Mind the first two hours. TikTok's algorithm leans hard on early velocity. Posting when your audience is awake gives that initial window real fuel; posting at 4am wastes it.
  3. Treat it as a hypothesis. The data points you at a window — then actually test posting there for a few weeks and confirm it holds.

Frequently Asked Questions

What's the best time to post on TikTok in 2025?

There's no universal answer — it depends entirely on when your audience is active, which is why generic charts underperform. The reliable method is to analyze an account's own videos: group them by the hour they were posted and see which hours correlate with the most views, as the code here does.

Why are generic "best time to post" charts unreliable?

They average the behavior of millions of unrelated users across every niche and timezone. Your audience is a specific slice of that, often with very different active hours. An average optimized for "everyone" is optimized for no one in particular.

Do TikTok timestamps need timezone conversion?

Yes. The create_time field is a UTC Unix timestamp. To find when your audience is actually scrolling, convert it to their local timezone first — otherwise your "peak hour" is just a peak in UTC, which may be the middle of the night for your viewers.

How many videos should I analyze?

Aim for at least 50. Fewer than that and one viral outlier skews every hour bucket. The paginated endpoint can pull a large sample in one call, so it's cheap to get a statistically useful set.

Can I find the best time to post for a competitor's audience?

Yes — run the same analysis on a competitor's public videos to see when their audience engages most. If you share a target audience, that's a useful cross-check on your own findings.

Does posting at the "best" time guarantee more views?

No. Post time influences the crucial early-velocity window, but content quality, hook, and sound matter more. The best time gives a good video its best shot; it won't rescue a weak one. Use it as one lever among several.

The bottom line

Stop posting on a stranger's schedule. Pull an account's own videos, convert the timestamps to your audience's timezone, and let the data show you the hours that actually perform — then test them. It's a few minutes of code for a real edge on early velocity.

Want to find your peak hours? Start free with SociaVault with 50 credits.


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.