PowerShell is the most underrated tool in the TikTok scraping conversation. While Python and Node tutorials dominate, every Windows server already ships with PowerShell, every sysadmin already knows it, and Invoke-RestMethod deserializes JSON into objects you can pipe directly into Export-Csv, Export-Excel, or SQL Server. No virtual environments. No pip install. No Node version drama. Just a script, a scheduled task, and reliable data flowing into your warehouse every morning.
This tutorial shows how to pull TikTok user profiles, follower counts, video lists, and follower graphs using PowerShell 7 and the TikLiveAPI REST API. We will cover authentication via Windows Credential Manager, idiomatic PSCustomObject handling, pagination, parallel fetches with ForEach-Object -Parallel, retry logic, CSV export, and a production-ready Task Scheduler deployment.
PowerShell is built for exactly this kind of work: hit a JSON HTTP endpoint, transform the response, and write it somewhere useful. Three reasons it beats spinning up a Python project on a Windows box:
Invoke-RestMethod auto-converts responses to PSCustomObject. You access $response.stats.followerCount directly - no manual deserialization.ForEach-Object -Parallel and HTTP/2). Stock Windows PowerShell 5.1 also works but lacks parallel cmdlets.Never hard-code the API key in a script. PowerShell has two clean options on Windows: the SecretManagement module backed by Windows Credential Manager, or DPAPI-encrypted files via ConvertFrom-SecureString.
The Credential Manager approach is best for interactive servers:
Install-Module Microsoft.PowerShell.SecretManagement -Scope CurrentUser
Install-Module Microsoft.PowerShell.SecretStore -Scope CurrentUser
Register-SecretVault -Name TikLive -ModuleName Microsoft.PowerShell.SecretStore -DefaultVault
Set-Secret -Name TikLiveApiKey -Secret 'YOUR_API_KEY_HERE'
For headless service accounts, DPAPI is simpler. It encrypts the key against the current user SID so only that account can read it:
Read-Host 'Paste API key' -AsSecureString |
ConvertFrom-SecureString |
Set-Content -Path "$env:USERPROFILE\.tiklive.key"
Then in your script:
$secure = Get-Content "$env:USERPROFILE\.tiklive.key" | ConvertTo-SecureString
$ApiKey = [System.Net.NetworkCredential]::new('', $secure).Password
TikLiveAPI authenticates with a single HTTP header: X-Api-Key. Every request goes to https://api.tikliveapi.com. Let's fetch a profile:
$BaseUrl = 'https://api.tikliveapi.com'
$Headers = @{ 'X-Api-Key' = $ApiKey }
$response = Invoke-RestMethod -Uri "$BaseUrl/userinfo-by-username/?username=charlidamelio" `
-Headers $Headers -Method Get
$response.user.nickname
$response.stats.followerCount
$response.stats.heartCount
$response.stats.videoCount
That's it. The response is a PSCustomObject with two top-level properties: user (nested object with id, uniqueId, nickname, avatarThumb, avatarMedium, avatarLarger, signature, verified, secUid, privateAccount, bioLink) and stats (camelCase counters: followingCount, followerCount, heartCount, videoCount, diggCount). See the full schema in the Users documentation.
Many endpoints take a numeric userid rather than a username. Use /userid/ to convert:
function Get-TikTokUserId {
param([string]$Username)
$result = Invoke-RestMethod -Uri "$BaseUrl/userid/?username=$Username" -Headers $Headers
return $result.id
}
$userId = Get-TikTokUserId -Username 'charlidamelio'
Write-Host "Numeric ID: $userId"
The response is a flat object with a single string field id. Cache these locally - they never change for a given account.
TikLiveAPI uses two pagination styles. /user-posts/ uses a cursor (millisecond timestamp string) plus a hasMore boolean. Follower and following endpoints use a time field (unix seconds) instead.
Here is a clean PowerShell loop that handles cursor-based pagination for video lists:
function Get-TikTokUserPosts {
param(
[string]$UserId,
[int]$MaxVideos = 200
)
$allVideos = [System.Collections.Generic.List[object]]::new()
$cursor = '0'
do {
$url = "$BaseUrl/user-posts/?userid=$UserId&count=35&cursor=$cursor"
$page = Invoke-RestMethod -Uri $url -Headers $Headers
foreach ($video in $page.videos) {
$allVideos.Add($video)
}
$cursor = $page.cursor
$hasMore = $page.hasMore
} while ($hasMore -and $allVideos.Count -lt $MaxVideos)
return $allVideos
}
Each video in videos[] uses snake_case fields: aweme_id, video_id, title, play, wmplay, play_count, digg_count, comment_count, share_count, create_time, and a nested author object.
For followers, pagination uses time instead of cursor, and the top-level key is followers:
function Get-TikTokFollowers {
param([string]$UserId, [int]$MaxFollowers = 1000)
$all = [System.Collections.Generic.List[object]]::new()
$time = 0
do {
$url = "$BaseUrl/user-followers/?userid=$UserId&count=50&time=$time"
$page = Invoke-RestMethod -Uri $url -Headers $Headers
$page.followers | ForEach-Object { $all.Add($_) }
$time = $page.time
} while ($page.hasMore -and $all.Count -lt $MaxFollowers)
return $all
}
Note: the /user-following/ endpoint uses the plural key followings (not following) - a common gotcha worth pinning to your monitor.
Networks fail. TikTok occasionally returns 5xx during regional CDN hiccups. Wrap calls in a retry helper:
function Invoke-WithRetry {
param(
[scriptblock]$ScriptBlock,
[int]$MaxAttempts = 4,
[int]$BaseDelaySeconds = 2
)
for ($attempt = 1; $attempt -le $MaxAttempts; $attempt++) {
try {
return & $ScriptBlock
} catch {
$status = $_.Exception.Response.StatusCode.value__
if ($attempt -eq $MaxAttempts -or ($status -ge 400 -and $status -lt 500 -and $status -ne 429)) {
throw
}
$delay = [Math]::Pow(2, $attempt - 1) * $BaseDelaySeconds
Write-Warning "Attempt $attempt failed (HTTP $status). Retrying in ${delay}s."
Start-Sleep -Seconds $delay
}
}
}
$profile = Invoke-WithRetry { Invoke-RestMethod -Uri "$BaseUrl/userinfo-by-username/?username=mrbeast" -Headers $Headers }
This retries transient 5xx and 429 (rate limit) responses with exponential backoff, but fails fast on 4xx like 401 (bad key) or 404. The default rate limit is 200 requests per minute - more than enough for most batch jobs.
PowerShell 7 ships ForEach-Object -Parallel, which is perfect for fanning out across hundreds of usernames. Each iteration runs in its own runspace, so you must pass shared variables via $using::
$usernames = Get-Content .\target-creators.txt
$apiKey = $ApiKey
$baseUrl = $BaseUrl
$results = $usernames | ForEach-Object -Parallel {
$headers = @{ 'X-Api-Key' = $using:apiKey }
try {
$r = Invoke-RestMethod -Uri "$using:baseUrl/userinfo-by-username/?username=$_" -Headers $headers
[PSCustomObject]@{
Username = $r.user.uniqueId
Nickname = $r.user.nickname
Followers = $r.stats.followerCount
Hearts = $r.stats.heartCount
Videos = $r.stats.videoCount
Verified = $r.user.verified
FetchedAt = (Get-Date).ToString('s')
}
} catch {
Write-Warning "Failed: $_"
}
} -ThrottleLimit 8
$results | Export-Csv -Path .\tiktok-creators.csv -NoTypeInformation -Encoding UTF8
On PowerShell 5.1, fall back to Start-ThreadJob from the ThreadJob module. Keep ThrottleLimit at or below 8 to stay under the 200 req/min limit while leaving headroom for retries.
Once data is a PSCustomObject array, output is trivial:
$results | Export-Csv -Path .\creators.csv -NoTypeInformation -Encoding UTF8
# Excel (requires ImportExcel module)
$results | Export-Excel -Path .\creators.xlsx -AutoSize -TableName Creators
# SQL Server (requires SqlServer module)
$results | Write-SqlTableData -ServerInstance 'sql01' -DatabaseName 'Analytics' -TableName 'tiktok_creators' -Force
To run your scraper every morning at 06:00, save the script as C:\Scripts\TikTokDaily.ps1 and register a task:
$action = New-ScheduledTaskAction -Execute 'pwsh.exe' `
-Argument '-NoProfile -File C:\Scripts\TikTokDaily.ps1'
$trigger = New-ScheduledTaskTrigger -Daily -At 06:00
$principal = New-ScheduledTaskPrincipal -UserId 'NT AUTHORITY\SYSTEM' `
-LogonType ServiceAccount -RunLevel Highest
$settings = New-ScheduledTaskSettingsSet -StartWhenAvailable `
-ExecutionTimeLimit (New-TimeSpan -Hours 2) `
-RestartCount 3 -RestartInterval (New-TimeSpan -Minutes 5)
Register-ScheduledTask -TaskName 'TikLiveAPI Daily Pull' `
-Action $action -Trigger $trigger -Principal $principal -Settings $settings
The task runs as SYSTEM, restarts on failure, and times out after two hours.
For production jobs, log to the Windows Event Log so Splunk or Azure Monitor can pick it up:
New-EventLog -LogName Application -Source 'TikLiveAPI' -ErrorAction SilentlyContinue
try {
$count = (Get-TikTokFollowers -UserId $userId -MaxFollowers 5000).Count
Write-EventLog -LogName Application -Source 'TikLiveAPI' `
-EntryType Information -EventId 1000 `
-Message "Pulled $count followers for $userId"
} catch {
Write-EventLog -LogName Application -Source 'TikLiveAPI' `
-EntryType Error -EventId 9000 -Message $_.Exception.Message
throw
}
The full documentation lists 37 endpoints. A few common picks for sysadmin pipelines:
/post-detail/ - flat snake_case object with aweme_id, play, wmplay, hdplay (HD no-watermark download URLs), plus full counter set./post-comments/ - returns comments[] with each item carrying id (not cid), text, digg_count, reply_total, paginated by cursor + hasMore./search-video/ - keyword video search with publish_time and sort_by filters./download-video/ - direct watermark-free download URL given a TikTok video URL.Test any endpoint live in the browser-based playground before wiring it into PowerShell.
Yes. Invoke-RestMethod, retry, and CSV export are identical. You lose ForEach-Object -Parallel - use Start-ThreadJob from the ThreadJob module instead.
The standard limit is 200 requests per minute. The Invoke-WithRetry helper above already retries 429 with exponential backoff. For higher limits, contact support.
Yes. PowerShell 7 is cross-platform. Swap Register-ScheduledTask for a cron entry and replace SecretStore with libsecret or environment variables.
Wrap query parameter values in [uri]::EscapeDataString($value) to handle dots, hyphens, and non-ASCII characters safely.
No. Credit deduction happens on the external API server only when a request returns successfully. Failed lookups (404, 5xx) do not consume credits.
Ready to deploy? Grab your key from profile, browse more patterns on the blog, and check the Users endpoint reference for the full PSCustomObject shapes you will be working with.
Ready to put what you read into code? Try our endpoints live or grab the full reference.