A self-hosted Threads analytics dashboard. Connect your access token and explore post performance with detailed charts and metrics.
- Quick Start
- Features
- Requirements
- Development
- Getting Your Threads Access Token
- Analytics Reference
- Deployment
git clone https://github.com/ridemountainpig/threads-analytics.git
cd threads-analytics
pnpm install
cp .env.example .env.local # or create .env.local manually
npx prisma migrate dev --name init
pnpm devOpen http://localhost:3000 and sign in with APP_PASSWORD.
- Overview — stat cards (views, likes, replies, reposts, quotes, shares, engagement rate) with period-over-period delta, daily views chart, best posting hour recommendation, viral posts
- Analytics — 15+ charts across Performance and Content tabs
- Posts — searchable, filterable list with per-post analytics panel
- Multi-account support with account switching
- Auto-sync on configurable intervals
- Password-protected (single
APP_PASSWORDenv var) - English / 繁體中文 / 日本語 UI
- Node.js 20.9+
- pnpm
- PostgreSQL database
git clone https://github.com/ridemountainpig/threads-analytics.git
cd threads-analytics
pnpm installCreate a .env.local file in the project root:
APP_PASSWORD=your_dashboard_password
DATABASE_URL=postgresql://... # PostgreSQL connection string
TOKEN_ENCRYPTION_KEY= # openssl rand -hex 32
CRON_SECRET= # optional, secures /api/cron/sync in production
SYNC_SCHEDULER_ENABLED=false # set true only on long-running deployments| Variable | Description | How to generate |
|---|---|---|
APP_PASSWORD |
Password to access the dashboard | Choose any string |
DATABASE_URL |
PostgreSQL connection string | From your DB provider |
TOKEN_ENCRYPTION_KEY |
Encrypts stored Threads access tokens at rest | openssl rand -hex 32 |
CRON_SECRET |
Secures /api/cron/sync in production |
Random 16+ chars |
SYNC_SCHEDULER_ENABLED |
Enables the built-in polling scheduler | true for Docker/VPS |
npx prisma migrate dev --name initpnpm devOpen http://localhost:3000 and sign in with your APP_PASSWORD.
- Go to Settings in the sidebar
- Click Add Threads account
- Paste your long-lived Threads access token (see Getting Your Access Token)
- The first sync starts automatically and may take a few minutes
pnpm dev # Start development server
pnpm build # Build for production
pnpm start # Run migrations and start production server
npx prisma studio # Open database GUI
npx prisma migrate dev --name <name> # Create a new migration- Go to developers.facebook.com and create an app with the Access the Threads API use case
- Generate an Access Token
- In the dashboard: Settings → Add Threads Account → paste token
For a screenshot-based walkthrough, see How to Generate a Threads Access Token.
Tokens are valid for 60 days. The dashboard shows an expiry warning when your token is within 30 days of expiring. Renew anytime with the Update token button on the account card in Settings — your synced data is kept.
| Section | What it shows |
|---|---|
| Stat Cards | Total views, likes, replies, reposts, quotes, shares, and engagement rate for the selected period. Each card shows a +/−% delta vs the previous period of the same length. |
| Best Hours | Top 2–3 posting hours ranked by median views, with a confidence indicator based on sample size. |
| Daily Views | Daily view counts with a 7-day rolling average and your personal median as a baseline. |
| Top Posts | Posts that exceeded the median view count, ranked by their multiplier (e.g. 3.2× median). |
| Chart | What it shows |
|---|---|
| Overall Performance | Combined daily views, post count, and average views per post on one timeline. |
| Post Quality Map | Scatter plot of every post by reach (views) vs. engagement rate. Dot size = shares. Four quadrants: Breakout, Conversation, Broadcast, Underperforming. |
| Views to Actions Funnel | Conversion rate from total views into each action type (likes, replies, reposts, quotes, shares). |
| Best Time to Post | Heatmap of median views by hour of day. Tooltip shows sample count and confidence level. |
| Engagement Rate Trend | Daily engagement rate (interactions ÷ views) with a 7-day smoothed average. |
| Best Day of Week | Median views, engagement rate, and post count by weekday. |
| Format × Length Matrix | 2-D heatmap comparing every combination of content format and post length against your median reach. |
| Engagement Type Breakdown | Pie chart of the proportion of likes, replies, reposts, quotes, and shares. |
| Engagement Breakdown | Stacked daily chart of likes, replies, reposts, and quotes over time. |
Stat metrics at the top of the tab: Posting Consistency (% of weeks with at least one post), Share Rate, Quote Ratio (quotes ÷ (quotes + reposts)), Total Posts, Longest Streak, and Current Streak.
| Chart | What it shows |
|---|---|
| Posting Activity | Calendar heatmap showing how many posts you published each day. |
| Content Type Performance | Median views, engagement rate, and share rate by media type (Text, Image, Video, Carousel, Audio). Low-sample buckets are faded. |
| Post Length Analysis | Median views broken down by character-count bucket. Tooltip includes average, P75, hit rate, and confidence. |
| Publishing Frequency vs Performance | Whether posting more in a given week raises or lowers per-post average views. |
| Shares Trend | Daily share counts over time. |
| Top Keywords by Engagement | Words (excluding hashtags) with the highest average engagement rate (minimum 3 posts). |
| Optimal Posting Frequency | Per-post reach and engagement compared across different weekly posting volumes. |
| Content Type by Time Slot | Best posting hour for each content format based on median views. |
| Top by Engagement Rate | Highest-engagement posts ranked by (likes + replies + reposts + quotes) ÷ views. |
| Reply-Rate Leaders | Posts ranked by replies ÷ views — your best conversation starters. |
The posts list supports:
- Sort by date, views, or likes
- Search full-text across post content
- Filter by media type (shows only types present in the current period)
Clicking any post opens a detail panel with views, engagement rate, vs-median multiplier, view percentile, and a per-action engagement breakdown.
The fastest way to get a hosted instance — both templates provision a PostgreSQL database and set the required environment variables for you:
| Platform | Deploy |
|---|---|
| Railway | |
| Zeabur |
Set SYNC_SCHEDULER_ENABLED=true in your environment variables. The built-in scheduler starts with the server and syncs at the interval configured in Settings.
Vercel does not support long-running processes. Use Vercel Cron Jobs to call /api/cron/sync on a schedule instead.
-
Add a
vercel.jsonto the project root:{ "crons": [ { "path": "/api/cron/sync", "schedule": "0 * * * *" } ] }Adjust
scheduleto match the sync interval you set in Settings (e.g.0 * * * *for every hour,*/30 * * * *for every 30 minutes). Note that Vercel's free plan limits cron frequency. -
In the Vercel dashboard go to Settings → Environment Variables and add:
Variable Value CRON_SECRETA random secret — generate with openssl rand -hex 32Vercel automatically injects this value as
Authorization: Bearer <CRON_SECRET>on every cron request, and the/api/cron/syncroute uses it to verify the call is legitimate.
A prebuilt multi-arch (amd64/arm64) image is published to GitHub Container Registry. Set all environment variables, then run:
docker run -p 3000:3000 --env-file .env.local ghcr.io/ridemountainpig/threads-analytics:latestOr build the image from source yourself:
docker build -t threads-analytics .
docker run -p 3000:3000 --env-file .env.local threads-analyticsThe Docker image runs prisma migrate deploy automatically on startup.
New versions ship as updated Docker images. Database migrations run automatically on startup, so updating only requires getting the new image or code:
-
Docker / VPS — pull the latest image, then stop the old container and start a new one with the same flags:
docker pull ghcr.io/ridemountainpig/threads-analytics:latest
-
Zeabur — open the
threads-analyticsservice and click Redeploy to pull the latest image. -
Railway — trigger a redeploy of the service from the Railway dashboard.
-
From source —
git pull, thenpnpm install && pnpm buildand restart withpnpm start(runs migrations automatically).
Your database (posts, insights, accounts) is preserved across updates.
