vastlint

How to validate VAST XML

VAST (Video Ad Serving Template) is the IAB Tech Lab standard for delivering video ads. A single malformed field — a missing <Impression>, a bad duration format, a deprecated VPAID attribute — is enough to silently kill an ad impression. This guide covers every way to validate a VAST tag before it reaches a player.

Option 1: Online validator (fastest)

The quickest way is to paste your tag into the vastlint web validator. It runs all 108 rules against the IAB VAST 2.0, 3.0, 4.0, 4.1, 4.2, and 4.3 specs and returns a structured report in under a millisecond. Nothing is stored.

  1. Open vastlint.org/validate
  2. Paste your VAST XML or drop an .xml file
  3. Press ⌘↵ or click Validate
  4. Review errors, warnings, and advisory messages — each linked to the rule that fired

Option 2: CLI (for CI/CD pipelines)

Install the vastlint CLI with Homebrew or Cargo and validate any file or stream from the command line. This is the standard approach for validating tags in a build pipeline or pre-flight check.

# Homebrew (macOS / Linux)
brew install aleksUIX/tap/vastlint

# Cargo
cargo install vastlint

# Validate a file
vastlint validate tag.xml

# Validate from stdin (pipe from curl, etc.)
curl -s https://example.com/vast.xml | vastlint validate -

# Exit code 0 = valid, 1 = errors found
# Use --format json for machine-readable output
vastlint validate tag.xml --format json

Option 3: Rust library (in-process, zero overhead)

For ad servers, SSPs, and DSPs that need inline validation at bid time, embed vastlint-core directly. It validates over 14,000 VAST tags per second on a single core with p95 latency under 100µs.

# Cargo.toml
[dependencies]
vastlint-core = "0.1"
use vastlint_core::validate;

let result = validate(vast_xml_bytes);
for issue in &result.issues {
    println!("{}: {} (line {})", issue.severity, issue.message, issue.line);
}

Option 4: Go binding

A Go binding wraps the same Rust core via CGo. Use it in Go-based ad servers without spawning a subprocess.

import vastlint "github.com/aleksUIX/vastlint-go"

result, err := vastlint.Validate(xmlBytes)
if err != nil {
    log.Fatal(err)
}
for _, issue := range result.Issues {
    fmt.Printf("%s: %s\n", issue.Severity, issue.Message)
}

What VAST validation checks

vastlint applies 108 rules derived from the published IAB VAST XSD schemas. Key checks include:

  • Required elements: <Impression>, <Duration>, <MediaFile> (for inline ads), <VASTAdTagURI> (for wrappers)
  • Duration format: must be HH:MM:SS or HH:MM:SS.mmm
  • URL validity: all tracking pixels, media URLs, and click-through URLs
  • Version consistency: elements from VAST 4.x used in a 3.0 tag, or vice versa
  • Deprecated features: VPAID, AdServingId misuse, old delivery attributes
  • Duplicate impressions: multiple <Impression> URIs that are identical
  • SkipOffset: value must be a valid time offset or percentage

Understanding validation results

Every issue has a severity level:

SeverityMeaningAction
ErrorTag violates a MUST requirement in the specFix before serving — likely to cause render failure
WarningTag violates a SHOULD requirement or uses deprecated syntaxFix soon — may cause issues on some players
InfoAdvisory — tag is valid but could be improvedOptional improvement

Next steps