{"id":99608,"date":"2025-02-20T14:44:07","date_gmt":"2025-02-20T14:44:07","guid":{"rendered":"https:\/\/peraltafinancing.com\/analytics\/diy-google-ads-account-monitor-with-r-adwords-api-and-slack-api\/"},"modified":"2025-02-20T14:44:07","modified_gmt":"2025-02-20T14:44:07","slug":"diy-google-ads-account-monitor-with-r-adwords-api-and-slack-api","status":"publish","type":"post","link":"https:\/\/fivemor.com\/?p=99608","title":{"rendered":"DIY Google Ads Account Monitor With R, AdWords API, And Slack API"},"content":{"rendered":"<p> <br \/>\n<\/p>\n<div>\n<p>Every now and then I\u2019m fortunate enough to be able to publish <a href=\"https:\/\/www.simoahava.com\/tags\/guest-post\/\">guest posts<\/a> by illustrious people in the analytics and digital marketing industries. This time, I get to work with an old colleague of mine who\u2019s a veritable wizard when it comes to building solutions that make our daily work in the digital industry so much smoother.<\/p>\n<p><strong>Erik Gr\u00f6nroos<\/strong> works as an analyst in <a href=\"https:\/\/markkinointiakatemia.fi\/\">MarkkinointiAkatemia<\/a>, a Finnish digital customer acquisition agency. Erik\u2019s job is to utilize data and analytics to help grow the agency as well as the companies of the clients he works with.<\/p>\n<p>In this guide, Erik will share with you how to build an application which helps you automate the monitoring of your <a href=\"https:\/\/ads.google.com\/\">Google Ads<\/a> accounts using the <a href=\"https:\/\/www.r-project.org\/\">R programming language<\/a>, the <a href=\"https:\/\/developers.google.com\/adwords\/api\/docs\/guides\/start\">AdWords API<\/a> and the <a href=\"https:\/\/api.slack.com\/\">Slack API<\/a>.<\/p>\n<div style=\"aspect-ratio: 3032 \/ 1514;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2019\/04\/article-header.jpg\" title=\"Google Ads account monitor\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"1514\" width=\"3032\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2019\/04\/article-header.jpg#ZgotmplZ\" alt=\"Google Ads account monitor\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>I\u2019m particularly excited to publish this article, as it\u2019s the first time (if I recall correctly) that Google Ads and R are covered in the blog.<\/p>\n<p>Anyway, as the editor, all the mistake and errors that you can find in the article are 100% <em>mea culpa<\/em>, so let me know in the comments if errors persisted to the final draft. Thank you!<\/p>\n<p>                <span class=\"simmer\"><br \/>\n  <span class=\"close\">X<\/span><\/p>\n<p>\n    <span class=\"fa fa-md fa-bell\"\/><br \/>\n    <strong>The Simmer Newsletter<\/strong>\n  <\/p>\n<p>\n    Subscribe to the <a href=\"https:\/\/www.simoahava.com\/newsletter\/\">Simmer newsletter<\/a> to get the latest news and content from Simo Ahava into your email inbox!\n  <\/p>\n<p>  <\/span><\/p>\n<h2 id=\"introduction\">Introduction<\/h2>\n<p>In this article I\u2019ll show you how to implement an <strong>automated monitoring application<\/strong> for keeping an eye on your Google Ads accounts. Since the nature of the implementation is a Proof of Concept (PoC), I\u2019ll try to keep it simple by using just one practical use case for demonstrating the end-to-end pipeline:<\/p>\n<p><strong>Zero-cost detection in Ads accounts, for monitoring credit card expirations or other issues that prevent your ads from rolling.<\/strong><\/p>\n<p>This implementation utilizes <strong>R<\/strong> and the <strong>Google AdWords API<\/strong> for checking the data, <strong>Slack API<\/strong> for realtime alerting via your Slack bot if something critical is detected, and good ol\u2019 <code>cron<\/code> for scheduling the script.<\/p>\n<p>If you\u2019re a marketing agency, a typical situation is that you have plenty of Google Ads accounts to take care of. When the number of accounts breaches the hundreds or the thousands, manually managing, monitoring, and maintaining them isn\u2019t really an option anymore.<\/p>\n<p>What you can do, of course, is set up Google Ads\u2019 <a href=\"https:\/\/support.google.com\/google-ads\/answer\/1704338?hl=en\">built-in alerts<\/a> one by one for each of your accounts. You could even manage these alerts via the <a href=\"https:\/\/developers.google.com\/google-ads\/api\/reference\/rpc\/\">Google Ads API<\/a> or utilize <a href=\"https:\/\/developers.google.com\/google-ads\/scripts\/\">Google Ads scripts<\/a>. But the problem with these is that they involve email alerts by default. Hey, it\u2019s 2019. Email is probably not the only channel you should use for building automation on.<\/p>\n<p>So, we decided to harness the force of APIs and, as a result, saved the galaxy!<\/p>\n<h2 id=\"how-the-solution-works\">How the solution works<\/h2>\n<p>The script is populated with the list of <strong>Google Ads account IDs<\/strong> we want to monitor. The monitoring system itself queries the <strong>cost data<\/strong> of each ID using the <strong>Google AdWords API<\/strong>, and parses the results for those accounts that have accumulated no cost at all, signalling potential issues with your budgets or your configurations.<\/p>\n<p>When this information has been parsed, the account IDs that were flagged are written to a <strong>database<\/strong> for further analysis, and a notification is dispatched to the <strong>Slack bot<\/strong> so that the team responsible can be immediately notified when problems arise.<\/p>\n<h2 id=\"requirements\">Requirements<\/h2>\n<h2 id=\"about-r\">About R<\/h2>\n<p>R is a statistical programming language that today supports pretty much the same Machine Learning (ML) tricks that Python does. R vs. Python is a long-standing debate, so it really boils down to language preference: which one do you want to use for cool data stuff! Okay, I\u2019ll put my pants back on, hold on.<\/p>\n<p>We don\u2019t need ML in this simple monitoring example, but we do use it for more advanced solutions. That\u2019s why R is the tool of choice for us.<\/p>\n<p>RStudio is an IDE for R, it makes R easier to use. Quote from RStudio\u2019s website:<\/p>\n<blockquote>\n<p>RStudio is an integrated development environment (IDE) for R. It includes a console, syntax-highlighting editor that supports direct code execution, as well as tools for plotting, history, debugging and workspace management.<br \/>\u00a0<\/p>\n<\/blockquote>\n<blockquote>\n<p>RStudio is available in open source and commercial editions and runs on the desktop (Windows, Mac, and Linux) or in a browser connected to RStudio Server or RStudio Server Pro (Debian\/Ubuntu, RedHat\/CentOS, and SUSE Linux).<\/p>\n<\/blockquote>\n<p>If you don\u2019t have RStudio installed yet, grab yours from <a href=\"https:\/\/www.rstudio.com\/products\/rstudio\/download\/\">RStudio downloads<\/a>.<\/p>\n<div style=\"aspect-ratio: 1910 \/ 933;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2019\/04\/r-studio.jpg\" title=\"R Studio\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"933\" width=\"1910\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2019\/04\/r-studio.jpg#ZgotmplZ\" alt=\"R Studio\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>We won\u2019t get into the RStudio installation details in this article. RStudio\u2019s website provides comprehensive documentation and step-by-step how-to\u2019s for that.<\/p>\n<p>Once you have your RStudio setup up and running, let\u2019s script!<\/p>\n<h2 id=\"episode-i-an-r-script\">Episode I: An R script<\/h2>\n<p>We are using <strong>MariaDB<\/strong> to store the Google Ads Customer IDs of our clients, where they can be retrieved from for several other integration and automation purposes we need.<\/p>\n<p>If you\u2019re using some other database server, you need to modify the DB part of the code to suit your needs. R has comprehensive support for a wide variety of databases, perhaps even all of them. Basically, if it\u2019s something to do with data, R can read from it.<\/p>\n<p>If you\u2019re not using a database, you can skip the DB part. Optionally, R can also read the IDs from e.g. Google Sheets or a CSV file. How to do this in R is your homework!<\/p>\n<p>You\u2019re probably thinking \u201cBut why aren\u2019t you reading the IDs directly from AdWords API\u201d?<\/p>\n<p>Well, the unfortunate thing is that <strong>RAdwords<\/strong>, the R AdWords package, implements the Adwords API Reporting Service only, and the Reporting Service does not support querying Google Ads Customer IDs on the Manager Account level (formerly known as <em>My Client Center<\/em> or <em>Google MCC<\/em>). Instead, you\u2019d need access to AdWords API Managed Customer Service to do that.<\/p>\n<p>In our case, this isn\u2019t a problem though, because we want to persist the IDs in our own custom environment and have different classifications and levels for different sort of contracts and clients. This way we are able to do the desired actions to those clients that need them, and we can also add monitoring capabilities to the setup.<\/p>\n<p>If you need to read the IDs directly from the AdWords API, you can access the Managed Customer Service via a newer R package called <a href=\"https:\/\/cran.r-project.org\/web\/packages\/adwordsR\/index.html\">adwordsR<\/a>.<\/p>\n<h3 id=\"now-lets-script-for-real\">Now, let\u2019s script for real!<\/h3>\n<p>Create a new R file in your RStudio with the following content, set up your DB user and pass, and save the file as <code>db_credentials.R<\/code> in <code>\/YOUR_R_USER_ROOT_DIR\/auth\/<\/code>. This is your DB auth file.<\/p>\n<div class=\"highlight\">\n<pre style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-r\" data-lang=\"r\">db_username = <span style=\"color:#a50\">\"YOUR_USERNAME\"<\/span>\ndb_password = <span style=\"color:#a50\">\"YOUR_PASSWORD\"<\/span><\/code><\/pre>\n<\/div>\n<p>Create another file including the following code and save the file as <code>connect_db.R<\/code> in <code>\/YOUR_R_USER_ROOT_DIR\/<\/code>. This is your DB connector.<\/p>\n<div class=\"highlight\">\n<pre style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-R\" data-lang=\"R\"><span style=\"color:#aaa;font-style:italic\"># Include DB credentials from your \/auth\/ dir<\/span>\n<span style=\"color:#0a0\">source<\/span>(<span style=\"color:#a50\">\"~\/auth\/db_credentials.R\"<\/span>)\n\n<span style=\"color:#aaa;font-style:italic\"># Check if the required packages are installed, install if not<\/span>\n<span style=\"color:#0a0\">if<\/span>(!<span style=\"color:#0a0\">require<\/span>(DBI)) <span style=\"color:#0a0\">install.packages<\/span>(<span style=\"color:#a50\">\"DBI\"<\/span>) \n<span style=\"color:#0a0\">if<\/span>(!<span style=\"color:#0a0\">require<\/span>(RMariaDB)) <span style=\"color:#0a0\">install.packages<\/span>(<span style=\"color:#a50\">\"RMariaDB\"<\/span>)\n\n<span style=\"color:#aaa;font-style:italic\"># Load packages<\/span>\n<span style=\"color:#0a0\">library<\/span>(DBI)\n<span style=\"color:#0a0\">library<\/span>(RMariaDB)\n\n<span style=\"color:#aaa;font-style:italic\"># Create a DB connection handle<\/span>\ncon dbConnect(\n  drv = RMariaDB::<span style=\"color:#0a0\">MariaDB<\/span>(),\n  username = db_username,\n  password = db_password,\n  host = <span style=\"color:#a50\">\"YOUR_DB_HOST\"<\/span>,\n  port = <span style=\"color:#099\">3306<\/span>,\n  <span style=\"color:#a50\">\"YOUR_DB_NAME\"<\/span>\n)<\/code><\/pre>\n<\/div>\n<p>And finally the actual Ads R script, include the following code and save it as <code>google-ads-zero-cost-monitor.R<\/code> in <code>\/YOUR_R_USER_ROOT_DIR\/<\/code>.<\/p>\n<div class=\"highlight\">\n<pre style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-r\" data-lang=\"r\"><span style=\"color:#aaa;font-style:italic\"># Init the DB connection<\/span>\n<span style=\"color:#0a0\">source<\/span>(<span style=\"color:#a50\">\"~\/connect_db.R\"<\/span>)\n\n<span style=\"color:#aaa;font-style:italic\"># Check if the required packages are installed, install if not<\/span>\n<span style=\"color:#0a0\">if<\/span>(!<span style=\"color:#0a0\">require<\/span>(RAdwords)) <span style=\"color:#0a0\">install.packages<\/span>(<span style=\"color:#a50\">\"RAdwords\"<\/span>)\n\n<span style=\"color:#aaa;font-style:italic\"># Load packages<\/span>\n<span style=\"color:#0a0\">library<\/span>(RAdwords)\n\n<span style=\"color:#aaa;font-style:italic\"># Define the file path where your AdWords API OAuth RData file should be stored to, we'll need this next<\/span>\ngoogle_ads_auth_file \"~\/auth\/google_ads_auth.RData\"\n\n<span style=\"color:#aaa;font-style:italic\"># Check if the google_auth RData file already exists, create if not<\/span>\n<span style=\"color:#0a0\">if<\/span>(!<span style=\"color:#0a0\">file.exists<\/span>(google_ads_auth_file)) {\n  <span style=\"color:#aaa;font-style:italic\"># Auth steps:<\/span>\n  <span style=\"color:#aaa;font-style:italic\"># 1) You'll need Google API Client ID, Client Secret and AdWords Developer Token here for authenticating, see https:\/\/github.com\/jburkhardt\/RAdwords for details<\/span>\n  <span style=\"color:#aaa;font-style:italic\"># 2) Once you've provided those in your R console when R requests them, you'll get redirected to OAuth screen in your browser<\/span>\n  <span style=\"color:#aaa;font-style:italic\"># 3) Authenticate with your Google Account (the same that has access to your AdWords account)<\/span>\n  <span style=\"color:#aaa;font-style:italic\"># 4) You'll be redirected to a OAuth success screen that provides a final token code and asks you to copy\/paste it to your application (to your R console in this case)<\/span>\n  google_auth doAuth(save = <span style=\"color:#00a\">FALSE<\/span>)\n  \n  <span style=\"color:#aaa;font-style:italic\"># Save the auth credentials, to be used the auth from a file from now on<\/span>\n  <span style=\"color:#0a0\">save<\/span>(google_auth, file = google_ads_auth_file)\n}\n\n<span style=\"color:#aaa;font-style:italic\"># Load Google auth credentials<\/span>\n<span style=\"color:#0a0\">load<\/span>(file = google_ads_auth_file)\n\n<span style=\"color:#aaa;font-style:italic\"># Init date vars<\/span>\ndayBeforeYesterday Sys.Date() - <span style=\"color:#099\">2<\/span>\nyesterday Sys.Date() - <span style=\"color:#099\">1<\/span>\n\n<span style=\"color:#aaa;font-style:italic\"># Fetch Google Ads Customer IDs from your DB<\/span>\nads_accounts dbGetQuery(con, <span style=\"color:#a50\">\"SELECT ads_customer_id FROM ads_accounts\"<\/span>)\n\n<span style=\"color:#aaa;font-style:italic\"># Create a list of the IDs for later looping<\/span>\naccount_list # Alternatively, if you want to skip the DB stuff, you can define your Google Ads Customer IDs manually by uncommenting the following line and listing the IDs here:\n<span style=\"color:#aaa;font-style:italic\"># account_list \n\n<span style=\"color:#aaa;font-style:italic\"># Init AdWords API request<\/span>\n<span style=\"color:#aaa;font-style:italic\"># We're checking the cost data from two previous days<\/span>\nbody statement<\/span>(select = <span style=\"color:#0a0\">c<\/span>(<span style=\"color:#a50\">'Cost'<\/span>),\n                  report = <span style=\"color:#a50\">\"ACCOUNT_PERFORMANCE_REPORT\"<\/span>,\n                  start  = dayBeforeYesterday,\n                  end    = yesterday)\n\n<span style=\"color:#aaa;font-style:italic\"># Init a data frame for collecting the data and and an index variable for keeping track of progressing during the for loop<\/span>\nadsdata data.frame()\nprogressIndex 0\n\n<span style=\"color:#aaa;font-style:italic\"># Get the Ads cost data<\/span>\n<span style=\"color:#0a0\">for <\/span>(account in account_list) {\n\n  <span style=\"color:#aaa;font-style:italic\"># Get cost data<\/span>\n  ads_api_response getData(clientCustomerId = account,\n                              google_auth      = google_auth,\n                              statement        = body)\n\n  <span style=\"color:#aaa;font-style:italic\"># Proceed only if the API doesn't return an error (happens when there's no data to return or when the API fails), this is for dropping off false zero cost alerts<\/span>\n  <span style=\"color:#0a0\">if<\/span>(!<span style=\"color:#0a0\">is.null<\/span>(<span style=\"color:#0a0\">dim<\/span>(ads_api_response))) {\n   \n    <span style=\"color:#aaa;font-style:italic\"># Init empty data frame<\/span>\n    adsdata_temp setNames(<span style=\"color:#0a0\">data.frame<\/span>(<span style=\"color:#0a0\">matrix<\/span>(ncol = <span style=\"color:#099\">2<\/span>, nrow = <span style=\"color:#099\">1<\/span>)), <span style=\"color:#0a0\">c<\/span>(<span style=\"color:#a50\">\"Cost\"<\/span>, <span style=\"color:#a50\">\"Account\"<\/span>))\n    \n    <span style=\"color:#aaa;font-style:italic\"># Include account id<\/span>\n    adsdata_temp$Account # Include cost data\n    <span style=\"color:#aaa;font-style:italic\"># If there's no data, the result is doubled for some reason<\/span>\n    <span style=\"color:#aaa;font-style:italic\"># Fix this by always reading the 1st value only with [1]<\/span>\n    adsdata_temp$Cost # If cost is zero, NA is returned, replace it with zero\n    <span style=\"color:#0a0\">if<\/span>(<span style=\"color:#0a0\">is.na<\/span>(adsdata_temp$Cost)) adsdata_temp$Cost 0\n    \n    <span style=\"color:#aaa;font-style:italic\"># Collect during each iteration<\/span>\n    adsdata rbind(adsdata, adsdata_temp, stringsAsFactors = <span style=\"color:#00a\">FALSE<\/span>)\n  \n  }\n  \n  <span style=\"color:#aaa;font-style:italic\"># Print progress to R console<\/span>\n  progressIndex 1\n  <span style=\"color:#0a0\">cat<\/span>(<span style=\"color:#0a0\">paste<\/span>(<span style=\"color:#a50\">\"Query \"<\/span>, progressIndex, <span style=\"color:#a50\">\" of \"<\/span>, <span style=\"color:#0a0\">NROW<\/span>(account_list), <span style=\"color:#a50\">\" done\\r\\n\"<\/span>, sep = <span style=\"color:#a50\">\"\"<\/span>))\n\n}\n\n<span style=\"color:#aaa;font-style:italic\"># Keep accounts with zero cost only<\/span>\nads_accounts_with_zero_cost subset(adsdata, Cost == <span style=\"color:#099\">0<\/span>)\n\n<span style=\"color:#aaa;font-style:italic\"># Count nbr of alerted accounts<\/span>\nnbr_of_accounts_with_alerts nrow(ads_accounts_with_zero_cost)\n\n<span style=\"color:#aaa;font-style:italic\"># Save all alerts to DB, if there was any<\/span>\n<span style=\"color:#aaa;font-style:italic\"># This assumes you have DB table named \"alerts\" existing with fields: <\/span>\n<span style=\"color:#aaa;font-style:italic\"># ads_customer_id VARCHAR<\/span>\n<span style=\"color:#aaa;font-style:italic\"># category VARCHAR<\/span>\n<span style=\"color:#aaa;font-style:italic\"># status_id INT<\/span>\n<span style=\"color:#aaa;font-style:italic\"># created_at DATETIME<\/span>\n<span style=\"color:#aaa;font-style:italic\"># updated_at DATETIME<\/span>\n\n<span style=\"color:#0a0\">if<\/span>(nbr_of_accounts_with_alerts &gt; <span style=\"color:#099\">0<\/span>) {\n  \n  <span style=\"color:#0a0\">for <\/span>(i in <span style=\"color:#099\">1<\/span>:<span style=\"color:#0a0\">nrow<\/span>(ads_accounts_with_zero_cost)) {\n    row \"ALERT_ZERO_COST\" <span style=\"color:#aaa;font-style:italic\"># For categorizing different types of alerts<\/span>\n    status_id 0 <span style=\"color:#aaa;font-style:italic\"># To be set to eg. 1 when the alert is handled via a custom UI<\/span>\n    \n    <span style=\"color:#aaa;font-style:italic\"># Save each item to db<\/span>\n    query paste(<span style=\"color:#a50\">\"INSERT INTO alerts (\n<\/span><span style=\"color:#a50\">                    ads_customer_id,\n<\/span><span style=\"color:#a50\">                    category,\n<\/span><span style=\"color:#a50\">                    status_id,\n<\/span><span style=\"color:#a50\">                    created_at,\n<\/span><span style=\"color:#a50\">                    updated_at)\n<\/span><span style=\"color:#a50\">                   VALUES (\"<\/span>,\n                    <span style=\"color:#a50\">\"'\"<\/span>, ads_customer_id, <span style=\"color:#a50\">\"', \"<\/span>,\n                    <span style=\"color:#a50\">\"'\"<\/span>, category, <span style=\"color:#a50\">\"', \"<\/span>,\n                    status_id, <span style=\"color:#a50\">\", \"<\/span>,\n                    <span style=\"color:#a50\">\"now(), \"<\/span>,\n                    <span style=\"color:#a50\">\"now())\"<\/span>,\n                   sep = <span style=\"color:#a50\">\"\"<\/span>\n    )\n    <span style=\"color:#0a0\">dbExecute<\/span>(con, query)\n  }\n  \n<span style=\"color:#aaa;font-style:italic\"># If you want to check\/debug what was stored, uncomment and run these lines<\/span>\n<span style=\"color:#aaa;font-style:italic\"># db_alerts \n<span style=\"color:#aaa;font-style:italic\"># View(db_alerts)<\/span>\n  \n}<\/span><\/code><\/pre>\n<\/div>\n<p>Now you have your Ads zero cost monitor script ready and you can move on to setting up your Slack bot.<\/p>\n<h2 id=\"episode-ii-the-slack-bot-strikes-back\">Episode II: The Slack bot strikes back<\/h2>\n<p>Because the guys at Slack are awesome, they have written an excellent guide on how to set up your bot. They even serve you virtual cookies! Slack\u2019s guides are a textbook example about how documentation should be written &#8211; they\u2019re always a pleasure to read and follow! Thanks guys!<\/p>\n<p>So, follow the <a href=\"https:\/\/api.slack.com\/bot-users\">Slack bot users guide<\/a>. The required steps for our alert notifications are:<\/p>\n<ol>\n<li>\n<p>Create your Slack app<\/p>\n<\/li>\n<li>\n<p>Create your bot user<\/p>\n<\/li>\n<li>\n<p>Install your bot to your Slack workspace<\/p>\n<\/li>\n<\/ol>\n<p>Once you manage to get your bot installed into your Slack workspace, you will get a <strong>webhook URL<\/strong> that you\u2019ll need in the following script.<\/p>\n<div style=\"aspect-ratio: 1751 \/ 941;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2019\/04\/slack-bot-token.jpg\" title=\"Webhook URL\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"941\" width=\"1751\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2019\/04\/slack-bot-token.jpg#ZgotmplZ\" alt=\"Webhook URL\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>The bot script part is here:<\/p>\n<div class=\"highlight\">\n<pre style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-R\" data-lang=\"R\"><span style=\"color:#aaa;font-style:italic\">### SLACK NOTIFICATIONS ###<\/span>\n\n<span style=\"color:#aaa;font-style:italic\"># Replace the default value with the webhook URL you got from Slack<\/span>\nwebhook_url \"https:\/\/hooks.slack.com\/services\/12345\"\n\n<span style=\"color:#aaa;font-style:italic\"># Check if the required packages are installed, install if not<\/span>\n<span style=\"color:#0a0\">if<\/span>(!<span style=\"color:#0a0\">require<\/span>(jsonlite)) <span style=\"color:#0a0\">install.packages<\/span>(<span style=\"color:#a50\">\"jsonlite\"<\/span>) \n<span style=\"color:#0a0\">if<\/span>(!<span style=\"color:#0a0\">require<\/span>(httr)) <span style=\"color:#0a0\">install.packages<\/span>(<span style=\"color:#a50\">\"httr\"<\/span>)\n\n<span style=\"color:#aaa;font-style:italic\"># Load packages<\/span>\n<span style=\"color:#0a0\">library<\/span>(jsonlite)\n<span style=\"color:#0a0\">library<\/span>(httr)\n\n<span style=\"color:#aaa;font-style:italic\"># Nbr of accounts for the Slack notification<\/span>\nnbr_of_accounts NROW(account_list)\n\n<span style=\"color:#aaa;font-style:italic\"># Setup the message you want to send to Slack<\/span>\nmsg_desc paste(<span style=\"color:#a50\">\"Number of Google Ads accounts checked: \"<\/span>,\n                  nbr_of_accounts,\n                  sep = <span style=\"color:#a50\">\"\"<\/span>)\n\nmsg_title paste(<span style=\"color:#a50\">\"ALERT - Number of zero cost accounts detected: \"<\/span>,\n                   nbr_of_accounts_with_alerts,\n                   sep = <span style=\"color:#a50\">\"\"<\/span>)\n\n<span style=\"color:#aaa;font-style:italic\"># This places a channel ping if there were occurrences with zero cost, and if not, defaults to empty message<\/span>\n<span style=\"color:#0a0\">if<\/span>(nbr_of_accounts_with_alerts &gt; <span style=\"color:#099\">0<\/span>) {\n  msg_body \"Alerts saved to database! \"\n} else {\n  msg_body \"\"\n}\n\n<span style=\"color:#aaa;font-style:italic\"># Alert to Slack - POST the msg<\/span>\nrequest POST(webhook_url,\n                body = <span style=\"color:#0a0\">paste<\/span>(\n                  <span style=\"color:#a50\">'{\"attachments\": [{'<\/span>,\n                  \n                  <span style=\"color:#a50\">'\"title\": \"'<\/span>,\n                  msg_title,\n                  <span style=\"color:#a50\">'\",'<\/span>,\n                  \n                  <span style=\"color:#a50\">'\"pretext\": \"'<\/span>,\n                  msg_desc,\n                  <span style=\"color:#a50\">'\",'<\/span>,\n                  \n                  <span style=\"color:#a50\">'\"text\": \"'<\/span>,\n                  msg_body,\n                  <span style=\"color:#a50\">'\", \"color\": \"#ee0000\"'<\/span>,\n                  \n                  <span style=\"color:#a50\">'}]}'<\/span>,\n                  \n                  sep = <span style=\"color:#a50\">''<\/span>\n                )\n)<\/code><\/pre>\n<\/div>\n<p>Once you\u2019ve set this up, include the above Slack part into your google-ads-zero-cost-monitor.R script. Your monitoring script is now done!<\/p>\n<p>This is what a sample notification would look like in Slack:<\/p>\n<div style=\"aspect-ratio: 1109 \/ 137;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2019\/04\/slack-alert.jpg\" title=\"Slack alert sample\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"137\" width=\"1109\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2019\/04\/slack-alert.jpg#ZgotmplZ\" alt=\"Slack alert sample\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>Test it carefully in your RStudio and make sure everything is working correctly. After that, you\u2019re ready to move your script to production by scheduling it via <code>cron<\/code>.<\/p>\n<h2 id=\"episode-iii-return-of-the-cron-job\">Episode III: Return of the cron job<\/h2>\n<p>Open <code>crontab<\/code> in your Linux shell for editing:<\/p>\n<p>Add a cron job that suits your needs. Here\u2019s an example syntax with a scheduler that runs the R monitoring script daily at 10 AM.<\/p>\n<div class=\"highlight\">\n<pre style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-bash\" data-lang=\"bash\"><span style=\"color:#aaa;font-style:italic\"># Daily at 10 AM<\/span>\n<span style=\"color:#099\">0<\/span> <span style=\"color:#099\">10<\/span> * * * Rscript \/home\/YOUR_USERNAME\/google-ads-zero-cost-monitor.R<\/code><\/pre>\n<\/div>\n<h2 id=\"summary\">Summary<\/h2>\n<p>You have now created a potentially powerful Google Ads monitoring pipeline that has endless opportunities for extending. Other useful use cases for utilizing the pipeline are for example anomaly detection, high spend tracking, campaign-level stuff, automation of ads placement optimizing, and so forth.<\/p>\n<p>It goes without saying that automation is absolutely necessary in this day and age when it can save you and your team from countless hours of manual labor. By automating trivial processes such as monitoring and anomaly detection, you are freeing up your most valuable resources (people) to work on more ambitious tasks.<\/p>\n<p>As always, we\u2019re looking forward to your comments and questions. Let us know what you think of the solution, if it could be improved, and if you already have extensions in mind!<\/p>\n<\/p><\/div>\n\n","protected":false},"excerpt":{"rendered":"<p>Every now and then I\u2019m fortunate enough to be able to publish guest posts by illustrious people in the analytics and digital marketing industries. This time, I get to work with an old colleague of mine who\u2019s a veritable wizard when it comes to building solutions that make our daily work in the digital industry [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":99609,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[12033],"tags":[3825,14239,43088,13966,2170,1531,3553,11155],"dealstore":[],"offerexpiration":[],"class_list":["post-99608","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-analytics","tag-account","tag-ads","tag-adwords","tag-api","tag-diy","tag-google","tag-monitor","tag-slack"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.4 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>DIY Google Ads Account Monitor With R, AdWords API, And Slack API - Som2ny Network<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/fivemor.com\/?p=99608\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"DIY Google Ads Account Monitor With R, AdWords API, And Slack API - Som2ny Network\" \/>\n<meta property=\"og:description\" content=\"Every now and then I\u2019m fortunate enough to be able to publish guest posts by illustrious people in the analytics and digital marketing industries. This time, I get to work with an old colleague of mine who\u2019s a veritable wizard when it comes to building solutions that make our daily work in the digital industry [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/fivemor.com\/?p=99608\" \/>\n<meta property=\"og:site_name\" content=\"Som2ny Network\" \/>\n<meta property=\"article:published_time\" content=\"2025-02-20T14:44:07+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/02\/article-header-scaled.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"2560\" \/>\n\t<meta property=\"og:image:height\" content=\"1278\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"admin\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"admin\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"12 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/fivemor.com\/?p=99608#article\",\"isPartOf\":{\"@id\":\"https:\/\/fivemor.com\/?p=99608\"},\"author\":{\"name\":\"admin\",\"@id\":\"https:\/\/fivemor.com\/#\/schema\/person\/b85e3c3dc0e1daea076524dc8810c371\"},\"headline\":\"DIY Google Ads Account Monitor With R, AdWords API, And Slack API\",\"datePublished\":\"2025-02-20T14:44:07+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/fivemor.com\/?p=99608\"},\"wordCount\":1520,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/fivemor.com\/#organization\"},\"image\":{\"@id\":\"https:\/\/fivemor.com\/?p=99608#primaryimage\"},\"thumbnailUrl\":\"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/02\/article-header-scaled.jpg\",\"keywords\":[\"Account\",\"ads\",\"AdWords\",\"API\",\"DIY\",\"Google\",\"Monitor\",\"Slack\"],\"articleSection\":[\"Analytics\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/fivemor.com\/?p=99608#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/fivemor.com\/?p=99608\",\"url\":\"https:\/\/fivemor.com\/?p=99608\",\"name\":\"DIY Google Ads Account Monitor With R, AdWords API, And Slack API - Som2ny Network\",\"isPartOf\":{\"@id\":\"https:\/\/fivemor.com\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/fivemor.com\/?p=99608#primaryimage\"},\"image\":{\"@id\":\"https:\/\/fivemor.com\/?p=99608#primaryimage\"},\"thumbnailUrl\":\"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/02\/article-header-scaled.jpg\",\"datePublished\":\"2025-02-20T14:44:07+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/fivemor.com\/?p=99608#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/fivemor.com\/?p=99608\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/fivemor.com\/?p=99608#primaryimage\",\"url\":\"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/02\/article-header-scaled.jpg\",\"contentUrl\":\"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/02\/article-header-scaled.jpg\",\"width\":2560,\"height\":1278},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/fivemor.com\/?p=99608#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/fivemor.com\/?bp_activities=1\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"DIY Google Ads Account Monitor With R, AdWords API, And Slack API\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/fivemor.com\/#website\",\"url\":\"https:\/\/fivemor.com\/\",\"name\":\"Som2ny Network\",\"description\":\"Daily Deals\",\"publisher\":{\"@id\":\"https:\/\/fivemor.com\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/fivemor.com\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/fivemor.com\/#organization\",\"name\":\"Som2ny Network\",\"url\":\"https:\/\/fivemor.com\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/fivemor.com\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/fivemor.com\/wp-content\/uploads\/2026\/07\/4a0953c4-logo-300x86-1.png\",\"contentUrl\":\"https:\/\/fivemor.com\/wp-content\/uploads\/2026\/07\/4a0953c4-logo-300x86-1.png\",\"width\":300,\"height\":86,\"caption\":\"Som2ny Network\"},\"image\":{\"@id\":\"https:\/\/fivemor.com\/#\/schema\/logo\/image\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\/\/fivemor.com\/#\/schema\/person\/b85e3c3dc0e1daea076524dc8810c371\",\"name\":\"admin\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/fivemor.com\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/729ae85bf62b9917e93538db2f2688ca?s=96&r=g&default=https%3A%2F%2Ffivemor.com%2Fwp-content%2Fplugins%2Fbuddypress-first-letter-avatar%2Fimages%2Fdefault%2F96%2Flatin_a.png\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/729ae85bf62b9917e93538db2f2688ca?s=96&r=g&default=https%3A%2F%2Ffivemor.com%2Fwp-content%2Fplugins%2Fbuddypress-first-letter-avatar%2Fimages%2Fdefault%2F96%2Flatin_a.png\",\"caption\":\"admin\"},\"sameAs\":[\"https:\/\/fivemor.com\"],\"url\":\"https:\/\/fivemor.com\/?author=1\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"DIY Google Ads Account Monitor With R, AdWords API, And Slack API - Som2ny Network","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/fivemor.com\/?p=99608","og_locale":"en_US","og_type":"article","og_title":"DIY Google Ads Account Monitor With R, AdWords API, And Slack API - Som2ny Network","og_description":"Every now and then I\u2019m fortunate enough to be able to publish guest posts by illustrious people in the analytics and digital marketing industries. This time, I get to work with an old colleague of mine who\u2019s a veritable wizard when it comes to building solutions that make our daily work in the digital industry [&hellip;]","og_url":"https:\/\/fivemor.com\/?p=99608","og_site_name":"Som2ny Network","article_published_time":"2025-02-20T14:44:07+00:00","og_image":[{"width":2560,"height":1278,"url":"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/02\/article-header-scaled.jpg","type":"image\/jpeg"}],"author":"admin","twitter_card":"summary_large_image","twitter_misc":{"Written by":"admin","Est. reading time":"12 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/fivemor.com\/?p=99608#article","isPartOf":{"@id":"https:\/\/fivemor.com\/?p=99608"},"author":{"name":"admin","@id":"https:\/\/fivemor.com\/#\/schema\/person\/b85e3c3dc0e1daea076524dc8810c371"},"headline":"DIY Google Ads Account Monitor With R, AdWords API, And Slack API","datePublished":"2025-02-20T14:44:07+00:00","mainEntityOfPage":{"@id":"https:\/\/fivemor.com\/?p=99608"},"wordCount":1520,"commentCount":0,"publisher":{"@id":"https:\/\/fivemor.com\/#organization"},"image":{"@id":"https:\/\/fivemor.com\/?p=99608#primaryimage"},"thumbnailUrl":"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/02\/article-header-scaled.jpg","keywords":["Account","ads","AdWords","API","DIY","Google","Monitor","Slack"],"articleSection":["Analytics"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/fivemor.com\/?p=99608#respond"]}]},{"@type":"WebPage","@id":"https:\/\/fivemor.com\/?p=99608","url":"https:\/\/fivemor.com\/?p=99608","name":"DIY Google Ads Account Monitor With R, AdWords API, And Slack API - Som2ny Network","isPartOf":{"@id":"https:\/\/fivemor.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/fivemor.com\/?p=99608#primaryimage"},"image":{"@id":"https:\/\/fivemor.com\/?p=99608#primaryimage"},"thumbnailUrl":"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/02\/article-header-scaled.jpg","datePublished":"2025-02-20T14:44:07+00:00","breadcrumb":{"@id":"https:\/\/fivemor.com\/?p=99608#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/fivemor.com\/?p=99608"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/fivemor.com\/?p=99608#primaryimage","url":"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/02\/article-header-scaled.jpg","contentUrl":"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/02\/article-header-scaled.jpg","width":2560,"height":1278},{"@type":"BreadcrumbList","@id":"https:\/\/fivemor.com\/?p=99608#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/fivemor.com\/?bp_activities=1"},{"@type":"ListItem","position":2,"name":"DIY Google Ads Account Monitor With R, AdWords API, And Slack API"}]},{"@type":"WebSite","@id":"https:\/\/fivemor.com\/#website","url":"https:\/\/fivemor.com\/","name":"Som2ny Network","description":"Daily Deals","publisher":{"@id":"https:\/\/fivemor.com\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/fivemor.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/fivemor.com\/#organization","name":"Som2ny Network","url":"https:\/\/fivemor.com\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/fivemor.com\/#\/schema\/logo\/image\/","url":"https:\/\/fivemor.com\/wp-content\/uploads\/2026\/07\/4a0953c4-logo-300x86-1.png","contentUrl":"https:\/\/fivemor.com\/wp-content\/uploads\/2026\/07\/4a0953c4-logo-300x86-1.png","width":300,"height":86,"caption":"Som2ny Network"},"image":{"@id":"https:\/\/fivemor.com\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/fivemor.com\/#\/schema\/person\/b85e3c3dc0e1daea076524dc8810c371","name":"admin","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/fivemor.com\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/729ae85bf62b9917e93538db2f2688ca?s=96&r=g&default=https%3A%2F%2Ffivemor.com%2Fwp-content%2Fplugins%2Fbuddypress-first-letter-avatar%2Fimages%2Fdefault%2F96%2Flatin_a.png","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/729ae85bf62b9917e93538db2f2688ca?s=96&r=g&default=https%3A%2F%2Ffivemor.com%2Fwp-content%2Fplugins%2Fbuddypress-first-letter-avatar%2Fimages%2Fdefault%2F96%2Flatin_a.png","caption":"admin"},"sameAs":["https:\/\/fivemor.com"],"url":"https:\/\/fivemor.com\/?author=1"}]}},"_links":{"self":[{"href":"https:\/\/fivemor.com\/index.php?rest_route=\/wp\/v2\/posts\/99608","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/fivemor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/fivemor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/fivemor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/fivemor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=99608"}],"version-history":[{"count":0,"href":"https:\/\/fivemor.com\/index.php?rest_route=\/wp\/v2\/posts\/99608\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/fivemor.com\/index.php?rest_route=\/wp\/v2\/media\/99609"}],"wp:attachment":[{"href":"https:\/\/fivemor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=99608"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/fivemor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=99608"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/fivemor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=99608"},{"taxonomy":"dealstore","embeddable":true,"href":"https:\/\/fivemor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fdealstore&post=99608"},{"taxonomy":"offerexpiration","embeddable":true,"href":"https:\/\/fivemor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fofferexpiration&post=99608"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}