The fastest way to integrate Advalidation. One call to validate a creative, no manual polling, no response parsing.
import { Advalidation } from "advalidation";
const client = new Advalidation({ apiKey: "your-api-key" });
const result = await client.validate({
url: "https://rtr.innovid.com/r1.66f3e735e66ba5.38642747;cb=[timestamp]",
type: "video",
});
console.log(result.passed); // true or false
console.log(result.issues); // number of failed tests
console.log(result.reportUrl); // link to the full visual report
That’s it. The SDK resolves the ad specification, creates a campaign, uploads, polls until done, and returns the result.
Exactly one of url, tag, file, or data must be provided.
// URL (hosted creative, VAST endpoint, ad tag URL)
await client.validate({ url: "https://example.com/ad.html", type: "display" });
// Tag (raw HTML/JavaScript or VAST XML)
await client.validate({ tag: "<script src='https://example.com/ad.js'></script>", type: "display" });
// File (local path, max 16 MB)
await client.validate({ file: "/path/to/video.mp4", type: "video" });
// Data (Buffer or Uint8Array, max 16 MB)
const buffer = await fs.readFile("/path/to/video.mp4");
await client.validate({ data: buffer, fileName: "video.mp4", type: "video" });
Every validation needs an ad specification. Provide exactly one of campaign, spec, or type.
// Use the default ad specification for a type (creates a new campaign)
await client.validate({ url: "https://example.com/ad.html", type: "display" });
// Use a specific ad specification by ID (creates a new campaign)
await client.validate({ url: "https://example.com/ad.html", spec: "123" });
// Upload into an existing campaign (adspec is inherited)
await client.validate({ url: "https://example.com/ad.html", campaign: 12345 });
By default you get a summary: pass/fail, issue count, and report URL.
Pass details: true to fetch the full test breakdown, including individual test results, VAST media files, and variations.
const result = await client.validate({
url: "https://example.com/vast.xml",
type: "video",
details: true,
});
console.log(result.tests); // test results
console.log(result.mediaFiles); // VAST media files with their tests
You can also start with a summary and fetch details later:
const summary = await client.validate({ url: "https://example.com/ad.html", type: "display" });
if (!summary.passed) {
const detailed = await client.getResults(summary.creativeId, { details: true });
if (detailed.status === "finished") {
console.log(detailed.tests);
}
}
getResults() returns a discriminated union — check response.status === "finished" before accessing result fields. Other statuses are "pending", "failed", and "cancelled".
Already have a creative ID from a previous run or the Advalidation UI? Skip the upload and poll:
const response = await client.getResults(creativeId);
if (response.status === "finished") {
console.log(response.passed, response.issues, response.reportUrl);
} else {
console.log(response.status); // "pending", "failed", or "cancelled"
}
| Option | Type | Default | Description |
|---|
name | string | auto | Campaign name. Auto-generated from the input if omitted. |
timeout | number | 300000 | Polling timeout in ms (default 5 minutes). validate() only. |
signal | AbortSignal | - | Cancel an in-progress operation via AbortController. |
verbose | boolean | false | Log progress to stderr (won’t interfere with stdout piping). |
details | boolean | false | Fetch the full test breakdown. validate() and getResults() only. |
validate() bundles upload + polling in a single long-running call. In serverless environments (Vercel, AWS Lambda, Cloudflare Workers) the function may timeout before the scan completes. Use submit() + getResults() to split the workflow across separate requests.
// Request 1: submit the creative (fast -- no polling)
const { creativeId } = await client.submit({
url: "https://example.com/vast.xml",
type: "video",
});
// Store creativeId (database, KV, cookie, query param, etc.)
// Request 2+: poll from separate short-lived requests
const response = await client.getResults(creativeId);
if (response.status === "finished") {
console.log(response.passed, response.issues);
} else if (response.status === "pending") {
// Not done yet -- try again in a few seconds
}
submit() accepts the same creative and targeting options as validate(), minus timeout and details (irrelevant without polling).
All errors extend AdvalidationError for easy catch-all handling, with specific subclasses for granular control.
import { Advalidation, AuthenticationError, InputError, ApiError, TimeoutError } from "advalidation";
try {
const result = await client.validate({ url: "https://example.com/ad.html", type: "display" });
} catch (error) {
if (error instanceof AuthenticationError) {
console.error("Bad API key");
} else if (error instanceof InputError) {
console.error("Invalid input:", error.message);
} else if (error instanceof ApiError) {
console.error(`API error ${error.status}:`, error.body);
} else if (error instanceof TimeoutError) {
console.error("Scan timed out");
}
}
See the SDK README on GitHub for the complete reference, including all options, result shape, verbose output, and more.