Building My Own Instagram Analytics

By Lz on 2026-10-03 · 7 min read

Building My Own Instagram Analytics Thumbnail

I post to Instagram as @lzctrl every day, and until recently the only way I had of knowing what was working was the insights screen in the Instagram app, which is fine for checking how one post did but not much help when I want to know which posts actually grew my account. It shows reach and views well enough, but the number I care about most, how many people followed me because of a specific post, is hard to find and almost impossible to compare across a month of posts.

So I did the same thing I did when I didn't want to pay for Mailchimp, and built it myself. There's now a Growth page in my admin app that pulls everything from the Instagram Graph API once a day and lays it out the way I want to read it. Here's how it's put together, along with the parts of the Meta side that cost me the most time.

Getting access to the API

The Graph API only works with an Instagram business account that's linked to a Facebook Page, and @lzctrl already was, so that part was done before I started. None of it costs anything either. There's no paid tier for reading insights on an account you own, and because my Meta app only ever reads my own account it can stay in Development mode and never needs to go through App Review.

Creating the app is the first place things have changed from what a lot of tutorials describe. Meta now asks you to pick use cases rather than products, so I called the app LzCtrl Insights and selected "Manage messaging & content on Instagram", which is what they call the Instagram Graph API now, along with "Manage everything on your Page". The Instagram one has to be configured for Facebook Login rather than Instagram Login, because the Instagram Login version uses a different token flow and nothing below applies to it. The scopes the working token ends up with are instagram_basic, instagram_manage_insights, pages_show_list and pages_read_engagement.

The checkbox that cost me some time

To get a first token you go to the Graph API Explorer, pick the app, add those four permissions and generate a user access token. A popup then asks which Pages and Instagram accounts the token should have access to, and if you don't tick them explicitly you get a token that authenticates perfectly and returns nothing at all. There's no error, just empty responses, which is a confusing way to lose a bit of time.

The token that doesn't expire

The token from the Explorer only lasts a short time, so the next step is exchanging it for a long-lived user token, which lasts 60 days. It's easy to stop there, and that's how you end up with a script that quietly stops returning anything two months later. The step that fixes it is calling /me/accounts with that long-lived user token and taking the access token off the Page instead, because a Page token derived from a long-lived user token doesn't expire.

curl -s "https://graph.facebook.com/v23.0/me/accounts?access_token=LONG_LIVED_USER_TOKEN"

From there one more call against the Page with fields=instagram_business_account gives you the Instagram user id, and those two values are all the sync needs.

What the API actually gives you

For posts, I can fetch 50 at a time with their insights nested on the media edge, so it's one call per 50 posts rather than one per post. Each one comes back with reach, views, saved, shares, total_interactions, profile_visits and follows.

follows is the reason this whole project exists. It attributes a follow to the post that caused it, which means I can rank posts by whether they grew the account instead of by how many likes they got, and that's the comparison the Instagram app doesn't really let me make.

For the account as a whole I get a daily series of reach and follower_count, and follower_count is net new followers per day rather than a running total, which is worth knowing before you chart it. Meta also reports the current day as 0 until it settles, so I drop the last entry instead of letting a false zero drag the average down.

Metrics that have moved

If you've used this API before you might reach for impressions and engagement, and Meta rejects both now. views replaced the first one and total_interactions replaced the second. When you ask for a metric that's been renamed, the error message lists every valid one, and that's been the fastest documentation I've found for this API.

Syncing it every day

The sync is a scheduled Firebase Cloud Function called syncGrowth that runs every day at 6am Eastern, pulls the last 30 days and writes the results to Firestore, which is where the admin app reads it from. It lives in the same project as my newsletter, so when something goes wrong it emails me through the same SES mailer the newsletter sends with. That happens when the token is missing, when Instagram rejects the request, and when the token gets within 14 days of expiring, because a sync that fails silently would leave me looking at old numbers without realizing it.

There's also a pnpm growth script that prints the same analysis to the terminal, and pnpm growth:sync for when I want fresh numbers without waiting for the next morning.

The Growth page

The page opens with a banner that tells me how many followers I have and how many I'm gaining a day, and how that pace compares with the goal I've set for the end of the year, including the daily rate I'd need to hit it and roughly where I'd finish if nothing changes, which is a more useful thing to read than a follower count on its own.

Under that are four tiles. One is the follower count, one is how many people a typical post is seen by along with what percentage of my followers that is, one is how many of my new followers came from a post compared with finding me some other way, and the last tells me whether my Instagram access is going to expire, which with a Page token should always say it doesn't.

Then there are two sparklines, one for new followers each day and one for people reached each day, and two small tables that split my posts up by photos versus reels and by day of the week so I can see which ones actually bring new people in. At the bottom is every post in a table I can sort by any column, with anything seen by more than twice as many people as usual highlighted in green. If the data ever goes stale, a warning shows up at the top with the command to run the sync myself.

Writing labels I'll actually read

I wrote every label in plain English on purpose. The tile says "A typical post is seen by" instead of "median reach", and the tables say "Usually seen by" and "New followers" rather than using the API's names. I'm the only person who uses this page, but I'm reading it to decide what to post next, and I'd rather it tell me something in a sentence than make me translate metric names in my head every time I open it.

What's next

Right now the Growth page only knows about Instagram, and that made sense as a starting point because it's where I post every day and where the numbers I cared about were hardest to get at. I'm starting to push a lot more content out over the next few months though, and most of it isn't going to live on Instagram alone, so the plan is to grow this into one place that covers everywhere I post.

TikTok and YouTube are the two I want to bring in first. I haven't dug into either of their APIs properly yet, so I don't know how close I'll get to the per-post follow attribution that makes the Instagram side useful, but even having reach and new followers from all of them side by side would tell me far more than opening each app separately and trying to remember what I saw in the last one. I'll pull in as much as each platform gives me and shape the page around whatever that turns out to be.

Threads should be the easiest one to add down the road, since the Meta app I set up for this already has the Threads API use case attached and just isn't using it yet.

Until then I'll keep posting every day and checking the Growth page to see what's actually landing, and if you want to watch that play out in real time you can follow along on Instagram. If you end up building something like this for your own account, I'd love to see how you put it together!