This code is a Google Ads script designed to manage...

August 26, 2025 at 08:50 PM

function main() { var propertyId = 1503 var apiKey = 'l6ZqQsR6PV47G3zzsnM2r71HjKGm7VBA3fKvp42J' let nklName = 'AdAi-1503' let nklPennyName = 'AdAi-1503-B' let pennyString = "_adai" // Campaign name filter for Penny Campaigns let nkl = GetNegativeKeywordList(nklName); let nklPenny = GetNegativeKeywordList(nklPennyName); let nklCampaignIterator = AdsApp.campaigns() .withCondition("Status = ENABLED") // conditions AND together // .withCondition("Name CONTAINS_IGNORE_CASE '| US |'") .withCondition("Name DOES_NOT_CONTAIN_IGNORE_CASE '" + pennyString + "'") .get() let nklPennyCampaignIterator = AdsApp.campaigns() // conditions AND together .withCondition("Status = ENABLED") //.withCondition("Name CONTAINS_IGNORE_CASE '| US |'") .withCondition("Name CONTAINS_IGNORE_CASE '" + pennyString + "'") .get() let nklCleanIterator //= nkl.campaigns() //.withCondition("Name DOES_NOT_CONTAIN_IGNORE_CASE '| US |'") //.get() let nklPennyCleanIterator //= pennyNkl.campaigns() //.withCondition("Name DOES_NOT_CONTAIN_IGNORE_CASE '| US |'") //.get() var resp = GetQueries(propertyId, apiKey) // Must have 200 Response or clear NKL if (resp.getResponseCode() === 200) { try { // JSON Must be well formatted or clear NKL var out = JSON.parse(resp.getContentText()) Logger.log(out["doUpdate"]) // doUpdate field must be present or clear NKL (correct json fields) if (out["doUpdate"] === undefined) { Logger.log("error, clearing nkl") ClearNKLs(nkl, nklPenny) } else if (out["doUpdate"]) { ClearNKLs(nkl, nklPenny) ProcessNegativeKeywords(nkl, nklPenny, out["negative"], out["positive"]) } else { Logger.log("no action taken") } ProcessAllQueries(out["queries"]) } catch (e) { Logger.log("error, clearing nkl") ClearNKLs(nkl, nklPenny) } } else { Logger.log("non-200 response, clearing nkl") ClearNKLs(nkl, nklPenny) } AddNegativeListToAllCampaigns(nkl, nklName, nklPenny, nklPennyName, nklCampaignIterator, nklPennyCampaignIterator) CleanNKLCampaigns(nkl, nklCleanIterator, pennyString) CleanPennyNKLCampaigns(nklPenny, nklPennyCleanIterator, pennyString) } // Remove any campaigns that don't match our filters OR are penny campaigns function CleanNKLCampaigns(nkl, nklCleanIterator, pennyString) { if (typeof (nklCleanIterator) != 'undefined') { while (nklCleanIterator.hasNext()) { let campaign = nklCleanIterator.next() campaign.removeNegativeKeywordList(nkl) Logger.log(campaign.getName() + " removed from NKL") } } // effectively an OR condition let cIt = nkl.campaigns() .withCondition("Name CONTAINS_IGNORE_CASE '" + pennyString + "'") .get() while (cIt.hasNext()) { let campaign = cIt.next() campaign.removeNegativeKeywordList(nkl) Logger.log(campaign.getName() + " removed from NKL") } } // Remove any campaigns that are NOT penny campaigns, use commented code for customization function CleanPennyNKLCampaigns(pennyNkl, pennyNklCleanIterator, pennyString) { // let cIt = pennyNkl.campaigns() // .withCondition("Name DOES_NOT_CONTAIN_IGNORE_CASE '| US |'") // .get() // while (pennyNklCleanIterator.hasNext()) { // let campaign = pennyNklCleanIterator.next() // campaign.removeNegativeKeywordList(pennyNkl) // Logger.log(campaign.getName() + " removed from Penny NKL") // } let cIt = pennyNkl.campaigns() .withCondition("Name DOES_NOT_CONTAIN_IGNORE_CASE '" + pennyString + "'") // .withCondition("Name CONTAINS_IGNORE_CASE '| US |'") .get() while (cIt.hasNext()) { let campaign = cIt.next() campaign.removeNegativeKeywordList(pennyNkl) Logger.log(campaign.getName() + " removed from Penny NKL") } } function ClearNKLs(nkl, nklPenny) { RemoveNegativeKeywords(nkl); RemoveNegativeKeywords(nklPenny); } function AddNegativeListToAllCampaigns(nkl, nklName, nklPenny, nklPennyName, nklCampaignIterator, nklPennyCampaignIterator) { // Standard NKL while (nklCampaignIterator.hasNext()) { var campaign = nklCampaignIterator.next(); var list = campaign.negativeKeywordLists().withCondition("Name = '" + nklName + "'").get() if (!list.hasNext()) { campaign.addNegativeKeywordList(nkl) Logger.log(campaign.getName() + " added to NKL") } else { Logger.log(campaign.getName() + " already in NKL") } } // Penny NKL while (nklPennyCampaignIterator.hasNext()) { var campaign = nklPennyCampaignIterator.next(); var list = campaign.negativeKeywordLists().withCondition("Name = '" + nklPennyName + "'").get() if (!list.hasNext()) { campaign.addNegativeKeywordList(nklPenny) Logger.log(campaign.getName() + " added to Penny NKL") } else { Logger.log(campaign.getName() + " already in Penny NKL") } } } function ProcessAllQueries(queries) { for (q in queries) { Logger.log(queries[q]["query"]) var list = GetAdsReport(queries[q]["query"], queries[q]["names"]) PutToS3(JSON.stringify(list), queries[q]["presignedUrl"]) } } function ProcessNegativeKeywords(nkl, nklPenny, newNegativeKeywords, newPositiveKeywords) { Logger.log(newNegativeKeywords) Logger.log(newPositiveKeywords) nkl.addNegativeKeywords(newNegativeKeywords); nklPenny.addNegativeKeywords(newPositiveKeywords); } function GetNegativeKeywordList(nklName) { var iterator = AdsApp.negativeKeywordLists().withCondition("Name = '" + nklName + "'").withLimit(1).get() var negKeywordList while (iterator.hasNext()) { negKeywordList = iterator.next() } return negKeywordList } // Update to remove from penny campaigns also function RemoveNegativeKeywords(list) { var negIterator = list.negativeKeywords().get() Logger.log("Removing existing Negative Keywords in List:") while (negIterator.hasNext()) { kw = negIterator.next(); Logger.log("REMOVING ... " + kw.getMatchType() + " ... " + kw.getText()) kw.remove() } } function GetAdsReport(query, names) { var report = AdsApp.report(query); var list = []; var rows = report.rows(); while (rows.hasNext()) { var row = rows.next(); var obj = {}; for (n in names) { obj[names[n]] = row[names[n]] } list.push(obj) } return list; } function PutToS3(json, url) { outbytes = Utilities.newBlob(json, "application/json") gz = Utilities.gzip(outbytes); var PutOptions = { 'method': 'put', 'muteHttpExceptions': true, 'contentType': 'application/json', 'headers': { 'Content-Encoding': 'gzip', }, 'payload': gz }; var PutResponse = UrlFetchApp.fetch(url, PutOptions); if (PutResponse == "") { Logger.log("Put OK to " + url.substring(0, url.indexOf('?'))); } else { Logger.log('PutToS3Response: ' + PutResponse); } } function GetQueries(propertyId, apiKey) { var payload = { 'method': 'PresignTest', 'propertyId': propertyId } var options = { 'method': 'post', 'headers': { 'x-api-key': apiKey }, 'muteHttpExceptions': true, 'payload': JSON.stringify(payload) } var response = UrlFetchApp.fetch("https://wpbyed3u1b.execute-api.us-west-1.amazonaws.com/prd/v1/ext/adwordsconnect", options); Logger.log(response) return response }

This code is a Google Ads script designed to manage negative keyword lists (NKLs) for advertising campaigns. Here's what it does in a high-level, step-by-step manner:

1. Setup and Initialization

  • The script defines constants like propertyId, apiKey, and names for two negative keyword lists (nklName and nklPennyName).
  • It initializes iterators for campaign lists:
    • nklCampaignIterator: Filters to campaigns that are enabled and do not contain a specific marker string ("_adai") in their names.
    • nklPennyCampaignIterator: Retrieves enabled campaigns whose names include the "pennyString" marker (indicating penny campaigns).

2. Retrieve or Create Negative Keyword Lists (NKLs)

  • Using GetNegativeKeywordList, the script fetches existing negative keyword lists by name or prepares new ones (nkl and nklPenny).

3. API Request to Fetch Data

  • GetQueries() sends an API request with the provided propertyId and apiKey to fetch campaign data and configuration info (e.g., keywords).
  • If the API responds with an HTTP 200 status:
    • It parses the JSON response.
    • If the response indicates doUpdate is true:
      • Clear existing keywords from the negative keyword lists (ClearNKLs).
      • Add new negative and positive keywords (ProcessNegativeKeywords).
    • If the JSON response is malformed or does not have the expected format, the script clears the negative keyword lists.

4. Campaign-Level Negative Keyword Management

  • The script attaches negative keyword lists to campaigns based on their names:
    • nklCampaignIterator: Attaches the standard negative keyword list (nkl) to campaigns not classified as penny campaigns.
    • nklPennyCampaignIterator: Attaches the "penny" negative keyword list (nklPenny) to campaigns classified as penny campaigns.

5. Clean Campaign Associations

  • Removes associations between campaigns and irrelevant negative keyword lists:
    • CleanNKLCampaigns(): Ensures campaigns not targeted for the standard NKL or classified as penny campaigns are disassociated from the nkl.
    • CleanPennyNKLCampaigns(): Ensures only penny campaigns remain linked to nklPenny.

6. Removing Existing Keywords

  • RemoveNegativeKeywords() removes all negative keywords from a given list.

7. Processing Queries

  • ProcessAllQueries() iterates through query results from the API response.
    • For each query, it generates a Google Ads report, converts the data to JSON, and uploads it to the specified endpoint using signed URLs (PutToS3()).

8. Helper Functions

  • PutToS3(): Compresses and uploads JSON reports to an S3-compatible storage endpoint using pre-signed AWS URLs.
  • GetAdsReport(): Runs custom Google Ads reports based on API-returned queries, extracting specific columns into an object format.
  • GetNegativeKeywordList(): Fetches a negative keyword list from Google Ads by name, returning the first match.
  • ClearNKLs(): Deletes all negative keywords from specified negative keyword lists.

Key Functionalities Provided by the Script

  1. Negative Keyword Automation: Handles the creation, removal, and association of negative keyword lists to campaigns based on conditions.
  2. API Integration: Retrieves campaign-specific configurations (e.g., keywords, queries) from an external system via an API.
  3. Campaign Segmentation: Differentiates campaign types (e.g., "penny" campaigns and standard campaigns) using their names.
  4. Data Cleaning: Ensures consistent keyword list associations by clearing or enforcing rules for which campaigns should be linked to which NKLs.
  5. Report Upload: Sends summarized campaign or query data to an S3-compatible endpoint for further analysis or usage.

Purpose

This script appears to be part of a workflow for managing Google Ads campaigns and ensuring compliance with automated keyword rules. It uses external data (via an API) to dynamically update campaign configurations and sends logs or reports to a backend system (likely for auditing or monitoring purposes).

Generate your own explanations
Download our vscode extension
Read other generated explanations

Built by @thebuilderjr
Sponsored by beam analytics
Read our terms and privacy policy
Forked from openai-quickstart-node