Fix Microsoft Advertising Scripts: Setup & Common Errors
Why This Is Happening
You've decided to automate your Microsoft Advertising campaigns , great move. You open the Scripts editor, paste in some JavaScript, hit Run, and... nothing works the way you expected. Maybe the script runs but pulls zero results. Maybe your withCondition() filter silently ignores your criteria. Maybe you're staring at a blank Logs panel wondering if anything even executed. I've seen this exact scenario play out dozens of times, and the frustrating part is that Microsoft Advertising Scripts errors almost never give you a useful message to work with.
Here's the core issue: Microsoft Advertising Scripts sits at the intersection of two complex systems , the Microsoft Advertising platform itself and a JavaScript execution environment that has its own quirks and limitations. When something breaks, it could be your JavaScript syntax, a misunderstood selector filter, a missing date range on a performance data query, wrong account context for multi-account scripts, or simply that you're trying to run something that preview mode doesn't actually support. The error messaging often doesn't tell you which one.
The most common pain points I see users run into with Microsoft Advertising Scripts setup and configuration are:
- Blank iterator results, your
AdsApp.keywords()orAdsApp.adGroups()selector returns an empty iterator even though the data clearly exists in the account - withCondition() not filtering correctly, the condition syntax looks right but the wrong entities come back, or none at all
- forDateRange() missing data, performance stats return zeros because the date range wasn't specified before calling
getStats() - Preview mode confusion, users think preview ran the script and made changes, or conversely, they don't understand why changes they expected aren't showing up
- Multi-account script failures, scripts that work fine on a single account throw errors or return wrong data when run from a manager account
- Builder object errors, the
build()method returns an operation object but the entity doesn't appear in the account, and the error is swallowed silently
I know this is frustrating, especially when it blocks time-sensitive campaign work. The good news is that nearly all of these issues have clear, repeatable fixes once you understand how the AdsApp object model actually works. This guide walks through every one of them. Browse all Microsoft fix guides →
The Quick Fix, Try This First
If your Microsoft Advertising Scripts aren't returning data or are throwing an error you can't decipher, the fastest diagnostic is to run a dead-simple baseline script first. This isolates whether the problem is with your specific script logic or with the Scripts environment itself.
Here's what you do. Open the Scripts editor (Tools > Scripts from the global top menu), create a new script, and paste in exactly this:
function main() {
var iterator = AdsApp.adGroups().get();
Logger.log("Ad group count: " + iterator.totalNumEntities());
}
Hit Preview, not "Run script now." Check the Logs tab after it finishes. If you see a real number (even zero if your account is empty), the Scripts environment is working and your original issue is in your script logic. If you see nothing in Logs, or an authentication/permission error, you've got an account access problem that needs fixing at the account level first.
Nine times out of ten, this baseline check points straight to the real problem. Either the environment is healthy (and your script has a logic bug) or it's broken (and you need to check account permissions and login context). Don't spend an hour debugging your withCondition() filter when the issue is actually that you're signed into the wrong account.
Once you confirm the environment works, the next most common quick win is adding a date range to any selector that uses performance columns. If your script filters on Impressions, Ctr, Clicks, or any other stat, you must chain a .forDateRange() call onto the selector or the query will silently fail. This single missing line accounts for a huge percentage of "my Microsoft Advertising Scripts aren't working" reports.
This sounds obvious, but getting into the Scripts editor the right way matters, especially if you're managing multiple accounts. I've seen people waste an hour debugging a script that was running against the wrong account entirely.
Here's the exact navigation path: sign in at ads.microsoft.com, then look at the global navigation bar running across the very top of the page. Click Tools, then select Scripts from the dropdown. You should land on the Scripts management page where you'll see a list of any existing scripts and a Create script button in the upper right.
If you're working with a single advertiser account, you're in the right place. Click Create script, click on the default "Untitled script" text at the top of the editor panel, and rename it to something meaningful before you do anything else. This saves confusion later when you have multiple scripts and can't remember which one does what.
If you need to run scripts across multiple accounts you manage, this is critical: you must access the Scripts editor from a manager account (previously called a Super Admin or agency account). If you navigate to Scripts while inside a sub-account, you'll only ever be able to access that one account's data. The multi-account AccountsApp object is only available when you're operating from a manager account context. Check the account name shown in the top-left corner of the interface, it should show your manager account name, not an individual advertiser name.
After creating your script, you'll see a code editor panel on the left and a Logs/Preview panel on the right. The two buttons you'll use most are Preview and Run script now. If everything is set up correctly, clicking Preview will execute the script in a read-only simulation mode and output results to the Logs tab, without modifying any live campaign data.
One of the biggest sources of confusion with Microsoft Advertising Scripts is misunderstanding what Preview actually does, and doesn't do. Let me clear this up because it directly affects whether your troubleshooting gives you accurate feedback.
Preview mode executes your script's read operations and logs output exactly as it would in a live run. So if your script calls AdsApp.keywords() and logs keyword data, Preview will show you that data accurately. This is genuinely useful for testing data retrieval logic without risk.
Here's the catch: write operations behave differently in Preview. If your script calls build() to create an ad group, or changes a bid, or pauses a campaign, Preview will simulate running those operations and report success in the logs, but the actual changes are not committed to your account. This is intentional and protective, but it means you can't fully verify that a write operation works until you run it for real.
Microsoft's documentation is explicit that preview mode has limitations for scripts that modify data. Always run write-heavy scripts in Preview first to catch JavaScript errors and logic bugs, then switch to Run script now for the live execution. Never go straight to a live run on a write script you haven't tested.
To check script output after either mode: click the Logs tab in the right panel. Anything you sent to Logger.log() in your code appears here. If the Logs tab is empty after a run, it means either your script has a syntax error that prevented execution, or your script ran but had no Logger.log() calls, which is a common mistake when adapting sample code. Add a simple Logger.log("Script started"); as the very first line of your main() function to confirm execution every time.
The AdsApp object is the entry point for everything in Microsoft Advertising Scripts single-account access. Every entity type, campaigns, ad groups, keywords, ads, has a corresponding selector method on AdsApp. Calling AdsApp.adGroups() gives you a selector for all ad groups; AdsApp.keywords() gives you all keywords; and so on.
The selector itself doesn't fetch data. It just defines what you want. You chain filter methods onto it (withCondition(), withIds(), forDateRange()) and then call .get() at the end to actually retrieve an iterator. This distinction trips up a lot of people. The selector is the query definition; the iterator is the result set. Don't call .get() until you've specified all your filters.
For withCondition() errors, the most common mistake is mismatching the column name or operator. The condition string uses specific column names that don't always match what you see in the UI. For example, filtering ad groups by campaign name uses CampaignName, not Campaign or campaign_name. Here's the correct pattern:
var campaignName = 'My Campaign';
var iterator = AdsApp.adGroups()
.withCondition(`CampaignName = '${campaignName}'`)
.get();
If you're chaining multiple conditions, each one is added as an independent .withCondition() call. Multiple conditions are treated as AND logic, only entities matching all conditions are returned. If you accidentally put two conditions in one string separated by AND, you'll get unpredictable results. Keep them separate:
var iterator = AdsApp.adGroups()
.withCondition(`CampaignName = '${campaignName}'`)
.withCondition(`Name = '${adGroupName}'`)
.get();
If you want to get a specific entity by its numeric ID rather than filtering by name, use .withIds() instead. Pass IDs as strings inside an array. This is faster and more reliable than name-based filtering when you have the ID available.
Once your selector is configured correctly, calling .get() returns an iterator. The iterator is how you actually loop through and work with your entities. The pattern is always the same:
while (iterator.hasNext()) {
var adGroup = iterator.next();
// work with adGroup here
}
hasNext() returns true if there's another entity to retrieve; next() advances the cursor and returns the entity object. If your while loop never executes, your selector returned zero results, which means your filter conditions eliminated everything, or the date range is missing, or you're looking at an account with no matching entities.
The most painful iterator issue is getting performance stats. When you call adGroup.getStats() inside the loop and see zeros for everything, you've almost certainly forgotten to chain .forDateRange() onto the selector before calling .get(). The date range must be declared at the selector level, not after the fact. Here's the correct structure:
var iterator = AdsApp.adGroups()
.forDateRange('LAST_30_DAYS')
.withCondition(`CampaignName = '${campaignName}'`)
.get();
while (iterator.hasNext()) {
var adGroup = iterator.next();
var stats = adGroup.getStats();
Logger.log(adGroup.getName() + ": " + stats.getImpressions() + " impressions");
}
You can use predefined string literals like 'LAST_WEEK', 'LAST_MONTH', 'LAST_30_DAYS', or 'YESTERDAY' for the date range. If you need a custom range, pass an object with year, month, and day properties for both start and end dates. Month is numeric (6 = June) and there's no zero-padding needed. This is also required any time your withCondition() filter references a performance column like Ctr, Clicks, or Cost, leaving out the date range when filtering on performance data will cause silent failures.
Another iterator gotcha: the .withLimit() method caps how many entities the iterator returns. The sample code in Microsoft's Getting Started docs uses .withLimit(15) to get 15 keywords. If you're building production scripts and copying that sample, remove or adjust the limit, otherwise you'll wonder why your script only processes a fraction of your account's entities.
Adding new entities through Microsoft Advertising Scripts, new ad groups, keywords, ads, uses a builder pattern that's different from the selector/iterator flow you use for reading data. Getting this wrong is a common source of "my script ran but nothing changed" complaints.
The builder lives on the parent object. To add an ad group, you first need a campaign object, then call .newAdGroupBuilder() on it. You can't call a builder from AdsApp directly. Here's the sequence: get the campaign through a selector, then build the child entity:
var campaignIterator = AdsApp.campaigns()
.withCondition(`Name = 'My Campaign'`)
.get();
if (campaignIterator.hasNext()) {
var campaign = campaignIterator.next();
var adGroupOperation = campaign.newAdGroupBuilder()
.withName("My New Ad Group")
.withStatus('ENABLED')
.withBiddingStrategy('MANUAL_CPC')
.withCpc(1.5)
.build();
}
The .build() method returns an operation object, not the entity itself. This is a common misunderstanding. To check whether the entity was actually created successfully, and to get the newly created entity back for further use, you need to call methods on the operation object:
if (adGroupOperation.isSuccessful()) {
var newAdGroup = adGroupOperation.getResult();
Logger.log("Created: " + newAdGroup.getName());
} else {
var errors = adGroupOperation.getErrors();
Logger.log("Failed: " + errors);
}
If you skip checking isSuccessful(), a failed build silently does nothing and your script logs no error. This is exactly why "I ran the script and nothing happened" is so common, the build failed, but without error checking in place, you never find out. Always wrap build operations in a success check. In Preview mode, isSuccessful() will return true even though no entity was committed, so you must use Run script now for final verification that a builder operation actually works end-to-end in your live account.
Advanced Troubleshooting
Multi-Account Scripts and the AccountsApp Object
If you manage multiple advertiser accounts and need your scripts to operate across all of them, you need to be running from a manager account and using the AccountsApp object instead of going straight to AdsApp. This is one of the more confusing architectural aspects of Microsoft Advertising Scripts for agency users and enterprise advertisers managing large account portfolios.
The basic pattern: use AccountsApp to get a list of managed accounts, then for each account, switch context to that account and use AdsApp to access its entities. Here's the structure that most multi-account scripts follow:
function main() {
var accountIterator = AccountsApp.accounts().get();
while (accountIterator.hasNext()) {
var account = accountIterator.next();
AdsManagerApp.select(account);
processAccount(account);
}
}
function processAccount(account) {
var iterator = AdsApp.campaigns().get();
Logger.log(account.getName() + ": " + iterator.totalNumEntities() + " campaigns");
}
The most common error here is running this structure from inside a single advertiser account rather than a manager account. If you do that, AccountsApp won't be available and you'll get a reference error. Double-check the account context you're signed into before running multi-account scripts.
Debugging Silent Failures with Verbose Logging
Microsoft Advertising Scripts doesn't throw exceptions the way traditional JavaScript environments do. Many errors, selector failures, build failures, permission issues, fail silently and return empty results. Your best tool is aggressive Logger.log() instrumentation. Log the result of iterator.totalNumEntities() immediately after every .get() call. Log the result of every isSuccessful() check. Log input variables before they're used in condition strings to catch interpolation bugs. The Logs tab can handle a lot of output, and temporary verbose logging during development saves enormous amounts of debugging time.
JavaScript Execution Environment Limits
Scripts runs in a sandboxed JavaScript environment with specific execution time limits. Long-running scripts that iterate over thousands of entities can hit these limits and be killed mid-execution. If your script processes large accounts and stops unexpectedly without a clear error, execution timeout is the likely cause. Break large processing jobs into smaller chunks using .withLimit() combined with saved state via PropertiesService if available, or redesign to process one campaign or ad group at a time rather than all entities at once.
AdsApp.adGroups().get() check described in the Quick Fix section) returns an authentication error, a permission denied message, or fails to execute entirely despite correct account navigation, you're looking at an account-level access issue that's outside the scope of script debugging. Similarly, if you're seeing data discrepancies between what your script returns and what the Microsoft Advertising web UI shows for the same date ranges and entities, that may indicate a platform-level data sync issue. In both cases, escalate directly to Microsoft Support, these aren't problems you can fix by adjusting your script code.
Prevention & Best Practices
Most Microsoft Advertising Scripts problems I see are preventable. Once you've burned an afternoon debugging a silent selector failure or a builder operation that did nothing, you start building habits that protect you from repeat encounters. Here's what actually works in practice.
Always run new scripts in Preview mode before committing a live run, particularly for any script that creates, modifies, or pauses entities. Preview catches JavaScript syntax errors, logic bugs, and selector misconfigurations before they touch real campaign data. The only thing Preview can't fully validate are write operations, so follow up a successful Preview with a small-scale live test (using .withLimit(1) to process just one entity) before going full scale.
Name your scripts with the account name, function, and version, something like AccountName_PauseLowImpressionKeywords_v2. When you have 20 scripts and need to find the one doing keyword management for a specific client, vague names like "Keyword Script" waste time. The Scripts management page shows all scripts in a flat list with no folder structure, so descriptive naming is your only organizational tool.
Keep scripts focused on one task. A script that pauses keywords, adjusts bids, and generates a report in one main() function is hard to debug and risky to run when only one part needs updating. Separate these into distinct scripts. Smaller, single-purpose scripts are easier to test, easier to schedule independently, and much easier to troubleshoot when something goes wrong.
Test your date range and condition logic against a campaign you know well, one where you already know the exact entity counts, impression numbers, and keyword list. If your selector returns the wrong count for a known campaign, you can immediately pinpoint whether the issue is in the condition string, the date range, or the account context. Using a familiar control dataset as your test baseline is the fastest way to validate script logic before deploying it account-wide.
- Add
Logger.log("Script started, " + new Date());as the very first line of everymain()function so you always know the script actually ran - Always call
iterator.totalNumEntities()after.get()and log the count, zero means your selector filtered out everything and saves you from looping over nothing - Wrap every
.build()call in anisSuccessful()check and log errors explicitly, never assume a build worked - Keep a personal library of tested, working selector patterns (date range filters, withCondition combinations) so you're not rewriting from scratch each time, reuse what already works
Frequently Asked Questions
How do I access Microsoft Advertising Scripts in the web interface?
Sign in to your Microsoft Advertising account, then click Tools in the global navigation bar at the top of the page, and select Scripts from the dropdown menu. From there, click Create script to open the code editor. You'll need to be signed in to an account that has appropriate access, if you're managing multiple advertiser accounts, sign in through a manager account to unlock multi-account script capabilities. The Scripts editor is entirely browser-based, so there's nothing to install locally.
Why is my AdsApp selector returning zero results even though I have campaigns and keywords?
The two most common causes are a withCondition() filter that doesn't match anything (often due to a typo in a campaign name or using the wrong column name) and a missing .forDateRange() call when you're filtering on performance metrics like impressions or CTR. Start by removing all conditions and just calling AdsApp.campaigns().get() to confirm the environment can read your account at all. Then add your filters one at a time and check iterator.totalNumEntities() after each addition to identify which condition is eliminating your results. Also verify you're signed into the correct account, running a script in the wrong account context is a surprisingly common reason for empty results.
What's the difference between Preview mode and Run script now in Microsoft Advertising Scripts?
Preview executes your script's read operations and outputs log data exactly as a live run would, but any write operations (creating entities, changing bids, pausing campaigns) are simulated, they're not actually committed to your account. This makes Preview safe for testing without risk to live campaign data. "Run script now" executes everything for real, including writes. Always use Preview first to catch JavaScript errors and verify your data retrieval logic, then switch to a live run once you're confident the script does what you expect. For write-heavy scripts, consider running a live version with .withLimit(1) on a test campaign before executing at full scale.
How do I get performance data like impressions and CTR for my ad groups?
You need to specify a date range on the selector before calling .get(), then call .getStats() on each entity inside the iterator loop. The date range can be a predefined string literal like 'LAST_30_DAYS', 'LAST_WEEK', or 'YESTERDAY', or a custom range using objects with year, month, and day fields. The common mistake is calling .forDateRange() after .get(), or not calling it at all, both result in getStats() returning zeros for everything. If you're also filtering with withCondition() on a performance column, the date range is mandatory or the condition is simply ignored.
How do I run Microsoft Advertising Scripts across multiple accounts I manage?
You need to access the Scripts editor from a manager account, not from inside an individual advertiser account. Once you're in the manager account context, you use the AccountsApp object to get an iterator of managed accounts, then call AdsManagerApp.select(account) to switch context to each account before using AdsApp to access that account's entities. If you navigate to Scripts while inside a sub-account, the AccountsApp object won't be available and any script relying on it will fail. Check the account name in the top-left corner of the Microsoft Advertising interface to confirm you're operating from the manager level.
My script creates an ad group with build() but nothing shows up in the account, what went wrong?
The .build() method returns an operation object, and the entity is only created if the operation succeeds. If you're not checking adGroupOperation.isSuccessful() after the build, you'll never know if it failed, the error is silently swallowed. Add an explicit check: call isSuccessful(), and if it returns false, log the result of getErrors() to see why it failed. Common reasons include invalid property values (for example, a bidding strategy that requires a specific CPC value you didn't provide), a campaign name that doesn't match any campaign (so the parent object was never found), or running the build in Preview mode, which simulates success but doesn't commit the entity. Verify all three before assuming the platform has a bug.