Skip to content
All articles
7 min read

Facebook Ads URL Parameters and Sub IDs: A Copy-Paste Tracking Setup

Hand-etched advertising signals converging through a tracking junction into one attribution target

The safest Facebook Ads tracking setup does not put every value into a long UTM string and hope analytics reconstructs it later. It passes the three stable Meta IDs into named tracker fields, uses sub IDs for placement and source, and verifies the final URL on a real click.

For DarkCore, paste this into the URL parameters field at ad level:

campaign_id={{campaign.id}}&adset_id={{adset.id}}&ad_id={{ad.id}}&sub1={{placement}}&sub2={{site_source_name}}

If you are building the complete destination URL manually, the first parameter needs ?:

https://go.example.com/?campaign_id={{campaign.id}}&adset_id={{adset.id}}&ad_id={{ad.id}}&sub1={{placement}}&sub2={{site_source_name}}

That small distinction is responsible for a surprising number of broken launches: a parameters-only field normally expects no leading ?, while a complete URL needs exactly one ? before the first key and & before every additional key.

What “Facebook sub ID” actually means

Meta does not provide a special macro called sub_id. A sub ID is a field owned by your tracker or affiliate network. Its value can be filled by one of Meta’s dynamic URL parameters.

For example:

sub1={{placement}}

Here sub1 is DarkCore’s field, while {{placement}} is the Meta macro. When the ad is clicked, Meta replaces the macro with a real value and the tracker receives something like:

sub1=facebook_feed

The same principle works for campaign, ad set and ad IDs. DarkCore has dedicated fields for those IDs, so there is no reason to consume three generic sub slots for data that already has a named home.

Incoming parameterMeta valueWhat it identifiesWhy this field
campaign_id{{campaign.id}}CampaignStable join key for reporting and cost data
adset_id{{adset.id}}Ad setStable audience/bid-group key
ad_id{{ad.id}}AdMost granular stable ad key
sub1{{placement}}Feed, Stories, Reels and other placementsUseful routing and profitability breakdown
sub2{{site_source_name}}Facebook, Instagram, Messenger or Audience Network sourceSeparates inventory source from placement

Meta’s dynamic URL parameter reference also exposes name-based values such as {{campaign.name}}, {{adset.name}} and {{ad.name}}. Use them only as readable labels, not as primary keys. Names can be edited, duplicated or reused; IDs are the safer fields for joining click, spend and conversion data.

Five distinct data tokens being sorted into a stable attribution record

If your team needs human-readable labels in the click log, add them after the stable fields:

campaign_id={{campaign.id}}&adset_id={{adset.id}}&ad_id={{ad.id}}&sub1={{placement}}&sub2={{site_source_name}}&sub3={{campaign.name}}&sub4={{adset.name}}&sub5={{ad.name}}

Do not replace the ID fields with those names.

Where fbclid fits—and where it does not

fbclid and sub IDs solve different problems.

  • fbclid is Meta’s click identifier. When present on the incoming URL, DarkCore captures it for click continuity and Meta attribution signals.
  • campaign_id, adset_id and ad_id describe the account structure that produced the click.
  • sub1sub10 are flexible business dimensions you control: placement, source, angle, buyer code, creative batch or another segmentation value.

Do not put fbclid into sub1 and do not manufacture it from an ad ID. Let Meta append the real click identifier, while your URL parameter template carries the campaign context you want to report on.

If Pixel and Conversions API can report the same business event, keep this click context separate from the shared event identity and use the Facebook CAPI deduplication checklist before launch.

How DarkCore carries the values forward

At the first request, DarkCore reads the named campaign fields and sub1 through sub10 from the query string. You can then reference those captured values in a Track Link:

https://affiliate.example/click?cid={click_id}&campaign={campaign_id}&adset={adset_id}&ad={ad_id}&placement={sub1}&source={sub2}

The brace syntax here belongs to DarkCore, not Meta. The two stages are deliberately different:

  1. Meta replaces {{ad.id}}, {{placement}} and the other double-brace macros before the landing request.
  2. DarkCore stores the received values, then replaces {ad_id}, {sub1} and {click_id} when it builds the outbound URL.

If a literal {{ad.id}} reaches DarkCore, the Meta stage did not expand it. If a literal {sub1} reaches the affiliate URL, the outbound Track Link mapping needs investigation.

For the later conversion half of the chain, see how to track Facebook affiliate conversions and the S2S postback guide.

When the network asks for {subid}, {aff_sub} or another unfamiliar token, use the click ID, sub ID and postback macro mapping guide before copying the template.

The pass/fail launch test

Do not approve this setup because the template “looks right.” Record one real or platform-generated test click and fill this table.

TestExpected factPass?
Final landing URLContains one ?, then valid key=value pairs separated by &
Macro expansionNo literal {{campaign.id}}, {{ad.id}} or other Meta token remains
Stable IDscampaign_id, adset_id and ad_id contain numeric IDs
Placementsub1 contains the actual placement, not a hard-coded guess
Sourcesub2 separates Facebook/Instagram/other Meta inventory
Click recordThe same values appear on the DarkCore click
Outbound link{click_id} and required captured fields are expanded once
Test conversionOne postback attaches to the same click without duplicate revenue

A missing connector being restored in an otherwise broken tracking path

Preview tools are useful for syntax, but the decisive evidence is the click record and final browser URL. Some preview paths do not behave exactly like a delivered ad click.

Common failures

The first parameter has no question mark

This is correct inside Meta’s dedicated URL parameters field, but wrong when you paste the same string directly after a domain. A full URL such as https://go.example.com/sub1={{placement}} treats sub1 as part of the path. It must be https://go.example.com/?sub1={{placement}}.

Every value is stored in UTMs only

UTMs are useful for web analytics, but a tracker still needs deterministic fields for routing, click lookup and postback reconciliation. Keep the stable IDs in named fields even if you also add a UTM layer.

Names are used as database keys

Campaign names are readable but operationally weak. A rename can split reports; duplicated names can merge unrelated traffic. Store IDs first, names second.

Meta macros and DarkCore macros are mixed

{{ad.id}} belongs in the ad URL parameters. {ad_id} belongs in a DarkCore outbound Track Link. Swapping the syntax leaves a literal token in the URL.

The team tests only the redirect

A 200 page load or a successful redirect proves transport, not attribution. The acceptance result is a complete row: incoming IDs, stored click, expanded outbound URL and exactly one conversion on the original click.

FAQ

Which Facebook macro should go into sub1?

For the recommended DarkCore mapping, use sub1={{placement}}. Campaign, ad set and ad IDs have dedicated fields, so placement is a more useful first custom dimension.

Can I use campaign name instead of campaign ID?

You can store the name as an additional label, but use {{campaign.id}} as the primary reporting and join key.

Is fbclid the same as a Facebook sub ID?

No. fbclid identifies a specific Meta click. A sub ID is a custom tracker field used to store a dimension such as placement, source or creative batch.

How many sub IDs does DarkCore support?

DarkCore captures sub1 through sub10 on the incoming click and exposes the same values as {sub1} through {sub10} in outbound Track Links.

  • facebook ads url parameters
  • facebook sub id
  • meta ads macros
  • affiliate tracking
  • sub id tracking