Skip to content

6 min read

How to track Instagram link in bio clicks to paying customers

Why Instagram visits land as direct or l.instagram.com, how to tag each post and story with utm_content, and how to count signups and first payments per post.

You post on Instagram, people tap the link, some of them sign up. Then you open your analytics to see how many, and Instagram barely registers. Some visits sit under direct, a few appear as a referral from l.instagram.com, and none of it says which post did the work. If you want to track Instagram link in bio traffic properly, the referrer won't do it for you. The link has to carry its own label.

Below: why the referrer fails, how to tag each post and story, whether to rotate one bio link or use a link page, and how to get from a tagged tap to signups and first payments per post.

Why does Instagram traffic show up as direct?

Two things happen between the tap and your page.

First, links on Instagram usually open in Instagram's in-app browser rather than in Safari or Chrome. Second, outbound links pass through Instagram's own redirect on l.instagram.com. Depending on the device, the app version and the browser's referrer policy, your page receives either that redirect domain as the referrer or no referrer at all.

With no referrer, your analytics files the visit under direct. With l.instagram.com, it depends on whether your tool recognizes that domain as social. Some do, some count it as a generic referral, and you end up with Instagram split across two or three rows, none of them complete.

There is a third leak that no tagging fixes: people who see your post, don't tap, and later type your URL or search your name. That traffic is real and caused by the post, and it will always look like direct or organic search. Tagging recovers the taps. It can't recover the people who never tapped.

How should you tag Instagram links?

Put the source in the URL with UTM parameters, so it survives whatever the referrer does:

https://yourapp.com/?utm_source=instagram&utm_medium=social&utm_campaign=bio&utm_content=POST_ID

Keep utm_source and utm_medium fixed for everything on Instagram, use utm_campaign for the placement, and use utm_content for the specific post or story. That last parameter is what makes per-post numbers possible.

Placementutm_campaignutm_content
The link in your biobioThe post the bio is currently promoting
A story link stickerstoryThe story, or a label for the story sequence
A post you're driving traffic topostThat post's ID

Captions don't make links clickable, so in practice every post link lives in your bio or in a story sticker. The post campaign is for when the bio link exists for one specific post and you want that recorded explicitly.

For utm_content, the post's own ID is the most useful value because it can be matched to the post's metrics later. If you'd rather use readable labels, choose a scheme like 2026-10-02-pricing-reel and keep it. The broader naming rules are the same as for any campaign; which blog posts bring paying customers walks through why consistent first-touch data matters more than clever names.

One link per post, or a link page?

This is the decision that determines whether per-post numbers are possible at all.

Rotating the bio link

Your bio has one link, and you change it every time you publish a post that should drive traffic, tagged with that post's utm_content.

The upside: every tap carries the post that sent it, and there is no extra page in the way. The downside: older posts that say "link in bio" now send people to whatever the newest link is, and the credit goes to the newest post. You also have to remember to update it each time. In exchange you get clean per-post numbers while each post is fresh.

Multiple bio links or a link page

Instagram lets you add several links to your bio, and link pages put a list of links behind one URL. Each entry can carry its own utm_content, so older posts keep a working, correctly tagged link.

Two cautions. Every extra tap loses some people, so a link page costs you visits compared with a direct link. And if the link page lives on your own domain, it becomes the first page those visitors land on, so a first-touch setup records the untagged bio link rather than the entry they tapped next. The post-level detail is lost. A third-party link page avoids this, because the first page on your domain is the tagged entry.

A workable default: rotate a single tagged bio link for the posts you're actively promoting, and use story stickers, each tagged, for everything time-limited. Reach for a link page only when you genuinely have several things to point at.

How do you measure signups and first payments per post?

Tagged visits are the start. To get from a tap to money you need the same chain as any other channel:

  1. On the first page view, record the landing page and UTM parameters, once, without overwriting them on later visits.
  2. At signup, attach that first touch to your user record.
  3. When the user pays, make sure the subscription carries the same user ID: metadata.user_id on the Stripe subscription, or the app user ID you pass to RevenueCat.

Then group by utm_content:

select
  u.first_touch->'utm'->>'utm_content' as post,
  count(distinct u.id) as signups,
  count(distinct s.user_id) as paying,
  coalesce(sum(s.first_payment_mrr), 0) as first_mrr
from users u
left join subscriptions s on s.user_id = u.id and s.first_paid_at is not null
where u.first_touch->'utm'->>'utm_source' = 'instagram'
group by 1
order by first_mrr desc;

Use the MRR at first payment rather than current MRR, so a post's number doesn't change months later because a customer upgraded.

Group by post, not by week. Stories disappear after 24 hours and feed posts are seen mostly while they're recent, so a post's result settles quickly. Weekly totals blend several posts and tell you little about any of them.

If the link sends people to the App Store rather than your site, UTM parameters don't apply. Use an App Store campaign link per post instead, and accept that the results are aggregate; App Store campaign links covers that side.

What consent and the in-app browser do to the numbers

In the EU, storing a first touch in a cookie or localStorage for analytics needs consent. Visitors who decline can't be followed to signup unless they sign up in the same session, so a post's signups are a floor, not a total. The consent side is covered in more depth in analytics consent and GDPR.

The in-app browser adds its own gap. It keeps storage separate from the phone's main browser, so someone who taps your link inside Instagram, leaves, and signs up days later in Safari arrives as a new visitor with no first touch. Same for anyone who reads on the phone and pays on a laptop.

None of that is fixable, but it is measurable. Next to the per-post numbers, keep the share of new signups that carry any first touch at all. If that share is low, the post ranking is noise and the first job is fixing capture.

Joinsight's link builder presets the Instagram source, medium and placement, and for posts puts the post's ID in utm_content, which is what joins a post to the signups and MRR it brought. Tagged links are attributed today. Instagram post metrics such as views, reach, saves and shares, matched on that same utm_content, are coming soon.

See what makes you money.

Connect Stripe or RevenueCat and Search Console, and you see combined MRR and 16 months of search history on day one. Page and keyword revenue follow as the snippet collects first touches.

No card required. The trial clock starts when your first revenue source connects.