How to Build UTM Links Programmatically (CLI + API)
Building UTM links programmatically means generating tracked campaign URLs from code — not a browser form. You need this when running multi-platform campaigns at scale, when your AI agent needs to produce links, when you're generating links from a CSV or CRM export, or when you want link creation inside a CI/CD pipeline. MissingLinkz gives you three ways to build UTM links programmatically: the CLI, the npm package, and the REST API. Here's how each one works.
When you need programmatic UTM building
A web form works fine when a single person is building a handful of links for a single campaign. It stops working when any of these conditions apply:
- Multi-platform campaigns at scale
- A campaign running across LinkedIn, X, Facebook, Google Ads, and email needs a unique tracked link for each channel. That's five links per campaign, and teams running dozens of campaigns per month can't afford to generate them manually without introducing naming inconsistencies that fragment analytics.
- AI agent workflows
- AI agents (Claude Code, Cursor, custom GPT-powered bots) can't interact with web forms reliably. They need a programmatic interface — CLI commands they can shell out to, an npm package they can import, or a REST API they can call. See why AI agents can't use UTM builders for the full explanation.
- CSV or database-driven campaigns
- Email marketing platforms, ad platforms, and CRMs often export campaign configs as CSVs. Generating a tracked link for every row in a 500-row export requires a loop and a programmatic tool, not a form.
- CI/CD pipeline integration
- When campaign destination URLs are part of a codebase, link generation and validation belong in the same pipeline that deploys the landing pages. See automating campaign link validation in CI/CD for the pipeline setup.
Method 1: CLI with mlz build
The simplest way to build UTM links programmatically is the mlz build command. It's designed for terminal use, shell scripts, and CI pipelines. Install it globally and call it from anywhere:
npm install -g missinglinkz
Build a tracked link with all five UTM parameters:
mlz build --url "https://yoursite.com/landing" --campaign "spring-launch" --source "linkedin" --medium "social" --term "campaign-infrastructure" --content "variant-a"
The response is structured JSON:
{
"tracked_url": "https://yoursite.com/landing?utm_source=linkedin&utm_medium=social&utm_campaign=spring-launch&utm_term=campaign-infrastructure&utm_content=variant-a",
"params": {
"utm_source": "linkedin",
"utm_medium": "social",
"utm_campaign": "spring-launch",
"utm_term": "campaign-infrastructure",
"utm_content": "variant-a"
},
"destination_url": "https://yoursite.com/landing",
"created_at": "2026-04-09T10:22:14.010Z",
"link_id": "lnk_9wlvd9qi",
"campaign_id": "cmp_kbcht77d",
"stored": true
}
To extract just the tracked URL from a script, pipe through jq:
mlz build --url "https://yoursite.com/landing" --campaign "spring-launch" --source "linkedin" --medium "social" --format json | jq -r '.tracked_url'
Add --validate to check the destination URL resolves before building the link, or --inspect to also verify OG tags and page metadata:
mlz build --url "https://yoursite.com/landing" --campaign "spring-launch" --source "linkedin" --medium "social" --validate
Bulk building from a CSV
To generate tracked links for every row in a campaign CSV, use a shell loop. Given a CSV with columns destination, source, medium, and campaign:
#!/bin/bash
while IFS=, read -r destination source medium campaign; do
url=$(mlz build \
--url "$destination" \
--source "$source" \
--medium "$medium" \
--campaign "$campaign" \
--format json | jq -r '.tracked_url')
echo "$campaign,$source,$url"
done < <(tail -n +2 campaigns.csv) # skip header row
This outputs one tracked URL per row to stdout, which you can redirect to a file or pipe into another tool.
Method 2: npm package
For Node.js applications, scripts, and serverless functions, the missinglinkz npm package provides the same link generation capabilities as the CLI without needing to shell out to a subprocess.
npm install missinglinkz
Build a single tracked link from a Node.js script:
import { execSync } from 'child_process';
function buildUTMLink(url, campaign, source, medium) {
const result = execSync(
`mlz build --url "${url}" --campaign "${campaign}" --source "${source}" --medium "${medium}" --format json`,
{ encoding: 'utf8' }
);
return JSON.parse(result);
}
const link = buildUTMLink(
'https://yoursite.com/landing',
'spring-launch',
'linkedin',
'social'
);
console.log(link.tracked_url);
// https://yoursite.com/landing?utm_source=linkedin&utm_medium=social&utm_campaign=spring-launch
For bulk building from an array of campaign configs:
import { execSync } from 'child_process';
const campaigns = [
{ url: 'https://yoursite.com/landing', campaign: 'spring-launch', source: 'linkedin', medium: 'social' },
{ url: 'https://yoursite.com/landing', campaign: 'spring-launch', source: 'twitter', medium: 'social' },
{ url: 'https://yoursite.com/landing', campaign: 'spring-launch', source: 'newsletter', medium: 'email' },
];
const trackedLinks = campaigns.map(({ url, campaign, source, medium }) => {
const output = execSync(
`mlz build --url "${url}" --campaign "${campaign}" --source "${source}" --medium "${medium}" --format json`,
{ encoding: 'utf8' }
);
const result = JSON.parse(output);
return { source, medium, tracked_url: result.tracked_url };
});
console.log(JSON.stringify(trackedLinks, null, 2));
This produces a JSON array of tracked links for all three platforms in a single script run.
Method 3: REST API
For backend services, serverless functions, and any environment where installing an npm package isn't practical, the MissingLinkz REST API provides the same link generation via HTTP. The API base URL is https://api.missinglinkz.io.
Build a tracked link with a POST request:
$ curl -X POST https://api.missinglinkz.io/v1/links \
-H "Authorization: Bearer $MLZ_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"destination_url": "https://yoursite.com/landing",
"campaign": "spring-launch",
"source": "linkedin",
"medium": "social"
}'
The API returns the same JSON structure as mlz build:
{
"tracked_url": "https://yoursite.com/landing?utm_source=linkedin&utm_medium=social&utm_campaign=spring-launch",
"params": {
"utm_source": "linkedin",
"utm_medium": "social",
"utm_campaign": "spring-launch"
},
"destination_url": "https://yoursite.com/landing",
"created_at": "2026-04-09T10:22:14.010Z",
"link_id": "lnk_9wlvd9qi",
"campaign_id": "cmp_kbcht77d",
"stored": true
}
The REST API is the right choice when you're building link generation into a backend service that runs outside of a Node.js context, or when you want to integrate with platforms like Zapier, Make, or Retool that can make HTTP requests but can't run CLI commands.
Adding validation to programmatic builds
Building UTM links programmatically is only half the picture. The other half is verifying that the destination URL works before the link goes into a campaign or an ad platform. The --validate flag on mlz build checks the destination URL for SSL, resolution, and redirects before generating the tracked link:
mlz build --url "https://yoursite.com/landing" --campaign "spring-launch" --source "linkedin" --medium "social" --validate --format json
If the destination fails validation (404, SSL error, too many redirects), the command exits with a non-zero status code and does not output a tracked URL. This makes it safe to use in a pipeline: if the destination is broken, the link generation step fails and nothing downstream receives a broken link.
For the complete pre-publish workflow that combines link building, destination validation, and OG tag inspection in a single command, see the campaign link validation guide. For the AI agent integration that calls these commands autonomously, see how to build UTM links with an AI agent.
FAQ
- Do I need an API key to build UTM links with mlz?
- No.
mlz buildgenerates correctly formatted UTM links without an API key. The API key is optional and only required if you want links stored in the MissingLinkz dashboard (for tracking, campaign organization, and themlz links listhistory). Without a key, all link generation works offline — the tracking URL is constructed locally and nothing is sent to a server. - Does mlz build enforce lowercase parameter values?
- Yes. All UTM parameter values are normalized to lowercase automatically.
--source "LinkedIn"producesutm_source=linkedinin the tracked URL. This prevents the most common cause of analytics fragmentation: mixed-case parameter values being treated as separate traffic sources in GA4 and other analytics tools. - Can I use mlz build in a serverless function?
- The CLI requires Node.js and a global npm install, which doesn't work in most serverless environments. Use the REST API instead: a single POST to
https://api.missinglinkz.io/v1/linkswith your API key generates the tracked URL from any environment that can make HTTP requests. This includes AWS Lambda, Cloudflare Workers, and Vercel Edge Functions. - What's the difference between mlz build --validate and mlz check?
mlz build --validatevalidates the destination URL as part of the build step and fails the build if validation fails.mlz checkis a standalone validation command that checks a URL without building a tracked link. Use--validatein build scripts to gate link generation on destination health. Usemlz checkin CI pipelines to validate a set of URLs independently of link generation.- How do I generate links for 50 campaigns at once?
- Use the shell loop pattern from the bulk building section above, or loop over an array in Node.js. Each call to
mlz buildtakes about 100ms for offline generation (without--validate). A 50-campaign batch completes in roughly 5 seconds locally, or fewer if parallelized.
Related reading
Build your first tracked link in 30 seconds
Install MissingLinkz and generate your first UTM-tracked link from the terminal. No browser, no form, no manual work.
npm install -g missinglinkz
mlz build --url "https://yoursite.com" --campaign "launch" --source "linkedin" --medium "social"
See the API docs for REST endpoints, or the SKILL.md for agent integration.