{"id":248,"date":"2025-10-04T00:00:22","date_gmt":"2025-10-04T00:00:22","guid":{"rendered":"https:\/\/shrinkforge.com\/blog\/?p=248"},"modified":"2025-10-04T00:08:45","modified_gmt":"2025-10-04T00:08:45","slug":"google-sheets-url-shortener-template","status":"publish","type":"post","link":"https:\/\/shrinkforge.com\/blog\/google-sheets-url-shortener-template\/","title":{"rendered":"Google Sheets URL Shortener: A No-Code Bulk Workflow With UTM Tracking (Template Inside)"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\"><strong>If you keep shortening links one-by-one, you\u2019ll burn hours you could spend getting traffic.<\/strong> This guide shows you how to set up a <strong>Google Sheets URL shortener<\/strong> that bulk-builds UTMs, calls your ShrinkForge API, and drops clean short links back into the sheet\u2014ready to copy, publish, and track.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It\u2019s a fast, no-code workflow anyone on your team can run. You\u2019ll learn:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The sheet layout (columns) that makes UTM tracking bullet-proof<\/li>\n\n\n\n<li>A tiny Apps Script to auto-shorten in bulk via API<\/li>\n\n\n\n<li>How to prevent duplicates, handle errors, and log issues<\/li>\n\n\n\n<li>Simple ways to double CTR from your short links without guessing<\/li>\n<\/ul>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\"><strong>Who this is for:<\/strong> creators, affiliates, community managers, and media buyers who ship a lot of links and want clean analytics without another SaaS in the middle.<\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">What you\u2019ll have at the end<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>A <strong>Google Sheets template<\/strong> that builds campaign-ready URLs with UTMs<\/li>\n\n\n\n<li>A <strong>\u201cShort URL\u201d<\/strong> column that fills automatically by calling ShrinkForge\u2019s API<\/li>\n\n\n\n<li>A <strong>status<\/strong> column that tells you if something needs fixing (bad URL, missing UTM, rate limit)<\/li>\n\n\n\n<li>One click to re-run and refresh short links any time<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Why Use a Google Sheets URL Shortener for Bulk Link Management?<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Speed &amp; scale:<\/strong> paste 100 URLs, press a button, get 100 short links.<\/li>\n\n\n\n<li><strong>Consistency:<\/strong> columns enforce clean UTMs, which means clear reporting.<\/li>\n\n\n\n<li><strong>Portability:<\/strong> anyone can open the Sheet and run the script\u2014no local setup.<\/li>\n\n\n\n<li><strong>Control:<\/strong> add validations (e.g., source\/medium lists) and protect formulas.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">If you\u2019re optimizing earnings by geography (Tier-1 vs mixed traffic, desktop vs mobile), structured links are how you prove which placements actually pay. Pair this with your <strong><a href=\"\/payout-rates\">payout rates<\/a><\/strong> page to decide which GEOs to chase first.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"683\" src=\"https:\/\/shrinkforge.com\/blog\/wp-content\/uploads\/2025\/10\/google-sheets-url-shortener-workflow-diagram-1024x683.webp\" alt=\"Workflow diagram: Long URL + UTM columns \u2192 Final URL \u2192 ShrinkForge API \u2192 Short URL (four labeled blocks with arrows left to right).\" class=\"wp-image-253\" srcset=\"https:\/\/shrinkforge.com\/blog\/wp-content\/uploads\/2025\/10\/google-sheets-url-shortener-workflow-diagram-1024x683.webp 1024w, https:\/\/shrinkforge.com\/blog\/wp-content\/uploads\/2025\/10\/google-sheets-url-shortener-workflow-diagram-300x200.webp 300w, https:\/\/shrinkforge.com\/blog\/wp-content\/uploads\/2025\/10\/google-sheets-url-shortener-workflow-diagram-768x512.webp 768w, https:\/\/shrinkforge.com\/blog\/wp-content\/uploads\/2025\/10\/google-sheets-url-shortener-workflow-diagram.webp 1536w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Step 1 \u2014 Make the Sheet (the exact columns)<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Open a new Google Sheet and create <strong>Row 1<\/strong> headers exactly like this:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>A<\/th><th>B<\/th><th>C<\/th><th>D<\/th><th>E<\/th><th>F<\/th><th>G<\/th><th>H<\/th><th>I<\/th><th>J<\/th><\/tr><\/thead><tbody><tr><td>Long URL<\/td><td>Title\/Note<\/td><td>Source<\/td><td>Medium<\/td><td>Campaign<\/td><td>Term (opt)<\/td><td>Content (opt)<\/td><td>Final URL (auto)<\/td><td>Short URL (auto)<\/td><td>Status<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>What each column does<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Long URL<\/strong> \u2013 the raw destination (must include <code>https:\/\/<\/code>).<\/li>\n\n\n\n<li><strong>Title\/Note<\/strong> \u2013 human label (video title, post name).<\/li>\n\n\n\n<li><strong>Source \/ Medium \/ Campaign<\/strong> \u2013 your core UTMs (e.g., <code>youtube<\/code>, <code>comment<\/code>, <code>launch-q1<\/code>).<\/li>\n\n\n\n<li><strong>Term \/ Content<\/strong> \u2013 optional UTMs for keywords\/creative tests.<\/li>\n\n\n\n<li><strong>Final URL<\/strong> \u2013 formula that assembles the UTM\u2019d URL.<\/li>\n\n\n\n<li><strong>Short URL<\/strong> \u2013 filled by the script (calls API).<\/li>\n\n\n\n<li><strong>Status<\/strong> \u2013 success\/error message so you can fix fast.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">The UTM formula for \u201cFinal URL\u201d<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">In <strong>H2<\/strong> paste this formula and drag it down:<\/p>\n\n\n\n<div style=\"position:relative\">\n  <button type=\"button\" onclick=\"navigator.clipboard.writeText(document.getElementById('utmFormula').innerText)\"\n          style=\"position:absolute;right:.5rem;top:.5rem;padding:.35rem .6rem;border-radius:.4rem;border:0;background:#0ea5e9;color:#fff;font-weight:700;cursor:pointer\">\n    Copy\n  <\/button>\n  <pre id=\"utmFormula\" style=\"padding:1rem 1rem 1.4rem;background:#0b1020;color:#e7f2ff;border-radius:.6rem;overflow:auto\"><code>=IFERROR(\n  IF(A2=\"\",\"\",\n    A2 &\n    IF(REGEXMATCH(A2,\"[?]\"), \"&\", \"?\") &\n    \"utm_source=\" & ENCODEURL(C2) &\n    \"&utm_medium=\" & ENCODEURL(D2) &\n    \"&utm_campaign=\" & ENCODEURL(E2) &\n    IF(F2<>\"\",\"&utm_term=\" & ENCODEURL(F2),\"\") &\n    IF(G2<>\"\",\"&utm_content=\" & ENCODEURL(G2),\"\")\n  ),\n\"\")\n<\/code><\/pre>\n<\/div>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Data validation (optional but recommended for google sheets url shortener):<\/strong><br>Create dropdown lists for <strong>Source<\/strong> and <strong>Medium<\/strong> (e.g., <code>youtube, reddit, blog, pinterest<\/code> and <code>description, comment, post, dm<\/code>). You\u2019ll get fewer typos and prettier reports.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Step 2 \u2014 Get your API key (one-time)<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">In your ShrinkForge account go to <strong>Dashboard \u2192 API<\/strong> and copy your <strong>API Key<\/strong>. If you don\u2019t see it, check the <strong><a href=\"\/pages\/faq\">FAQ<\/a><\/strong> for instructions on enabling API access.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">You\u2019ll paste this key into the script in the next step. Keep it private.<\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Step 3 \u2014 Add the bulk shortener script (no code experience needed)<\/h2>\n\n\n\n<ol class=\"wp-block-list\">\n<li>In Google Sheets, click <strong>Extensions \u2192 Apps Script<\/strong>.<\/li>\n\n\n\n<li>Delete anything inside <code>Code.gs<\/code> and paste the script below.<\/li>\n\n\n\n<li>Update the <code>API_ENDPOINT<\/code> and <code>API_KEY<\/code> placeholders to match your account (check your dashboard\u2019s API docs for the exact endpoint &amp; params).<\/li>\n\n\n\n<li><strong>Save<\/strong>, then click <strong>Run<\/strong> \u2192 authorize the script (Google will ask you to approve it once).<\/li>\n<\/ol>\n\n\n\n<div style=\"position:relative;margin:14px 0\">\n  <button type=\"button\"\n    onclick=\"(function(btn){\n      var el = document.getElementById('sgfBulkScript');\n      var text = el.innerText || el.textContent;\n      if (navigator.clipboard &#038;&#038; navigator.clipboard.writeText) {\n        navigator.clipboard.writeText(text).then(function(){\n          btn.textContent='Copied!';\n          setTimeout(function(){ btn.textContent='Copy'; }, 1400);\n        }, function(){\n          \/\/ Fallback: select + copy\n          var r=document.createRange(); r.selectNode(el);\n          var s=window.getSelection(); s.removeAllRanges(); s.addRange(r);\n          try{ document.execCommand('copy'); btn.textContent='Copied!'; }\n          catch(e){ btn.textContent='Press Ctrl\/Cmd+C'; }\n          setTimeout(function(){ s.removeAllRanges(); btn.textContent='Copy'; }, 1400);\n        });\n      } else {\n        \/\/ Older browsers fallback\n        var r=document.createRange(); r.selectNode(el);\n        var s=window.getSelection(); s.removeAllRanges(); s.addRange(r);\n        try{ document.execCommand('copy'); btn.textContent='Copied!'; }\n        catch(e){ btn.textContent='Press Ctrl\/Cmd+C'; }\n        setTimeout(function(){ s.removeAllRanges(); btn.textContent='Copy'; }, 1400);\n      }\n    })(this)\"\n    style=\"position:absolute;right:.5rem;top:.5rem;padding:.35rem .6rem;border-radius:.4rem;border:0;background:#0ea5e9;color:#fff;font-weight:700;cursor:pointer\">\n    Copy\n  <\/button>\n\n  <pre id=\"sgfBulkScript\"\n       style=\"padding:1rem 1rem 1.4rem;background:#0b1020;color:#e7f2ff;border-radius:.6rem;overflow:auto;white-space:pre\"><code>\/**\n * ShrinkForge Bulk Shortener for Google Sheets\n * - Reads Long URL + UTM columns\n * - Writes Short URL + Status\n * - Skips rows that already have a Short URL (idempotent)\n * - Adds simple rate-limit backoff + error handling\n *\n * How to use:\n *   - Put data in columns A:G (per the guide)\n *   - Final URL formula in column H\n *   - Short URL will be written to column I\n *   - Status to column J\n *\/\n\nconst API_ENDPOINT = 'https:\/\/YOUR_ENDPOINT_HERE'; \/\/ e.g. https:\/\/shrinkforge.com\/api\/shorten\nconst API_KEY      = 'YOUR_API_KEY_HERE';          \/\/ paste your key\nconst START_ROW    = 2;  \/\/ first data row\nconst COL_FINAL    = 8;  \/\/ H = Final URL\nconst COL_SHORT    = 9;  \/\/ I = Short URL\nconst COL_STATUS   = 10; \/\/ J = Status\n\nfunction ShortenAll() {\n  const sh = SpreadsheetApp.getActiveSheet();\n  const last = sh.getLastRow();\n  if (last &lt; START_ROW) {\n    SpreadsheetApp.getActive().toast('No data found.');\n    return;\n  }\n\n  const range = sh.getRange(START_ROW, 1, last-START_ROW+1, COL_STATUS);\n  const values = range.getValues();\n\n  \/\/ Process each row\n  for (let i = 0; i &lt; values.length; i++) {\n    const rowIdx   = START_ROW + i;\n    const finalUrl = values[i][COL_FINAL-1]; \/\/ zero-based\n    const shortUrl = values[i][COL_SHORT-1];\n\n    \/\/ Skip if no final URL or already shortened\n    if (!finalUrl || shortUrl) continue;\n\n    try {\n      const result = shortenOnce_(finalUrl);\n      \/\/ Expect { short: \"https:\/\/frge.top\/abc123\" } or similar\n      if (result && result.short) {\n        sh.getRange(rowIdx, COL_SHORT).setValue(result.short);\n        sh.getRange(rowIdx, COL_STATUS).setValue('OK');\n      } else {\n        sh.getRange(rowIdx, COL_STATUS).setValue('Unexpected response');\n      }\n    } catch (err) {\n      sh.getRange(rowIdx, COL_STATUS).setValue('ERR: ' + String(err).slice(0, 120));\n      \/\/ small backoff to be gentle with the API\n      Utilities.sleep(350);\n    }\n  }\n\n  SpreadsheetApp.getActive().toast('Shortening complete.');\n}\n\nfunction shortenOnce_(finalUrl) {\n  \/\/ Adjust fields to match your account\u2019s API (query or JSON body).\n  \/\/ The most common pattern is GET with ?api=KEY&amp;url=URL\n  const url = API_ENDPOINT + '?api=' + encodeURIComponent(API_KEY) +\n              '&amp;url=' + encodeURIComponent(finalUrl);\n\n  const res = UrlFetchApp.fetch(url, { method: 'get', muteHttpExceptions: true, contentType: 'application\/json' });\n  const status = res.getResponseCode();\n  const body = res.getContentText();\n\n  if (status &gt;= 400) {\n    throw new Error('HTTP ' + status + ' ' + body);\n  }\n\n  \/\/ Try to parse JSON first; fall back to plain text\n  try {\n    const json = JSON.parse(body);\n    \/\/ Standardize a return shape\n    if (json.short || json.shortlink || json.link) {\n      return { short: json.short || json.shortlink || json.link };\n    }\n    \/\/ Some APIs wrap in \"data\"\n    if (json.data && (json.data.short || json.data.link)) {\n      return { short: json.data.short || json.data.link };\n    }\n  } catch (_) {\n    \/\/ Some APIs return plain text short URL\n    if (\/^https?:\\\/\\\/\/i.test(body.trim())) {\n      return { short: body.trim() };\n    }\n  }\n\n  throw new Error('Parse error: ' + body.slice(0, 160));\n}\n\n\/\/ Optional: menu shortcut\nfunction onOpen(){\n  SpreadsheetApp.getUi().createMenu('ShrinkForge')\n    .addItem('Shorten all', 'ShortenAll')\n    .addToUi();\n}\n<\/code><\/pre>\n<\/div>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Add a menu button (optional):<\/strong> If you want a friendly menu item, add this snippet at the bottom:<\/p>\n\n\n\n<div style=\"position:relative;margin:14px 0\">\n  <button type=\"button\"\n    onclick=\"(function(btn){\n      var el = document.getElementById('sgfOnOpenSnippet');\n      var text = el.innerText || el.textContent;\n      if (navigator.clipboard &#038;&#038; navigator.clipboard.writeText) {\n        navigator.clipboard.writeText(text).then(function(){\n          btn.textContent='Copied!';\n          setTimeout(function(){ btn.textContent='Copy'; }, 1400);\n        }, function(){\n          var r=document.createRange(); r.selectNode(el);\n          var s=window.getSelection(); s.removeAllRanges(); s.addRange(r);\n          try{ document.execCommand('copy'); btn.textContent='Copied!'; }\n          catch(e){ btn.textContent='Press Ctrl\/Cmd+C'; }\n          setTimeout(function(){ s.removeAllRanges(); btn.textContent='Copy'; }, 1400);\n        });\n      } else {\n        var r=document.createRange(); r.selectNode(el);\n        var s=window.getSelection(); s.removeAllRanges(); s.addRange(r);\n        try{ document.execCommand('copy'); btn.textContent='Copied!'; }\n        catch(e){ btn.textContent='Press Ctrl\/Cmd+C'; }\n        setTimeout(function(){ s.removeAllRanges(); btn.textContent='Copy'; }, 1400);\n      }\n    })(this)\"\n    style=\"position:absolute;right:.5rem;top:.5rem;padding:.35rem .6rem;border-radius:.4rem;border:0;background:#0ea5e9;color:#fff;font-weight:700;cursor:pointer\">\n    Copy\n  <\/button>\n\n  <pre id=\"sgfOnOpenSnippet\"\n       style=\"padding:1rem 1rem 1.4rem;background:#0b1020;color:#e7f2ff;border-radius:.6rem;overflow:auto;white-space:pre\"><code>function onOpen(){\n  SpreadsheetApp.getUi()\n    .createMenu('ShrinkForge')\n    .addItem('Shorten all', 'ShortenAll')\n    .addToUi();\n}\n<\/code><\/pre>\n<\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Now you\u2019ll have <strong>ShrinkForge \u2192 Google sheets url shortener<\/strong> <strong>to<\/strong> <strong>shorten all<\/strong> in the Sheets menu.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>New to Apps Script?<\/strong> Read Google\u2019s official guide on <a href=\"https:\/\/support.google.com\/docs\/answer\/7665004\" target=\"_blank\" rel=\"noreferrer noopener\">automating tasks in Google Sheets<\/a><\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"683\" src=\"https:\/\/shrinkforge.com\/blog\/wp-content\/uploads\/2025\/10\/google-sheets-url-shortener-utm-formula-apps-script-1024x683.webp\" alt=\"Side-by-side code card: left shows the Final URL UTM formula in Google Sheets; right shows the Apps Script \u201cShortenAll\u201d function calling the API and writing the Short URL.\" class=\"wp-image-254\" srcset=\"https:\/\/shrinkforge.com\/blog\/wp-content\/uploads\/2025\/10\/google-sheets-url-shortener-utm-formula-apps-script-1024x683.webp 1024w, https:\/\/shrinkforge.com\/blog\/wp-content\/uploads\/2025\/10\/google-sheets-url-shortener-utm-formula-apps-script-300x200.webp 300w, https:\/\/shrinkforge.com\/blog\/wp-content\/uploads\/2025\/10\/google-sheets-url-shortener-utm-formula-apps-script-768x512.webp 768w, https:\/\/shrinkforge.com\/blog\/wp-content\/uploads\/2025\/10\/google-sheets-url-shortener-utm-formula-apps-script.webp 1536w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Step 4 \u2014 Run it (and re-run safely)<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Paste a batch of destinations into <strong>A: Long URL<\/strong> and fill out UTMs.<\/li>\n\n\n\n<li>Check that <strong>H: Final URL<\/strong> is building correctly.<\/li>\n\n\n\n<li>Go to <strong>ShrinkForge \u2192 Shorten all<\/strong> (or <strong>Run \u2192 ShortenAll<\/strong>).<\/li>\n\n\n\n<li>Watch as <strong>I: Short URL<\/strong> fills in; <strong>J: Status<\/strong> should say <code>OK<\/code>.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Re-runs are safe.<\/strong> The script <strong>skips<\/strong> rows that already have a Short URL. If you want to regenerate a link (e.g., you changed a UTM), just clear the old short link in column <strong>I<\/strong> for that row and run again.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Step 5 \u2014 Common errors (and quick fixes)<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>\u201cERR: HTTP 4xx\u201d<\/strong> \u2013 usually an auth\/endpoint issue. Recheck <code>API_ENDPOINT<\/code>, <code>API_KEY<\/code>, and make sure your account has API access.<\/li>\n\n\n\n<li><strong>\u201cUnexpected response \/ Parse error\u201d<\/strong> \u2013 your endpoint returns a different JSON shape; update the property names in the script\u2019s <code>shortenOnce_<\/code> return mapping.<\/li>\n\n\n\n<li><strong>\u201cNo data found\u201d<\/strong> \u2013 you hit run on an empty sheet; paste rows starting at <strong>Row 2<\/strong>.<\/li>\n\n\n\n<li><strong>Blocked URL<\/strong> \u2013 if a destination violates policy, it won\u2019t shorten. Review rules in the <strong><a href=\"\/pages\/faq\">FAQ<\/a><\/strong> and replace the URL.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Step 6 \u2014 QA the links before you ship<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Preview<\/strong> random short links and confirm they resolve to the UTM\u2019d Final URL.<\/li>\n\n\n\n<li><strong>Sort<\/strong> by <strong>Status<\/strong> and fix any <code>ERR:<\/code> rows first.<\/li>\n\n\n\n<li><strong>Spot-check<\/strong> UTMs for casing (<code>utm_source=youtube<\/code> vs <code>YouTube<\/code>\u2014pick one and stick to it).<\/li>\n\n\n\n<li><strong>Protect<\/strong> your UTM columns once you\u2019re confident (Data \u2192 Protect sheets and ranges).<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Advanced extras (when you\u2019re ready)<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">1) Prevent duplicates automatically<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Add a simple de-duplication step: if the <strong>Final URL<\/strong> already exists above, copy the existing <strong>Short URL<\/strong> instead of calling the API. That keeps a single short link (Google sheets URL shortener) per asset across campaigns.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">2) Device or GEO-specific destinations<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Create separate rows for <strong>mobile<\/strong> and <strong>desktop<\/strong> versions of a page, or for different GEO landers, and track performance side-by-side. Once you see which one earns more, standardize your placements. And make the google sheets url shortener as a powerful tool.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">3) Team workflow<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>One tab for <strong>Drafts<\/strong> (writers paste ideas + raw links).<\/li>\n\n\n\n<li>One tab for <strong>Live<\/strong> (approved rows only).<\/li>\n\n\n\n<li>Only the <strong>Live<\/strong> tab gets shortened, so your stats stay clean.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">CTR boosters that actually move the needle<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Your short link is only as good as its placement.<\/strong> These small tweaks tend to lift click-through:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Lead with value<\/strong>: introduce the link with a benefit, not \u201ccheck this out.\u201d<\/li>\n\n\n\n<li><strong>Use pinned placements<\/strong>: in YouTube, pin the top comment; in blogs, above-the-fold link blocks beat footer links every time.<\/li>\n\n\n\n<li><strong>One link per micro-moment<\/strong>: multiple links side-by-side cannibalize clicks\u2014give people one clear action.<\/li>\n\n\n\n<li><strong>Match the promise<\/strong>: the destination headline should repeat the promise you just made in the post or caption.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">If you aren\u2019t sure where to invest next, glance at your GEO performance vs CPM and prioritize traffic that aligns with your best <strong><a href=\"\/payout-rates\">payout rates<\/a><\/strong>.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<hr>\n<section class=\"seo-helper faq-mini\">\n  <h2>Quick Troubleshooting (Google Sheets URL Shortener)<\/h2>\n\n  <details>\n    <summary>My API key says \u201cinvalid\u201d \u2014 what did I miss?<\/summary>\n    <p>Double-check you pasted the live key (not a test key), and that API access is enabled in your account. Make sure there are no leading\/trailing spaces.<\/p>\n  <\/details>\n\n  <details>\n    <summary>I get \u201cParse error\u201d in the Status column.<\/summary>\n    <p>Your endpoint returns a different JSON shape. Update the script\u2019s <code>shortenOnce_<\/code> mapping to read the correct property (e.g., <code>json.link<\/code> or <code>json.data.short<\/code>).<\/p>\n  <\/details>\n\n  <details>\n    <summary>Running the script does nothing.<\/summary>\n    <p>Rows must start at Row 2 and column H (\u201cFinal URL\u201d) must not be empty. Re-authorize the script in <em>Extensions \u2192 Apps Script<\/em> if Google prompts for permissions.<\/p>\n  <\/details>\n\n  <details>\n    <summary>Rate-limited or \u201cHTTP 429\/5xx\u201d errors.<\/summary>\n    <p>Wait a few seconds and run again. The script includes a small backoff; for very large batches, process in chunks (e.g., 200 rows at a time).<\/p>\n  <\/details>\n\n  <details>\n    <summary>The UTM formula breaks when I paste it.<\/summary>\n    <p>Ensure straight quotes (&#8220;) not smart quotes (\u201c \u201d). If your editor altered them, copy from the one-line variant in the post and paste directly into H2.<\/p>\n  <\/details>\n\n  <details>\n    <summary>Short links don\u2019t match my updated UTMs.<\/summary>\n    <p>Clear the old value in the \u201cShort URL\u201d cell for that row, then run the script again. It will regenerate only the cleared rows.<\/p>\n  <\/details>\n<\/section>\n\n\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<script type=\"application\/ld+json\">\n{\n  \"@context\":\"https:\/\/schema.org\",\n  \"@type\":\"FAQPage\",\n  \"mainEntity\":[\n    {\n      \"@type\":\"Question\",\n      \"name\":\"My API key says \u201cinvalid\u201d \u2014 what did I miss?\",\n      \"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Double-check you pasted the live key (not a test key), and that API access is enabled in your account. Make sure there are no leading\/trailing spaces.\"}\n    },\n    {\n      \"@type\":\"Question\",\n      \"name\":\"I get \u201cParse error\u201d in the Status column.\",\n      \"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Your endpoint returns a different JSON shape. Update the script\u2019s shortenOnce_ mapping to read the correct property (e.g., json.link or json.data.short).\"}\n    },\n    {\n      \"@type\":\"Question\",\n      \"name\":\"Running the script does nothing.\",\n      \"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Rows must start at Row 2 and column H (Final URL) must not be empty. Re-authorize the script in Extensions \u2192 Apps Script if Google prompts for permissions.\"}\n    },\n    {\n      \"@type\":\"Question\",\n      \"name\":\"Rate-limited or \u201cHTTP 429\/5xx\u201d errors.\",\n      \"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Wait a few seconds and run again. The script includes a small backoff; for very large batches, process in chunks (e.g., 200 rows at a time).\"}\n    },\n    {\n      \"@type\":\"Question\",\n      \"name\":\"The UTM formula breaks when I paste it.\",\n      \"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Ensure straight quotes (\\\") not smart quotes (\u201c \u201d). If your editor altered them, copy from the one-line variant in the post and paste directly into H2.\"}\n    },\n    {\n      \"@type\":\"Question\",\n      \"name\":\"Short links don\u2019t match my updated UTMs.\",\n      \"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Clear the old value in the Short URL cell for that row, then run the script again. It will regenerate only the cleared rows.\"}\n    }\n  ]\n}\n<\/script>\n\n\n\n\n<h2 class=\"wp-block-heading\">Where to go next<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Now that your short links are clean, <strong>read our step-by-step UTM guide<\/strong> to interpret what the numbers mean and make better placement decisions: <strong><a href=\"\/blog\/url-shortener-tracking-utms-analytics\/\">UTM Tracking &amp; Analytics (Beginner to Pro)<\/a><\/strong>.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Copy-paste checklist<\/h2>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Create the sheet with columns A\u2013J from this guide.<\/li>\n\n\n\n<li>Paste the <strong>Final URL<\/strong> formula in <strong>H2<\/strong> and drag down.<\/li>\n\n\n\n<li>Add the Apps Script (update <code>API_ENDPOINT<\/code> + <code>API_KEY<\/code>).<\/li>\n\n\n\n<li>Run <strong>ShrinkForge \u2192 Shorten all<\/strong> to fill <strong>Short URL<\/strong>.<\/li>\n\n\n\n<li>Fix any <code>ERR:<\/code> in <strong>Status<\/strong> and re-run.<\/li>\n\n\n\n<li>Ship links; track performance; repeat what pays.<\/li>\n<\/ol>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>You\u2019re done.<\/strong> This simple Google Sheets shortener will save you hours every week, keep your UTMs pristine, and make it obvious which channels, devices, and GEOs actually earn. When you\u2019re ready to scale, just paste more rows and click once.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>If you keep shortening links one-by-one, you\u2019ll burn hours you could spend getting traffic. This guide shows you how to set up a Google Sheets URL shortener that bulk-builds UTMs, calls your ShrinkForge API, and drops clean short links back into the sheet\u2014ready to copy, publish, and track. It\u2019s a fast, no-code workflow anyone on [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":252,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_kadence_starter_templates_imported_post":false,"footnotes":""},"categories":[84],"tags":[115,110,114,108,109,112,111,69,113],"class_list":["post-248","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-monetization","tag-api-integration","tag-apps-script","tag-bulk-link-shortening","tag-google-sheets","tag-google-sheets-url-shortener","tag-link-monetization","tag-url-shortener","tag-utm-tracking-2","tag-workflow-automation"],"_links":{"self":[{"href":"https:\/\/shrinkforge.com\/blog\/wp-json\/wp\/v2\/posts\/248","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/shrinkforge.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/shrinkforge.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/shrinkforge.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/shrinkforge.com\/blog\/wp-json\/wp\/v2\/comments?post=248"}],"version-history":[{"count":6,"href":"https:\/\/shrinkforge.com\/blog\/wp-json\/wp\/v2\/posts\/248\/revisions"}],"predecessor-version":[{"id":258,"href":"https:\/\/shrinkforge.com\/blog\/wp-json\/wp\/v2\/posts\/248\/revisions\/258"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/shrinkforge.com\/blog\/wp-json\/wp\/v2\/media\/252"}],"wp:attachment":[{"href":"https:\/\/shrinkforge.com\/blog\/wp-json\/wp\/v2\/media?parent=248"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/shrinkforge.com\/blog\/wp-json\/wp\/v2\/categories?post=248"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/shrinkforge.com\/blog\/wp-json\/wp\/v2\/tags?post=248"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}