This guide walks you through the core API operations: verifying your API key, creating a campaign, uploading a creative, polling for results, and retrieving scan data.
- An API key — contact your account manager to get one.
All examples use the base URL https://app.advalidation.io/v2 and require the X-API-Key header.
const BASE_URL = "https://app.advalidation.io/v2";
const headers = {
Accept: "application/json",
"X-API-Key": "your-api-key-here",
};
Check that your key works by fetching your user profile.
curl https://app.advalidation.io/v2/users/me \
-H "Accept: application/json" \
-H "X-API-Key: your-api-key-here"
const response = await fetch(`${BASE_URL}/users/me`, { headers });
const { data } = await response.json();
console.log(data);
Ad specifications define what tests run against your creatives. Each account has a default specification for display and one for video. If the defaults work for you, you can skip this step and pass adspecId: null when creating a campaign. Otherwise, list the available specifications and note the id of the one you need.
curl https://app.advalidation.io/v2/ad-specifications \
-H "Accept: application/json" \
-H "X-API-Key: your-api-key-here"
const response = await fetch(`${BASE_URL}/ad-specifications`, { headers });
const { data } = await response.json();
console.log(data);
Campaigns group creatives together. Set type to display or video, and pass an adspecId from step 2 or null for the default.
curl -X POST https://app.advalidation.io/v2/campaigns \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key-here" \
-d '{"name": "My first campaign", "type": "display", "adspecId": null}'
const response = await fetch(`${BASE_URL}/campaigns`, {
method: "POST",
headers: { ...headers, "Content-Type": "application/json" },
body: JSON.stringify({
name: "My first campaign",
type: "display",
adspecId: null,
}),
});
const { data } = await response.json();
const campaignId = data.id;
console.log("Campaign ID:", campaignId);
Upload a creative to your campaign. The simplest method sends a URL or HTML tag as a JSON payload. See the file upload guide for other methods including binary and base64.
curl -X POST https://app.advalidation.io/v2/campaigns/CAMPAIGN_ID/creatives \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key-here" \
-d '{"payload": "<iframe src=\"https://labs.advalidation.net/animation/10s/\" style=\"border:0; width:300px; height:250px;\" scrolling=\"no\"></iframe>"}'
const response = await fetch(
`${BASE_URL}/campaigns/${campaignId}/creatives`,
{
method: "POST",
headers: { ...headers, "Content-Type": "application/json" },
body: JSON.stringify({
payload:
'<iframe src="https://labs.advalidation.net/animation/10s/" style="border:0; width:300px; height:250px;" scrolling="no"></iframe>',
}),
}
);
const { data } = await response.json();
const creativeId = data[0].id;
console.log("Creative ID:", creativeId);
Most scans complete within 30 seconds. Poll the creative endpoint until processingStatus is finished. We recommend polling every 20 seconds.
# Repeat until processingStatus is "finished"
curl https://app.advalidation.io/v2/creatives/CREATIVE_ID \
-H "Accept: application/json" \
-H "X-API-Key: your-api-key-here"
async function waitForScan(creativeId) {
while (true) {
const response = await fetch(`${BASE_URL}/creatives/${creativeId}`, {
headers,
});
const { data } = await response.json();
const status = data.latestScanStatus?.processingStatus;
if (status === "finished") {
console.log("Scan finished. Scan ID:", data.latestScanStatus.id);
return data.latestScanStatus.id;
}
if (status !== "queued" && status !== "processing") {
throw new Error(`Unexpected status: ${status}`);
}
await new Promise((resolve) => setTimeout(resolve, 20000));
}
}
const scanId = await waitForScan(creativeId);
Test results live on the scan object, not the creative. Use the scan ID from step 5 to fetch results. For VAST creatives, the scan hierarchy can be more complex (media files, variations). See the data model for the full picture.
curl https://app.advalidation.io/v2/scans/SCAN_ID \
-H "Accept: application/json" \
-H "X-API-Key: your-api-key-here"
const response = await fetch(`${BASE_URL}/scans/${scanId}`, { headers });
const { data } = await response.json();
console.log("Issues found:", data.nbIssues);
console.log("Tests:", data.tests);
The tests array contains each check with its name, value, result (pass, fail, or warn), and detailed attributes. For display and simple video creatives, this single scan contains all test results. For VAST creatives with media files or variations, additional scans exist at deeper levels. See the data model for details.
If the ad specification changes or remote tag assets are updated (e.g. a VAST or HTML tag), you can re-scan without re-uploading. The request body must be an empty JSON object ({}).
curl -X POST https://app.advalidation.io/v2/creatives/CREATIVE_ID/rescan \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key-here" \
-d '{}'
const response = await fetch(`${BASE_URL}/creatives/${creativeId}/rescan`, {
method: "POST",
headers: { ...headers, "Content-Type": "application/json" },
body: JSON.stringify({}),
});
const { data } = await response.json();
console.log("Rescan ID:", data.id);
After triggering a rescan, poll for results using the same approach as step 5. The rescan’s sequenceType will be manual-rescan.