DEV Community

Aleksander Sekowski
Aleksander Sekowski

Posted on

The VAST Macros Cheat Sheet Every Ad Ops Engineer Need

If you have ever stared at a tracking URL full of [CACHEBUSTING], [TIMESTAMP], and [ADPLAYHEAD] and wondered which of those the player actually fills in, this post is for you.

VAST macros are substitution tokens of the form [MACRO] that ad servers and players replace inside tracking, click, error, impression, and media URLs at request time. They are how a static VAST tag carries dynamic, per-impression context: the device ID, the consent string, the playhead position, the error code. Get them wrong and you lose attribution, break frequency capping, or fire tracking pixels that a cache silently collapses into one.

I have debugged enough broken tags to know the failure modes are always the same handful. Here is the working reference, grouped the way you actually reason about them.

The mental model

A macro only has a defined value in the context where the spec says it does. [ERRORCODE] means nothing inside an Impression pixel. [REASON] means nothing outside a verificationNotExecuted URI. If you drop a macro into the wrong element, the player either leaves the literal [ERRORCODE] string in the URL or substitutes an empty value, and your reporting goes sideways.

The second rule: anything that is itself a URL or contains reserved characters must be percent-encoded when nested inside another URL. [PAGEURL], [ASSETURI], [TIMESTAMP], and the various user-agent macros are the usual offenders.

That is most of the bugs right there. Context and encoding.

Cache busting and timing

The two macros that belong on nearly every tracking URL:

  • CACHEBUSTING resolves to a fresh random 8-digit number per request. Without it, proxies and browser caches happily collapse repeated tracking calls into a single hit, and your impression counts come in low.
  • TIMESTAMP is the ISO 8601 time the request fired. Useful for ordering events and as a secondary cache buster. Percent-encode it, because the colons in 18:30:00Z are reserved.

Playhead: the one everyone gets wrong

This is the single most common source of legacy-tag confusion.

  • ADPLAYHEAD is the current position inside the ad creative, in HH:MM:SS.mmm. This is the VAST 4.1 macro you should be using.
  • CONTENTPLAYHEAD is the pre-4.1 macro. It was ambiguous about whether it meant ad time or content time, so 4.1 deprecated it in favour of [ADPLAYHEAD]. Newer players may not populate it at all.

If your quartile reporting is empty on modern CTV players, a stale [CONTENTPLAYHEAD] in your tracking URLs is the first thing to check.

Errors and verification

  • ERRORCODE substitutes the numeric VAST error code (303, 401, and friends) and only has a defined value inside an <Error> URI. Put it anywhere else and it stays literal.
  • REASON carries why a verification script did not run (1 could not load, 2 could not verify, 3 rejected) and is only valid inside a verificationNotExecuted tracking URI.

Identity and privacy

Everything attribution and consent depends on:

  • IFA is the resettable device advertising identifier: IDFA on iOS/tvOS, AAID on Android, a platform ID on CTV. Empty when the user has opted out.
  • GDPR is the 1/0 flag for whether GDPR applies, and GDPRCONSENT carries the IAB TCF consent string. If [GDPR] is 1 you should be sending a valid consent string alongside it.
  • LIMITADTRACKING reports the device limit-ad-tracking setting. When it is 1, the [IFA] is typically zeroed and personalised targeting must be suppressed.

Context: where and how the ad served

  • DEVICEIP is the end-user device IP, central to geolocation and fraud detection in server-side flows.
  • DOMAIN and PAGEURL identify the supply. Remember to percent-encode [PAGEURL] when it rides inside another URL as a query parameter.
  • PLAYERSIZE gives width,height for viewability context and creative selection.

Identifiers that tie the supply chain together

  • ADSERVINGID is a single identifier from the InLine ad that every party in the chain can log, which is the fastest path to debugging a discrepancy across SSP, DSP, and verification logs.
  • UNIVERSALADID uniquely identifies the creative across systems for creative-level frequency capping.
  • ADCOUNT is the ad's position within a pod, for pod-level reporting and pacing.

The deprecated ones still hiding in production

VAST 4.1 deprecated the older playhead macros. [CONTENTPLAYHEAD] and [MEDIAPLAYHEAD] were both folded into [ADPLAYHEAD]. They still appear constantly in tags copied from older ad servers, and they are a silent failure: the URL looks fine, but the value never arrives on a player that only implements the 4.1 macro.

How to actually catch these

Reading macro tables by hand does not scale past a few tags. The cases that bite are the boring ones: a macro in the wrong element, an unencoded URL, a deprecated token nobody noticed. Those are exactly what a linter is good at.

I run tags through vastlint, an open VAST validator that flags unknown macros, lowercase casing mistakes, missing percent-encoding, deprecated tokens, and context violations (an [ERRORCODE] outside <Error>, a [REASON] outside verificationNotExecuted). The full per-macro reference, with the value each one resolves to and where it is valid, lives at vastlint.org/docs/vast-macros.

If you maintain VAST tags, bookmark the macro index and wire the validator into CI. The discrepancies you prevent are the ones you never have to explain on a reconciliation call.

Top comments (0)