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.

Terminal window showing mlz build command with JSON output, with three method badges: CLI, npm, and REST API

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:

mlz build — JSON output
{
  "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:

bulk-build.sh
#!/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:

build-link.mjs
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:

bulk-build.mjs
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 — build a UTM link via API
$ 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:

API response
{
  "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 build generates 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 the mlz links list history). 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" produces utm_source=linkedin in 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/links with 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 --validate validates the destination URL as part of the build step and fails the build if validation fails. mlz check is a standalone validation command that checks a URL without building a tracked link. Use --validate in build scripts to gate link generation on destination health. Use mlz check in 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 build takes about 100ms for offline generation (without --validate). A 50-campaign batch completes in roughly 5 seconds locally, or fewer if parallelized.

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.