// Shotlane for Google Sheets: =IMAGE(SHOTLANE(A2)) shows a live screenshot of the URL in A2. // Extensions → Apps Script → paste this file → set your keys below → Save. No API call happens here: // the function only builds a signed image URL, and Sheets loads it. Each URL is cached for 24 hours, // so a sheet costs 1 credit per URL per day however often it recalculates. const SHOTLANE_PUBLIC_KEY = "pub_..."; // dashboard → Public key const SHOTLANE_SECRET_KEY = "sl_..."; // dashboard → Secret API key (stays in this script, not in the sheet) /** * A signed Shotlane screenshot URL for use with IMAGE(). * @param {string} url The page to capture. * @param {string} options Optional extra parameters, e.g. "device=iphone&full_page=true". * @customfunction */ function SHOTLANE(url, options) { if (!url) return ""; const params = { url: String(url), format: "png", width: "1280", height: "800", hide_cookie_banners: "true" }; String(options || "").split("&").filter(String).forEach(function (pair) { const i = pair.indexOf("="); params[decodeURIComponent(pair.slice(0, i))] = decodeURIComponent(pair.slice(i + 1)); }); const enc = function (s) { // the same encoding as Python's urlencode return encodeURIComponent(s).replace(/%20/g, "+").replace(/[!'()*]/g, function (c) { return "%" + c.charCodeAt(0).toString(16).toUpperCase(); }); }; const query = Object.keys(params).sort().map(function (k) { return enc(k) + "=" + enc(params[k]); }).join("&"); const path = "/v1/screenshot"; const sig = Utilities.computeHmacSha256Signature(path + "?" + query, SHOTLANE_SECRET_KEY) .map(function (b) { return ("0" + (b & 0xff).toString(16)).slice(-2); }).join(""); return "https://shotlane.dev" + path + "?" + query + "&key=" + SHOTLANE_PUBLIC_KEY + "&signature=" + sig; }