This is a walkthrough for making your own private analytics dashboard, the kind that pulls your TikTok and Instagram into one page you check every week. It also builds the two documents brands ask for, a rate card and a media kit, straight from the same numbers, so they can never go stale.
It runs on your own computer. There is no website to sign up for, no monthly fee, and nothing about your accounts gets uploaded anywhere. You build it once, connect it to your accounts, and then it updates itself.
You do not need to know how to code. You will hand the work to Claude, and you will spend most of your time clicking through two setup websites. The dashboard will look like your brand, not mine, because the first thing you do is tell Claude what your brand looks like.
What you end up with
The dashboard, one page you open every week:
- Your total audience across TikTok and Instagram, and how it moved since last week
- A row of stats for the last 7 days: net new followers, engagement, reach, posts
- A growth chart that adds one point per week
- A card for each platform with followers, a trend line, engagement rate, average views per reel and engagement per post
- Your Instagram audience broken down by top locations, age and gender
- Your best-performing posts, ranked, building up into an all-time leaderboard
- A calendar showing how often you post, and your current streak
The rate card, a one-page priced document you save as a PDF and email to a brand that has asked what things cost.
The public media kit, a page for your link in bio: your bio, your numbers, the brands you have worked with, a grid of your best videos, and your packages without prices. It is published from the dashboard and hosted for free.
What you need
- A Mac (if you use Windows, tell Claude at the start and it will adjust the steps)
- Claude Code, the tool that builds all of this for you. Get it at claude.com/claude-code and follow the install steps there.
- Node.js, a small free tool the sync uses. Install it from nodejs.org, or ask Claude Code to help you get it.
- Your TikTok and Instagram logins
- Google Chrome or Microsoft Edge for the media kit step (Safari cannot write files to a folder)
- About two hours, best split over two sittings. Sitting one is the dashboard and the two developer sites. Sitting two is the rate card and media kit.
Step 1: Write your brand brief
Everything Claude builds will be styled from a short description of your brand, so write that first. Fill in this template, five minutes, and keep it, because you will paste it into every prompt.
MY BRAND
Name: [your name]
Handle: [@yourhandle, same on both platforms or list both]
Tagline: [one line, e.g. "helping you get ahead in your 20s: career, AI, and tech"]
Niche: [e.g. tech and AI, fitness, food]
Location: [city, country]
Look and feel: [pick a motif and describe it in a sentence or two.
Examples: "a morning newspaper, cream paper, serif headlines, thin rules";
"a clean dark terminal, monospace type, one neon accent";
"a pastel scrapbook, rounded cards, handwritten labels"]
Colours: [2 to 4 hex codes if you have them, or plain words:
"cream background, dark ink, one deep blue accent, a little gold"]
Fonts: [a display font for headlines and a text font for everything else,
from Google Fonts. If unsure, say "pick two that fit the motif"]
Name of the dashboard: [something in your voice, e.g. "The Daily [Name]"
for a newspaper, "[Name] HQ", "Mission Control"]
Do not overthink it. You can restyle later in one prompt (there is a section on that at the end). What matters is that Claude has something to design from, otherwise you get a generic grey dashboard.
Step 2: Build the dashboard
Make an empty folder on your computer for the project. Your Desktop is a fine spot. Open Claude Code inside that folder.
Paste in the prompt below. Before you send it, put your brand brief at the top and replace the two account links with your own.
If your Claude Code shows a
/goalcommand, you can type/goalfollowed by the prompt instead. It makes Claude keep working until the whole thing is done rather than stopping halfway to check in. If you do not see the command, just paste the prompt as a normal message. Both work.
[paste your brand brief here]
Build me a personal social media analytics dashboard, following the idea
from this TikTok video:
https://www.tiktok.com/@tess.barclay/video/7621583403368041748
It runs on my own Mac. It brings my TikTok and Instagram into one page
that I check once a week, styled exactly to the brand brief above. Keep it
fully private: a plain HTML, CSS and JavaScript page with no login and no
online server, all data saved in my own browser's localStorage.
My accounts:
TikTok: https://www.tiktok.com/@yourhandle
Instagram: https://www.instagram.com/yourhandle
STYLE
- Put the palette and the fonts in one file, brand.css, as CSS variables.
Nothing else should define colours or typefaces. Later I will restyle
by editing that one file.
- Put my name, handles, tagline and the dashboard's name in one small
data file so I can change them without touching the code.
THE DASHBOARD SHOULD INCLUDE
- A headline with my total audience across both platforms and the change
since last week, with an auto-written one-line summary
- A row of stats for the last 7 days: net new followers, engagement, reach,
and posts
- A growth chart of both platforms over time that adds one point each week
as I file new numbers (hover for the weekly breakdown)
- A card for each platform with followers, a small trend line, 7-day
change, engagement rate, average views per reel, average engagement per
post (both as 30-day rolling averages), reach, and a link to the profile
- An "audience" section that shows my Instagram followers broken down by
top locations, age range and gender. If Instagram has no demographic
data yet (it needs 100 or more followers), hide the section cleanly
instead of showing empty charts
- A "best posts" section that ranks my strongest posts by views and
engagement, and accumulates into an all-time leaderboard across syncs
- A posting calendar heatmap for the last 10 weeks, plus my current
posting streak
- A weekly "file this week's numbers" step that records the week and
drives the week-over-week comparisons. The form should come pre-filled
from the sync (below) so I only review and confirm
- Footer tools: export a backup as a .json file, import a backup, "set
baseline" (collapse history to the most recent filed week and clear
leftover sample data), and "reset to sample"
- Ship with 12 weeks of sample numbers so it looks alive on day one. The
first time I file real numbers, clear the samples automatically so my
numbers become week one
THE SYNC
Also build a sync script in Node.js with no dependencies that pulls my
real numbers automatically from the official TikTok Display API and
Instagram API with Instagram Login. It should collect:
- follower counts, posts, reach and engagement for both platforms
- my recent individual posts with views, likes, comments, saves and
shares (Instagram per-post insights), so the best posts section and
the posting calendar fill in by themselves
- Instagram audience demographics (follower_demographics: country, age,
gender) for the audience section
It writes everything to one JSON file the dashboard reads. Cache per-post
insights between runs so it does not refetch old posts every time.
CONNECTION DETAILS THAT MATTER
- TikTok uses OAuth with PKCE. Its code challenge must be a hex-encoded
SHA256 of the code verifier, which is TikTok's own variant rather than
the usual base64. Scopes: user.info.basic, user.info.stats, video.list.
Send the redirect address http://127.0.0.1:5176/callback/ and run a
small local server on port 5176 to catch the reply. In TikTok's
developer portal the redirect address has to be saved as a wildcard,
http://127.0.0.1:*/callback/.
- Instagram uses OAuth with the redirect address
https://localhost:5177/callback and the scopes instagram_business_basic
and instagram_business_manage_insights. There is no local HTTPS server,
so let me paste the redirected web address by hand. Exchange for a
long-lived token and refresh it automatically on later runs.
- Keep my API keys in a separate config file, sync/config.json, that is
never uploaded. Ship a config.example.json with PASTE_YOUR_... placeholders.
- Save tokens to a file so I only approve access once.
LAUNCHERS
Make two files I can double-click: serve.command, which starts a local
server with no-cache headers and opens the dashboard, and sync.command,
which runs the sync and waits for a keypress before closing. The dashboard
must be opened through serve.command for the sync file to load (browsers
block file:// pages from reading it), so say so in a README.
If any part of the setup throws an error later, I will paste it back to
you and we can fix it together.
Claude will create a folder of files and a working dashboard, and it will build for a while. At first it shows sample numbers so you can see the layout. Double-click serve.command to look at it. If the style is off, this is the cheapest moment to say so: “make the headlines bigger”, “the accent is too loud”, “closer to a real newspaper front page”. Iterate until it feels like yours.
Keep Claude Code open. If anything goes wrong later, you will paste the error back to it.
Step 3: Connect TikTok
This lets the dashboard read your TikTok numbers on its own. You will set up a free developer app on TikTok’s website.
-
Go to developers.tiktok.com and log in with your TikTok account. Agree to the developer terms if it asks.
-
Click Manage apps, then Connect an app. Give it any name, such as “My Dashboard Sync”.
-
Add the Login Kit product to the app. You do not need any other product. The data comes from the scopes, not from a separate product.
-
At the top of the app page there is a switch between Production and Sandbox. Click Sandbox, and do the rest of these steps inside it. Do not click “Submit for review”. You do not need a review to read your own account.
-
Find the Platforms section and check the Desktop box.
-
Find the Redirect URI field under Login Kit and click the Desktop tab. Type this in exactly, including the star and the slash at the end:
http://127.0.0.1:*/callback/This is the form TikTok’s validator accepts. The word “localhost” and a fixed port number both get rejected in practice, so use this and nothing else.
-
Find the Scopes section and add these three:
user.info.basic,user.info.stats,video.list. -
The Sandbox makes you fill in app details: a category, a description, a Terms of Service link, and a Privacy Policy link. None of this is reviewed by anyone, so a short description is fine, and for the link fields you can use your own TikTok profile address.
-
Open Sandbox settings and add your own TikTok account as a target user.
-
Open App details inside the Sandbox and copy the Client key and the Client secret.
Now open the config file Claude made. It is named config.json, inside the sync folder of your project (if you only see config.example.json, duplicate it and rename the copy to config.json). Open it in any text editor. Paste your Client key and Client secret in where it says PASTE_YOUR_TIKTOK_CLIENT_KEY and PASTE_YOUR_TIKTOK_CLIENT_SECRET.
Paste each value between the quotation marks, and keep the quotation marks. If your text editor curls the quotes into fancy ones, turn that off (in TextEdit it is under Edit → Substitutions → uncheck Smart Quotes).
Step 4: Connect Instagram
Instagram’s API only works with a Professional account, which is free.
-
In the Instagram app on your phone, open Settings → Account type and tools → Switch to professional account. Choose Creator. You can switch back later if you want.
-
On your computer, go to developers.facebook.com and log in with your Facebook account. If you do not have one, create one.
-
Click Create App. Give it any name. The business portfolio dropdown can stay empty. For the use case, pick the Instagram option if you see one, otherwise pick Other and then choose Business as the type. Finish creating it.
-
You are now on the app dashboard. Find Instagram in the product list, click Set up, and choose API setup with Instagram business login (older screens call it “API setup with Instagram login”, it is the same thing).
-
Your Instagram app ID and Instagram app secret are shown near the top of this page. You will copy them in a moment.
-
Find the section called Set up Instagram business login and click its Set up button. In the panel that opens, find the redirect field and type this in exactly:
https://localhost:5177/callbackIt has to start with https. Plain http gets rejected with “Error saving redirect URIs”. Ignore the separate “Callback URL” under Configure webhooks, that is a different feature you do not need.
-
In the left menu, open Permissions and features and make sure both of these are added:
instagram_business_basicandinstagram_business_manage_insights. The insights one is what gives you reach, per-post views and the audience breakdown. -
Make your account a tester. This has two parts, and the order matters:
- Open the app’s Roles page. Under Instagram Testers, add your Instagram username. This sends an invite.
- Go to instagram.com/accounts/manage_access in a web browser, signed in as that account, and accept the invite. Meta can take five or ten minutes to notice.
-
You can ignore “Complete app review”. Review is only needed to read other people’s accounts. For your own, being a tester is enough.
-
Copy the Instagram app ID and Instagram app secret from step 5.
Open config.json again and paste them in where it says PASTE_YOUR_INSTAGRAM_APP_ID and PASTE_YOUR_INSTAGRAM_APP_SECRET. Keep them inside the quotation marks.
Step 5: Turn on the sync
In your project folder, double-click the file sync.command. A window opens and your browser asks you to approve access.
For TikTok, click Allow. The window handles the rest on its own.
For Instagram, click Allow. Your browser will land on a page that says it cannot be reached. That is expected. Copy the full web address from the address bar (it contains ?code=) and paste it into the sync window when it asks.
When the sync finishes, double-click serve.command. Your dashboard opens in the browser.
Click Update this week’s numbers. Both platforms should be pre-filled and marked as synced. Look the numbers over and click the confirm button (mine says File the edition; yours will match your style).
Your dashboard is now live with your real data. The best posts and the posting calendar are already filled in. The audience section fills in once your Instagram account has 100 or more followers; below that, Instagram simply does not share demographics.
That is the end of sitting one. The next two steps build the documents brands ask for.
Step 6: Add your rate card
The rate card is a one-page priced document that opens in a new tab and saves as a PDF. It deliberately carries no follower numbers: those live on the public media kit (next step), which is always current, so a PDF sitting in someone’s inbox six months from now can never quote a stale figure.
Back in Claude Code, paste this:
Add a rate card to the dashboard, in the same brand.css style.
- A button in the dashboard's header opens it in a new tab as a
standalone page. It is designed for "Save as PDF" from the browser's
print dialog, so add a print stylesheet and make links print as
readable text.
- Sections: a small hero with my name, portrait, handles and a one-line
intro ("Below you'll find my rates for sponsored videos and usage
rights."); the rate card itself as a table; my past collaborations as
a row of brand logos; and a closing line with my email and a link to
my public media kit for the live numbers.
- It carries no follower counts or audience charts. Those belong on the
public media kit only.
- Two editors in the dashboard's footer, saved in localStorage:
"Edit rate card": rows of label, price, optional note, a one-line
public blurb (used later by the media kit, which never shows prices),
and a "most popular" checkbox.
"Edit partnerships": rows of brand name and the brand's website
domain. Fetch the logo from the domain automatically so I never
upload logo files.
- Put my email, portrait (as an embedded image so it travels inside the
PDF) and the intro line in the same data file as my name and handles.
- Seed it with three placeholder rows so I can see the layout.
Open the dashboard, fill in Edit rate card with your real packages and prices, and Edit partnerships with the brands you have worked with. Click the rate card button, check it, and use Save as PDF. That PDF is what you send when a brand asks for rates.
If you have not priced yourself yet, a rough starting point: work out what a brand would pay per thousand views on your average reel, then check it against what similar creators in your niche charge. Most people start too low.
Step 7: Publish your public media kit
The media kit is a public page: your bio, your live numbers, your audience split, the brand wall, a grid of your best videos, and your packages without prices. The dashboard writes it as a single HTML file into a folder, and you drag that folder onto a free host. Nothing is typed twice, so the numbers on it are always the numbers on your dashboard.
In Claude Code, paste this:
Add a public media kit, built from the same data as everything else, in
the same brand.css style.
WHAT IT SHOWS
- Hero: my name, role (e.g. "Creator"), niche, location, an "available
for briefs" badge, my portrait, and a short bio written for someone
who just landed on the page. Put role, niche, location, bio, turnaround
time and a closing line in the data file so I can edit them in one place.
- My numbers, pulled from the synced history: total audience, engagement
rate, average views per reel, average engagement per post, with the
Instagram audience split (locations, age, gender). If my engagement
rate is above the usual benchmark for my audience size, add a small
callout saying so; otherwise do not show the callout at all.
- A brand wall built from the partnerships I already entered for the
rate card.
- A work grid of my best videos. Each card plays the video inline in a
popup, shows a poster frame, the brand name and logo, and a chip for
the post type (UGC deliverable, brand collab, or organic post). Brand
collabs and organic posts can also carry a link to the original post.
- A "work with me" section listing my packages using the public blurbs
from the rate card, with the "most popular" one flagged, and NO prices.
A line says rates are available on request.
- Contact: my email and handles.
- Anything I leave empty should hide itself rather than show a blank.
HOW IT PUBLISHES
- A footer editor, "Edit public media kit", with a row per video: brand,
domain (for the logo; fall back to the same brand in Edit partnerships
if blank), post type, optional post link, and a drop zone. When I drag
an .mp4 onto the row, copy it into public-media-kit/media/ under a
URL-safe filename, fill in the path, and grab a poster frame from
about one second in so the card has a thumbnail without me doing
anything. Let me drop my own still to override the poster.
- Use the File System Access API (Chrome and Edge) so I "Connect" the
public-media-kit folder once, and after that "Save & publish" writes
index.html and the videos into it directly. If the browser cannot do
that, hand me the index.html as a download instead.
- The generated index.html must be self-contained: inline the
stylesheet, my portrait and every poster frame. Only the videos stay
as separate files in media/, because they are too big to inline.
- A "Publish public kit" footer button that republishes without opening
the editor, so I can run it after each sync.
- A "Download site" footer button that zips index.html plus the media
folder into one file for hosting from another machine.
- Warn me by name at publish time if a video a card points to is missing
from media/.
- Add a README inside public-media-kit/ saying the folder is generated
and never edited by hand.
Then, in Chrome or Edge:
- Open the dashboard through
serve.command, scroll to the footer, and click Edit public media kit. - Click Connect and pick the
public-media-kitfolder inside your project. You do this once. - Add a row per video. Export each video as .mp4, H.264, 1080×1920, under about 15 MB (a .mov often will not play in Chrome). Drag the file onto its row, fill in the brand, domain and post type.
- Click Save & publish. The page is written.
To put it online, go to app.netlify.com, make a free account, and drag the whole public-media-kit folder onto the deploy box. It is live in about ten seconds. Under Site settings → Change site name, give it a readable address, then put that link in your bio. When you want to update it later, publish from the dashboard again and drag the folder onto the same site’s Deploys tab.
Do not drag index.html on its own. The page needs its media folder or every video card is a dead player. If you are publishing from a machine that is not the one the dashboard lives on, use Download site and drag the zip instead.
Netlify’s free tier is fine for a handful of videos. If you get to dozens, host the videos on Vimeo or Cloudflare Stream and paste the link into the row instead of dropping a file.
Your weekly routine
Pick one day a week and stick with it. Monday works well.
- Double-click
sync.commandto pull fresh numbers - Double-click
serve.commandto open the dashboard - Click Update this week’s numbers, review, and file
- If you have a media kit online, click Publish public kit and drag the folder onto Netlify again, so the receipts show this week’s numbers
That is about three minutes. The growth chart and the week-over-week comparisons fill in as the weeks add up. Your best posts, your posting calendar, your streak and your audience breakdown update on their own every time you sync.
Making it yours, and changing your mind later
Because the palette and fonts live in one file, restyling is one prompt. Some that work well:
- “Restyle the whole thing as [new motif]. Keep every feature and every number exactly as it is; only brand.css, the type and the layout details change.”
- “Swap the accent colour to #B34263 everywhere and make sure every text colour still reads clearly against the background.”
- “Rename the dashboard to [new name] and update the header.”
- “The rate card should look more like a printed invoice than a web page.”
Small copy changes (your tagline, bio, turnaround time, closing line) do not need Claude at all. They are in the data file Claude made in Step 2; open it in a text editor, change the text between the quotes, save, and reload.
If you ask for a restyle, republish the media kit afterwards. It carries a copy of the stylesheet inside it, so it does not pick up the change until you do.
If something goes wrong
The two developer sites are fiddly, and most people hit at least one snag. The fastest fix is always the same: copy the error message, paste it into Claude Code, and ask it to help. It knows the whole project and will walk you through it.
A few common ones:
- TikTok will not save the redirect address. Check that you typed
http://127.0.0.1:*/callback/exactly, with the star and the trailing slash, on the Desktop tab. - Instagram says “Insufficient Developer Role”. You have not accepted the tester invite yet. Go to instagram.com/accounts/manage_access and accept it, then wait a few minutes.
- Instagram will not save the redirect address. It must start with https, not http.
- Instagram redirect_uri mismatch. The address in
config.jsonmust match the one you saved in Business login settings exactly, https and all. - The audience section is empty. Instagram only shares demographics for accounts with 100 or more followers. Nothing is wrong; it fills in once you cross that line.
- The sync says it needs Node.js. Install it from nodejs.org and run the sync again.
- The update form is not pre-filled. You opened
index.htmldirectly. Open the dashboard withserve.commandinstead. - Token errors after weeks away. Delete
sync/tokens.jsonand run the sync again to re-approve. - A video will not play on the media kit. Export it again as .mp4 (H.264). Chrome is picky about .mov.
A note on privacy
Everything stays on your own computer. Your API keys live in the config file on your machine and are never uploaded. Your numbers are saved only in your own browser. The dashboard has no account and no server. The only thing that ever goes online is the media kit, and only when you drag it there yourself. It is yours, and it is private.