[{"data":1,"prerenderedAt":1409},["ShallowReactive",2],{"/blog/how-to-cut-your-firebase-bill-2026/":3,"/blog/how-to-cut-your-firebase-bill-2026/-related":522,"/blog/how-to-cut-your-firebase-bill-2026/-resources":1394},{"id":4,"title":5,"author":6,"body":10,"category":502,"createdAt":503,"description":504,"extension":505,"location":506,"meta":507,"navigation":513,"ogImage":514,"path":515,"readingTime":516,"seo":517,"stem":518,"tags":519,"updatedAt":503,"url":520,"__hash__":521},"blog/blog/how-to-cut-your-firebase-bill-2026.md","How to Cut Your Firebase Bill: A Line-by-Line Cost Audit (2026)",{"name":7,"social":8},"Simon Dziak",{"twitter":9},"@SzymonDziak",{"type":11,"value":12,"toc":483},"minimark",[13,21,30,35,45,73,77,80,118,121,125,132,196,202,206,230,234,237,278,282,285,296,299,303,306,326,330,333,337,340,420,423,427,439,443,448,451,455,458,462,465,469,472,476,479],[14,15,16,20],"p",{},[17,18,19],"strong",{},"Most Firebase bills that keep creeping up have a few specific causes, and you can find them in an afternoon."," Measure what each service actually does, price it at Google's list prices, then rank every possible saving by dollars a month and by the risk your users would notice. Guessing (\"let's turn off min instances everywhere\") usually trades a small saving for slower screens.",[14,22,23,24,29],{},"This is the method App369 uses for its ",[25,26,28],"a",{"href":27},"/fixed-price/#firebase-cost-audit","fixed-price Firebase Cost-Cut Audit",". It is also how Simon audits the production Firebase project behind his own SaaS, Workspace369, which runs about 980 Cloud Run services. On that project, the audit found that roughly 42% of steady-state spend was recoverable. It was a small bill, and not every finding was applied, but the pattern is common.",[31,32,34],"h2",{"id":33},"why-firebase-bills-grow-quietly","Why Firebase bills grow quietly",[14,36,37,38,44],{},"Firebase's pay-as-you-go ",[25,39,43],{"href":40,"rel":41},"https://firebase.google.com/pricing",[42],"nofollow","Blaze plan"," bills each product separately, so there is rarely one obvious line item. The usual sources are:",[46,47,48,55,61,67],"ul",{},[49,50,51,54],"li",{},[17,52,53],{},"2nd gen Cloud Functions run on Cloud Run."," Every function is its own Cloud Run service with its own memory, CPU, concurrency and minimum-instance settings. A project with 100 functions has 100 sets of settings that nobody reviews after launch.",[49,56,57,60],{},[17,58,59],{},"Minimum instances bill while idle."," Keeping an instance warm removes cold starts, but you pay for it around the clock, whether or not anyone calls the function.",[49,62,63,66],{},[17,64,65],{},"Firestore bills per document read."," A screen that listens to a whole collection, or a scheduled job that scans one every few minutes, multiplies reads with no visible symptom.",[49,68,69,72],{},[17,70,71],{},"Background work has no user to complain."," Retry loops, triggers that fire other triggers, and verbose logging run at full speed until the invoice arrives.",[31,74,76],{"id":75},"step-1-measure-before-you-cut","Step 1: Measure before you cut",[14,78,79],{},"Start from data, not settings.",[81,82,83,95,106],"ol",{},[49,84,85,88,89,94],{},[17,86,87],{},"Billing export."," If ",[25,90,93],{"href":91,"rel":92},"https://cloud.google.com/billing/docs/how-to/export-data-bigquery",[42],"Cloud Billing export to BigQuery"," is on, you have actual spend by service and SKU. If it isn't, turn it on now; it only collects data from the day you enable it.",[49,96,97,100,101,105],{},[17,98,99],{},"Usage from Cloud Monitoring."," For each Cloud Run service, pull 7 and 30 days of request count and billable instance time (",[102,103,104],"code",{},"run.googleapis.com/container/billable_instance_time","). For Firestore, pull document reads, writes and deletes.",[49,107,108,111,112,117],{},[17,109,110],{},"List prices."," The ",[25,113,116],{"href":114,"rel":115},"https://cloud.google.com/billing/docs/how-to/get-pricing-information-api",[42],"Cloud Billing Catalog API"," returns the current price of every SKU in your region, so each estimate is priced from Google's own numbers rather than a blog post.",[14,119,120],{},"Multiply usage by price, per service, and you have an estimated monthly bill by service. Sort it. In most projects a handful of services account for most of the money.",[31,122,124],{"id":123},"step-2-always-on-minimum-instances","Step 2: Always-on minimum instances",[14,126,127,128,131],{},"List every service with ",[102,129,130],{},"minInstances"," above zero:",[133,134,139],"pre",{"className":135,"code":136,"language":137,"meta":138,"style":138},"language-bash shiki shiki-themes github-light github-dark","gcloud run services list --format=json \\\n  | jq -r '.[] | [.metadata.name, (.spec.template.metadata.annotations[\"autoscaling.knative.dev/minScale\"] // \"0\")] | @tsv' \\\n  | awk '$2 > 0'\n","bash","",[102,140,141,167,185],{"__ignoreMap":138},[142,143,146,150,154,157,160,164],"span",{"class":144,"line":145},"line",1,[142,147,149],{"class":148},"sScJk","gcloud",[142,151,153],{"class":152},"sZZnC"," run",[142,155,156],{"class":152}," services",[142,158,159],{"class":152}," list",[142,161,163],{"class":162},"sj4cs"," --format=json",[142,165,166],{"class":162}," \\\n",[142,168,170,174,177,180,183],{"class":144,"line":169},2,[142,171,173],{"class":172},"szBVR","  |",[142,175,176],{"class":148}," jq",[142,178,179],{"class":162}," -r",[142,181,182],{"class":152}," '.[] | [.metadata.name, (.spec.template.metadata.annotations[\"autoscaling.knative.dev/minScale\"] // \"0\")] | @tsv'",[142,184,166],{"class":162},[142,186,188,190,193],{"class":144,"line":187},3,[142,189,173],{"class":172},[142,191,192],{"class":148}," awk",[142,194,195],{"class":152}," '$2 > 0'\n",[14,197,198,199,201],{},"For each one, ask whether a user waits on it. A login or checkout function may deserve a warm instance. A nightly report or a webhook that Stripe retries anyway usually doesn't. In code, the setting lives on the function (",[102,200,130],{}," in the Firebase Functions v2 options), so change it there, not only in the console, or the next deploy puts it back.",[31,203,205],{"id":204},"step-3-memory-cpu-and-concurrency","Step 3: Memory, CPU and concurrency",[46,207,208,214,224],{},[49,209,210,213],{},[17,211,212],{},"Memory."," Functions are often deployed at 1 GiB or more \"to be safe\". Compare the configured memory with the memory utilization metric and size down where the peak is far below the limit.",[49,215,216,219,220,223],{},[17,217,218],{},"Concurrency."," 2nd gen functions can serve many requests per instance (80 by default with a full CPU); 1st gen functions serve one. A function forced to ",[102,221,222],{},"concurrency: 1"," scales out an instance per request.",[49,225,226,229],{},[17,227,228],{},"Timeouts."," A long timeout on a function that hangs on a slow upstream keeps a billed instance busy until it gives up.",[31,231,233],{"id":232},"step-4-firestore-reads","Step 4: Firestore reads",[14,235,236],{},"Read costs come from patterns, not single queries:",[46,238,239,245,268],{},[49,240,241,244],{},[17,242,243],{},"Collection listeners on busy screens."," A dashboard that listens to every document in a collection pays for every change, for every open client. Paginate, filter, or listen to a summary document instead.",[49,246,247,250,251,256,257,260,261,260,264,267],{},[17,248,249],{},"Counting by reading."," Use ",[25,252,255],{"href":253,"rel":254},"https://firebase.google.com/docs/firestore/query-data/aggregation-queries",[42],"aggregation queries"," (",[102,258,259],{},"count()",", ",[102,262,263],{},"sum()",[102,265,266],{},"average()",") instead of downloading documents to count them.",[49,269,270,273,274,277],{},[17,271,272],{},"Scheduled scans."," A job that reads a whole collection every five minutes to find a few changed documents should query on an indexed ",[102,275,276],{},"updatedAt"," field instead.",[31,279,281],{"id":280},"step-5-trigger-fan-out-and-retry-storms","Step 5: Trigger fan-out and retry storms",[14,283,284],{},"Event-triggered functions can multiply quietly:",[46,286,287,290,293],{},[49,288,289],{},"A function that writes to the document that triggered it can re-trigger itself.",[49,291,292],{},"One write can fan out to several triggers, each of which writes again.",[49,294,295],{},"A function with retries enabled that fails on the same bad data keeps retrying, for hours or days, and every attempt is billed.",[14,297,298],{},"Look for invocation counts that are far higher than the user actions behind them, and for error rates that never drop.",[31,300,302],{"id":301},"step-6-secrets-logs-and-images","Step 6: Secrets, logs and images",[14,304,305],{},"These are smaller lines, but they add up across many services:",[46,307,308,314,320],{},[49,309,310,313],{},[17,311,312],{},"Secret Manager"," bills for active secret versions and for access operations. Disable or destroy versions you no longer use, and read a secret once per instance rather than once per request.",[49,315,316,319],{},[17,317,318],{},"Cloud Logging"," charges for ingestion beyond the free monthly allotment. Logging full request payloads is expensive, and it can also write customer data into your logs.",[49,321,322,325],{},[17,323,324],{},"Artifact Registry"," stores a container image for every function deploy. A cleanup policy keeps only the recent ones.",[31,327,329],{"id":328},"step-7-ai-and-vertex-ai-usage","Step 7: AI and Vertex AI usage",[14,331,332],{},"If your app calls Gemini or another model, look at tokens per user action, not just total spend. Common fixes are caching repeated prompts, trimming the context you send, and putting a per-user or per-workspace limit on expensive features. If you resell AI features, check that your price covers your cost per call; that is a revenue leak, not a cloud cost, but it shows up in the same audit.",[31,334,336],{"id":335},"rank-the-savings-then-decide","Rank the savings, then decide",[14,338,339],{},"Write every finding in one table and sort it by dollars a month:",[341,342,343,366],"table",{},[344,345,346],"thead",{},[347,348,349,353,357,360,363],"tr",{},[350,351,352],"th",{},"Finding",[350,354,356],{"align":355},"right","$/month",[350,358,359],{},"User-visible risk",[350,361,362],{},"Effort",[350,364,365],{},"How to roll back",[367,368,369,390,406],"tbody",{},[347,370,371,375,378,381,384],{},[372,373,374],"td",{},"Example: min instances on a nightly job",[372,376,377],{"align":355},"measured",[372,379,380],{},"None",[372,382,383],{},"1 line",[372,385,386,387,389],{},"Set ",[102,388,130],{}," back",[347,391,392,395,397,400,403],{},[372,393,394],{},"Example: collection listener on the dashboard",[372,396,377],{"align":355},[372,398,399],{},"Low",[372,401,402],{},"Half a day",[372,404,405],{},"Revert the query",[347,407,408,411,413,415,417],{},[372,409,410],{},"Example: 1 GiB on a 120 MiB function",[372,412,377],{"align":355},[372,414,399],{},[372,416,383],{},[372,418,419],{},"Raise memory again",[14,421,422],{},"Apply the low-risk, high-dollar rows first. Leave anything on a latency-sensitive path until you've watched it in staging.",[31,424,426],{"id":425},"when-to-get-help","When to get help",[14,428,429,430,433,434,438],{},"If the bill matters but nobody on the team has a free week, App369 runs this audit for a ",[25,431,432],{"href":27},"fixed $750",", delivered in 5 business days with read-only, temporary access. You get the bill by service, the ranked savings table, a CSV of every service, and the exact commands to apply each change. Nothing in your project is changed. If you are also worried about who can read what, the ",[25,435,437],{"href":436},"/blog/firebase-security-guide-2026/","Firebase Security Rules guide"," covers the access side.",[31,440,442],{"id":441},"frequently-asked-questions","Frequently Asked Questions",[444,445,447],"h3",{"id":446},"why-is-my-firebase-bill-so-high-when-traffic-is-low","Why is my Firebase bill so high when traffic is low?",[14,449,450],{},"Usually because something runs without traffic: minimum instances kept warm around the clock, scheduled functions that scan collections, or triggers that re-fire. Low traffic with a high bill points to background work or idle capacity, not to users.",[444,452,454],{"id":453},"do-minimum-instances-cost-money-when-nobody-calls-the-function","Do minimum instances cost money when nobody calls the function?",[14,456,457],{},"Yes. A minimum instance is kept running so it can answer without a cold start, and it is billed while idle. Keep minimum instances only on functions where a user is waiting for the response.",[444,459,461],{"id":460},"how-do-i-see-the-cost-of-each-cloud-function","How do I see the cost of each Cloud Function?",[14,463,464],{},"Turn on Cloud Billing export to BigQuery for actual spend by SKU, and use Cloud Monitoring's request count and billable instance time per Cloud Run service. 2nd gen functions appear as Cloud Run services, so each function's usage can be priced separately.",[444,466,468],{"id":467},"will-cutting-costs-make-my-app-slower","Will cutting costs make my app slower?",[14,470,471],{},"Only if you cut on the wrong path. Rank each saving by user-visible risk: background jobs, webhooks and reports can usually scale to zero, while login, checkout and screen-loading functions may need warm instances.",[444,473,475],{"id":474},"can-i-lower-my-firebase-bill-without-changing-code","Can I lower my Firebase bill without changing code?",[14,477,478],{},"Partly. Minimum instances, memory, concurrency and timeouts can be changed in configuration. Firestore read patterns, trigger loops and logging volume usually need code changes, and settings changed only in the console are overwritten by the next deploy if the code says otherwise.",[480,481,482],"style",{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":138,"searchDepth":169,"depth":169,"links":484},[485,486,487,488,489,490,491,492,493,494,495],{"id":33,"depth":169,"text":34},{"id":75,"depth":169,"text":76},{"id":123,"depth":169,"text":124},{"id":204,"depth":169,"text":205},{"id":232,"depth":169,"text":233},{"id":280,"depth":169,"text":281},{"id":301,"depth":169,"text":302},{"id":328,"depth":169,"text":329},{"id":335,"depth":169,"text":336},{"id":425,"depth":169,"text":426},{"id":441,"depth":169,"text":442,"children":496},[497,498,499,500,501],{"id":446,"depth":187,"text":447},{"id":453,"depth":187,"text":454},{"id":460,"depth":187,"text":461},{"id":467,"depth":187,"text":468},{"id":474,"depth":187,"text":475},"AI & Technology","2026-10-12T09:00:00.000Z","A line-by-line method for finding where a Firebase or Google Cloud bill goes: measure usage, price every Cloud Function and Cloud Run service, then rank savings by dollars a month and risk.","md","Boynton Beach, FL",{"readingTimeStats":508},{"text":509,"minutes":510,"time":511,"words":512},"7 min read",6.36,381600,1272,true,null,"/blog/how-to-cut-your-firebase-bill-2026","10 min",{"title":5,"description":504},"blog/how-to-cut-your-firebase-bill-2026","firebase cost, firebase pricing, cloud functions cost, cloud run cost, google cloud cost optimization, firestore reads","https://app369.com/blog/how-to-cut-your-firebase-bill-2026/","OTdhHEgyTym3C7WLBa_O07KZOT70UCwTXVok61lwKa4",[523,847],{"id":524,"title":525,"author":526,"body":528,"category":502,"createdAt":832,"description":833,"extension":505,"location":506,"meta":834,"navigation":513,"ogImage":514,"path":840,"readingTime":841,"seo":842,"stem":843,"tags":844,"updatedAt":832,"url":845,"__hash__":846},"blog/blog/how-to-make-a-saas-launch-video-2026.md","How to Make a SaaS Launch Video From Your Real Product Screens (2026)",{"name":7,"social":527},{"twitter":9},{"type":11,"value":529,"toc":816},[530,536,549,553,556,559,585,588,592,595,613,617,620,646,655,659,685,689,692,753,756,760,763,767,779,781,785,788,792,795,799,802,806,809,813],[14,531,532,535],{},[17,533,534],{},"A good SaaS launch video shows the product doing one valuable thing, fast."," You don't need actors, a studio or 3D animation. You need a tight script, sharp recordings of your real screens, motion that guides the eye, and the right cuts for each place the video will run. Sixty seconds is enough for a homepage hero, a launch post or an ad.",[14,537,538,539,543,544,548],{},"This is the process App369 follows for its ",[25,540,542],{"href":541},"/fixed-price/#saas-launch-video","fixed-price SaaS Launch Video",". Simon makes the product videos and Shorts for his own SaaS, Workspace369, the same way; there is a 30-second reel on the ",[25,545,547],{"href":546},"/fixed-price/#reel","fixed-price page",".",[31,550,552],{"id":551},"step-1-write-the-script-first","Step 1: Write the script first",[14,554,555],{},"The script decides whether the video works; animation can't rescue a vague one. For 60 seconds, plan on roughly 140 words of voiceover, or fewer words on screen if you go captions-only.",[14,557,558],{},"A structure that fits most launches:",[81,560,561,567,573,579],{},[49,562,563,566],{},[17,564,565],{},"The problem, in the viewer's words (5–8 s)."," \"Chasing clients for payment again?\"",[49,568,569,572],{},[17,570,571],{},"The product in one sentence (5 s)."," What it is and who it's for.",[49,574,575,578],{},[17,576,577],{},"Three features, each as an outcome (35–40 s)."," Not \"automated reminders\" but \"clients get a reminder before the invoice is late\".",[49,580,581,584],{},[17,582,583],{},"The call to action (5–8 s)."," One action, the same one as on your page: \"Start free at …\".",[14,586,587],{},"Get the script approved in writing before anyone records or animates anything. Changing a sentence later means re-timing everything after it.",[31,589,591],{"id":590},"step-2-record-the-screens-properly","Step 2: Record the screens properly",[14,593,594],{},"The video can only be as sharp as the recordings.",[46,596,597,600,607,610],{},[49,598,599],{},"Record at the highest resolution you can; a 2× (retina) display gives crisp text after scaling down. Never upscale a small recording.",[49,601,602,603,606],{},"Use ",[17,604,605],{},"test data"," that looks real: believable names, amounts and dates. Never show real customer data.",[49,608,609],{},"Hide everything that isn't the product: bookmarks bar, notifications, other tabs, the cursor where it isn't needed.",[49,611,612],{},"Record each feature as a short separate clip, slower than feels natural. It's easy to speed up in editing; it's impossible to slow down a rushed click.",[31,614,616],{"id":615},"step-3-motion-that-guides-the-eye","Step 3: Motion that guides the eye",[14,618,619],{},"Motion graphics turn screen recordings into something people watch:",[46,621,622,628,634,640],{},[49,623,624,627],{},[17,625,626],{},"Animated UI shots."," Crop into the part of the screen that matters, then zoom or pan to follow the action, so viewers never hunt for the cursor.",[49,629,630,633],{},[17,631,632],{},"Kinetic type."," Short on-screen phrases timed to the voiceover carry the message for people watching without sound.",[49,635,636,639],{},[17,637,638],{},"Clean transitions."," Cut on the beat of the music, and keep transitions consistent. One style, used everywhere, looks deliberate.",[49,641,642,645],{},[17,643,644],{},"Your brand."," Use your colors, fonts and logo, so the video looks like your product and not a template.",[14,647,648,649,654],{},"Code-driven tools such as ",[25,650,653],{"href":651,"rel":652},"https://www.remotion.dev/",[42],"Remotion"," make it practical to keep one source and export several formats from it, which matters in step 5.",[31,656,658],{"id":657},"step-4-sound-voice-and-captions","Step 4: Sound, voice and captions",[46,660,661,667,673,679],{},[49,662,663,666],{},[17,664,665],{},"Music and sound design"," must be licensed for commercial use. Subtle UI sounds on clicks and transitions make screen recordings feel alive.",[49,668,669,672],{},[17,670,671],{},"Voiceover:"," your own recording reads as most authentic for founder-led products. A synthetic voice can work if you approve and license it. Captions-only is a valid choice for feeds where most people watch muted.",[49,674,675,678],{},[17,676,677],{},"Loudness:"," master to around −14 LUFS integrated, a common target for online platforms, so your video isn't quieter or louder than what plays before it.",[49,680,681,684],{},[17,682,683],{},"Captions:"," burn them into vertical cuts, and ship an SRT file for players that support closed captions.",[31,686,688],{"id":687},"step-5-export-the-cuts-youll-actually-use","Step 5: Export the cuts you'll actually use",[14,690,691],{},"One video rarely fits every placement. From the same build, export:",[341,693,694,707],{},[344,695,696],{},[347,697,698,701,704],{},[350,699,700],{},"Cut",[350,702,703],{},"Format",[350,705,706],{},"Where it runs",[367,708,709,720,731,742],{},[347,710,711,714,717],{},[372,712,713],{},"60-second master",[372,715,716],{},"16:9, 1920×1080, H.264",[372,718,719],{},"Homepage, YouTube, launch posts",[347,721,722,725,728],{},[372,723,724],{},"15-second cut",[372,726,727],{},"16:9",[372,729,730],{},"Ads, social posts, email GIF previews",[347,732,733,736,739],{},[372,734,735],{},"Vertical cut with captions",[372,737,738],{},"9:16, 1080×1920",[372,740,741],{},"Shorts, Reels, TikTok, LinkedIn mobile",[347,743,744,747,750],{},[372,745,746],{},"Cover frame",[372,748,749],{},"1280×720 PNG",[372,751,752],{},"YouTube thumbnail, video poster",[14,754,755],{},"For the vertical cut, keep text and key UI out of the bottom and right edges, where platforms overlay buttons and captions.",[31,757,759],{"id":758},"step-6-review-in-two-rounds","Step 6: Review in two rounds",[14,761,762],{},"Plan two rounds of feedback, each in writing: the first on a full rough cut (story, order, timing), the second on the polished cut (details, typos, sound). Collect all comments from your team before sending them; contradictory notes from different people are the most common cause of delays.",[31,764,766],{"id":765},"what-it-costs","What it costs",[14,768,769,770,773,774,778],{},"Freelance and agency prices for a 60-second motion-graphics product video vary widely, depending on how much is custom-animated versus built from screens. App369 makes them at a ",[25,771,772],{"href":541},"fixed $1,500",", in 10 business days: script first, a 60-second 1080p master, a 15-second cut and a vertical cut with captions, licensed music or sound design, a cover frame and two rounds of revisions. If you'd rather have a steady stream of short videos, ",[25,775,777],{"href":776},"/fixed-price/#shorts-as-a-service","Shorts-as-a-Service"," delivers twelve a month.",[31,780,442],{"id":441},[444,782,784],{"id":783},"how-long-should-a-saas-launch-video-be","How long should a SaaS launch video be?",[14,786,787],{},"Sixty seconds works for most homepage and launch uses, with a 15-second cut for ads. Longer walkthroughs belong in your help center or on YouTube, not in the launch video.",[444,789,791],{"id":790},"do-i-need-a-voiceover-for-a-product-video","Do I need a voiceover for a product video?",[14,793,794],{},"No. Many people watch muted in feeds, so on-screen text and captions carry the message either way. A voiceover helps on your homepage and YouTube; your own voice works best for founder-led products.",[444,796,798],{"id":797},"can-i-make-a-launch-video-from-screenshots-instead-of-screen-recordings","Can I make a launch video from screenshots instead of screen recordings?",[14,800,801],{},"Yes. Motion graphics can animate still screenshots convincingly, with zooms, highlights and simulated clicks. High-resolution screenshots are better than a blurry recording.",[444,803,805],{"id":804},"what-resolution-should-a-saas-product-video-be","What resolution should a SaaS product video be?",[14,807,808],{},"Export the master at 1920×1080 (16:9) in H.264, and a 1080×1920 (9:16) version for vertical feeds. Record your screens at a higher resolution than that so text stays sharp.",[444,810,812],{"id":811},"how-do-i-keep-real-customer-data-out-of-my-product-video","How do I keep real customer data out of my product video?",[14,814,815],{},"Record in a test or demo account filled with realistic sample data, and check every frame before publishing. Blurring real data afterwards is error-prone; it is easier to never record it.",{"title":138,"searchDepth":169,"depth":169,"links":817},[818,819,820,821,822,823,824,825],{"id":551,"depth":169,"text":552},{"id":590,"depth":169,"text":591},{"id":615,"depth":169,"text":616},{"id":657,"depth":169,"text":658},{"id":687,"depth":169,"text":688},{"id":758,"depth":169,"text":759},{"id":765,"depth":169,"text":766},{"id":441,"depth":169,"text":442,"children":826},[827,828,829,830,831],{"id":783,"depth":187,"text":784},{"id":790,"depth":187,"text":791},{"id":797,"depth":187,"text":798},{"id":804,"depth":187,"text":805},{"id":811,"depth":187,"text":812},"2026-10-12T12:00:00.000Z","How to make a 60-second SaaS launch video from your real product screens: script, screen capture, motion graphics, sound, captions and the cuts you need for your homepage, YouTube and vertical feeds.",{"readingTimeStats":835},{"text":836,"minutes":837,"time":838,"words":839},"5 min read",4.535,272100,907,"/blog/how-to-make-a-saas-launch-video-2026","8 min",{"title":525,"description":833},"blog/how-to-make-a-saas-launch-video-2026","saas launch video, product video, motion graphics, explainer video, saas marketing video, product demo video","https://app369.com/blog/how-to-make-a-saas-launch-video-2026/","m6GdVIhLILzV6VHwAkAd95B6LSq7Wj-tcZMvY7rAMbA",{"id":848,"title":849,"author":850,"body":852,"category":502,"createdAt":503,"description":1380,"extension":505,"location":506,"meta":1381,"navigation":513,"ogImage":514,"path":1387,"readingTime":1388,"seo":1389,"stem":1390,"tags":1391,"updatedAt":503,"url":1392,"__hash__":1393},"blog/blog/how-to-add-an-mcp-server-to-your-saas-2026.md","How to Add an MCP Server to Your SaaS: A Practical Guide (2026)",{"name":7,"social":851},{"twitter":9},{"type":11,"value":853,"toc":1362},[854,860,867,871,880,900,903,907,910,924,927,931,934,954,958,961,975,984,1239,1245,1249,1252,1272,1275,1279,1282,1286,1303,1307,1310,1312,1322,1324,1328,1331,1335,1338,1342,1345,1349,1352,1356,1359],[14,855,856,859],{},[17,857,858],{},"An MCP server lets people use your product from inside Claude, ChatGPT and other AI assistants."," If your SaaS already has a documented REST API, you don't need new endpoints. An MCP server sits on top of the API, describes a handful of actions as \"tools\" the assistant can call, and handles sign-in. The work is mostly in choosing the right tools and describing them well.",[14,861,862,863,548],{},"Simon built and runs the MCP server for Workspace369, App369's own SaaS. This guide is the checklist App369 follows for its ",[25,864,866],{"href":865},"/fixed-price/#mcp-connector","fixed-price MCP Connector",[31,868,870],{"id":869},"what-mcp-is","What MCP is",[14,872,873,874,879],{},"The ",[25,875,878],{"href":876,"rel":877},"https://modelcontextprotocol.io/",[42],"Model Context Protocol"," is an open standard, introduced by Anthropic in November 2024, for connecting AI applications to outside tools and data. A server exposes three kinds of things:",[46,881,882,888,894],{},[49,883,884,887],{},[17,885,886],{},"Tools:"," actions the model can call, such as \"create an invoice\" or \"search clients\".",[49,889,890,893],{},[17,891,892],{},"Resources:"," data the client can read into context, such as a document or a record.",[49,895,896,899],{},[17,897,898],{},"Prompts:"," reusable templates a user can pick.",[14,901,902],{},"For a SaaS integration, tools do most of the work. Each tool has a name, a plain-language description and a JSON schema for its inputs. The assistant reads the descriptions and decides when to call which tool.",[31,904,906],{"id":905},"local-or-remote","Local or remote",[14,908,909],{},"MCP servers talk to clients over one of two transports:",[46,911,912,918],{},[49,913,914,917],{},[17,915,916],{},"stdio:"," the server runs as a local process on the user's machine. This suits developer tools, but asks every user to install something.",[49,919,920,923],{},[17,921,922],{},"Streamable HTTP:"," the server runs on the web at a URL. This is what a SaaS wants: users add your connector by URL and sign in, with nothing to install.",[14,925,926],{},"Build a remote server over Streamable HTTP unless you have a reason not to.",[31,928,930],{"id":929},"step-1-pick-5-to-10-tools","Step 1: Pick 5 to 10 tools",[14,932,933],{},"Don't mirror your whole API. Pick the jobs people would actually ask an assistant to do, and make each one a tool:",[46,935,936,942,948],{},[49,937,938,941],{},[17,939,940],{},"Read tools first."," \"Find a client\", \"list overdue invoices\", \"show this project's tasks\". They are safe, and they are what people try first.",[49,943,944,947],{},[17,945,946],{},"A few write tools."," \"Create a draft invoice\", \"add a note\". Prefer creating drafts over sending or deleting, so a person confirms the final step in your app.",[49,949,950,953],{},[17,951,952],{},"No destructive tools at first."," Deleting, refunding or emailing customers from a chat is where mistakes get expensive.",[31,955,957],{"id":956},"step-2-write-descriptions-for-the-model","Step 2: Write descriptions for the model",[14,959,960],{},"The model only knows what your descriptions tell it. For each tool:",[46,962,963,966,969,972],{},[49,964,965],{},"Say what it does and when to use it, in one or two sentences.",[49,967,968],{},"Name every input in plain words, with formats (\"ISO date, e.g. 2026-10-12\") and allowed values.",[49,970,971],{},"Return compact, labelled JSON. Strip internal IDs and fields the user would never ask about, and paginate long lists.",[49,973,974],{},"Return errors as readable messages (\"No client named 'Acme'. Try search_clients first.\"), so the model can recover.",[14,976,977,978,983],{},"A minimal tool with the official ",[25,979,982],{"href":980,"rel":981},"https://github.com/modelcontextprotocol/typescript-sdk",[42],"TypeScript SDK"," looks like this:",[133,985,989],{"className":986,"code":987,"language":988,"meta":138,"style":138},"language-ts shiki shiki-themes github-light github-dark","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\n\nconst server = new McpServer({ name: 'your-saas', version: '1.0.0' });\n\nserver.tool(\n  'list_overdue_invoices',\n  'List invoices that are past their due date, newest first. Use when the user asks who owes them money.',\n  { limit: z.number().int().min(1).max(50).default(10) },\n  async ({ limit }) => {\n    const invoices = await api.get('/invoices', { status: 'overdue', limit });\n    return { content: [{ type: 'text', text: JSON.stringify(invoices) }] };\n  },\n);\n","ts",[102,990,991,1009,1023,1028,1061,1066,1078,1087,1095,1145,1167,1201,1227,1233],{"__ignoreMap":138},[142,992,993,996,1000,1003,1006],{"class":144,"line":145},[142,994,995],{"class":172},"import",[142,997,999],{"class":998},"sVt8B"," { McpServer } ",[142,1001,1002],{"class":172},"from",[142,1004,1005],{"class":152}," '@modelcontextprotocol/sdk/server/mcp.js'",[142,1007,1008],{"class":998},";\n",[142,1010,1011,1013,1016,1018,1021],{"class":144,"line":169},[142,1012,995],{"class":172},[142,1014,1015],{"class":998}," { z } ",[142,1017,1002],{"class":172},[142,1019,1020],{"class":152}," 'zod'",[142,1022,1008],{"class":998},[142,1024,1025],{"class":144,"line":187},[142,1026,1027],{"emptyLinePlaceholder":513},"\n",[142,1029,1031,1034,1037,1040,1043,1046,1049,1052,1055,1058],{"class":144,"line":1030},4,[142,1032,1033],{"class":172},"const",[142,1035,1036],{"class":162}," server",[142,1038,1039],{"class":172}," =",[142,1041,1042],{"class":172}," new",[142,1044,1045],{"class":148}," McpServer",[142,1047,1048],{"class":998},"({ name: ",[142,1050,1051],{"class":152},"'your-saas'",[142,1053,1054],{"class":998},", version: ",[142,1056,1057],{"class":152},"'1.0.0'",[142,1059,1060],{"class":998}," });\n",[142,1062,1064],{"class":144,"line":1063},5,[142,1065,1027],{"emptyLinePlaceholder":513},[142,1067,1069,1072,1075],{"class":144,"line":1068},6,[142,1070,1071],{"class":998},"server.",[142,1073,1074],{"class":148},"tool",[142,1076,1077],{"class":998},"(\n",[142,1079,1081,1084],{"class":144,"line":1080},7,[142,1082,1083],{"class":152},"  'list_overdue_invoices'",[142,1085,1086],{"class":998},",\n",[142,1088,1090,1093],{"class":144,"line":1089},8,[142,1091,1092],{"class":152},"  'List invoices that are past their due date, newest first. Use when the user asks who owes them money.'",[142,1094,1086],{"class":998},[142,1096,1098,1101,1104,1107,1110,1112,1115,1118,1121,1124,1127,1129,1132,1134,1137,1139,1142],{"class":144,"line":1097},9,[142,1099,1100],{"class":998},"  { limit: z.",[142,1102,1103],{"class":148},"number",[142,1105,1106],{"class":998},"().",[142,1108,1109],{"class":148},"int",[142,1111,1106],{"class":998},[142,1113,1114],{"class":148},"min",[142,1116,1117],{"class":998},"(",[142,1119,1120],{"class":162},"1",[142,1122,1123],{"class":998},").",[142,1125,1126],{"class":148},"max",[142,1128,1117],{"class":998},[142,1130,1131],{"class":162},"50",[142,1133,1123],{"class":998},[142,1135,1136],{"class":148},"default",[142,1138,1117],{"class":998},[142,1140,1141],{"class":162},"10",[142,1143,1144],{"class":998},") },\n",[142,1146,1148,1151,1154,1158,1161,1164],{"class":144,"line":1147},10,[142,1149,1150],{"class":172},"  async",[142,1152,1153],{"class":998}," ({ ",[142,1155,1157],{"class":1156},"s4XuR","limit",[142,1159,1160],{"class":998}," }) ",[142,1162,1163],{"class":172},"=>",[142,1165,1166],{"class":998}," {\n",[142,1168,1170,1173,1176,1178,1181,1184,1187,1189,1192,1195,1198],{"class":144,"line":1169},11,[142,1171,1172],{"class":172},"    const",[142,1174,1175],{"class":162}," invoices",[142,1177,1039],{"class":172},[142,1179,1180],{"class":172}," await",[142,1182,1183],{"class":998}," api.",[142,1185,1186],{"class":148},"get",[142,1188,1117],{"class":998},[142,1190,1191],{"class":152},"'/invoices'",[142,1193,1194],{"class":998},", { status: ",[142,1196,1197],{"class":152},"'overdue'",[142,1199,1200],{"class":998},", limit });\n",[142,1202,1204,1207,1210,1213,1216,1219,1221,1224],{"class":144,"line":1203},12,[142,1205,1206],{"class":172},"    return",[142,1208,1209],{"class":998}," { content: [{ type: ",[142,1211,1212],{"class":152},"'text'",[142,1214,1215],{"class":998},", text: ",[142,1217,1218],{"class":162},"JSON",[142,1220,548],{"class":998},[142,1222,1223],{"class":148},"stringify",[142,1225,1226],{"class":998},"(invoices) }] };\n",[142,1228,1230],{"class":144,"line":1229},13,[142,1231,1232],{"class":998},"  },\n",[142,1234,1236],{"class":144,"line":1235},14,[142,1237,1238],{"class":998},");\n",[14,1240,873,1241,1244],{},[102,1242,1243],{},"api"," client is your existing REST client, called with the signed-in user's credentials.",[31,1246,1248],{"id":1247},"step-3-decide-how-users-sign-in","Step 3: Decide how users sign in",[14,1250,1251],{},"There are two common options:",[46,1253,1254,1260],{},[49,1255,1256,1259],{},[17,1257,1258],{},"API key or personal access token."," The user pastes a key from your settings page. It is the fastest to build and fine for technical users and internal teams.",[49,1261,1262,1265,1266,1271],{},[17,1263,1264],{},"OAuth."," The user clicks \"Connect\", signs in to your product and approves access, with no keys to copy. The MCP specification defines ",[25,1267,1270],{"href":1268,"rel":1269},"https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization",[42],"authorization for HTTP transports"," based on OAuth 2.1. It takes longer to build, but it is what non-technical users expect, and directories tend to favour it.",[14,1273,1274],{},"Either way, every tool call must run as the signed-in user, with that user's permissions. Never give the MCP server one shared admin key.",[31,1276,1278],{"id":1277},"step-4-host-it-next-to-your-api","Step 4: Host it next to your API",[14,1280,1281],{},"A remote MCP server is a small web service. Cloud Run, Cloudflare Workers and Vercel all work well; pick whatever already hosts your API, so logs, secrets and billing stay in one place. Keep API keys and OAuth client secrets in your platform's secret manager, not in environment files committed to git.",[31,1283,1285],{"id":1284},"step-5-test-in-real-clients","Step 5: Test in real clients",[81,1287,1288,1297,1300],{},[49,1289,1290,1291,1296],{},"Use the ",[25,1292,1295],{"href":1293,"rel":1294},"https://github.com/modelcontextprotocol/inspector",[42],"MCP Inspector"," to call each tool directly and check its schema and output.",[49,1298,1299],{},"Add the server as a custom connector in Claude and in ChatGPT, and ask ordinary questions (\"who owes me money this month?\"). Watch which tools the model picks, and rewrite descriptions where it picks wrong.",[49,1301,1302],{},"Test a user without permission to a record, and a user from another account. They must get errors, not data.",[31,1304,1306],{"id":1305},"step-6-submit-to-directories","Step 6: Submit to directories",[14,1308,1309],{},"Anthropic and OpenAI both list third-party connectors, and both review submissions. Prepare listing copy, screenshots, a privacy policy that covers the connector, and a test account for the reviewers. Approval is up to each directory, so plan your launch around your own users first: a \"Connect to Claude\" page in your help center works on day one.",[31,1311,766],{"id":765},[14,1313,1314,1315,1317,1318,548],{},"Built in-house, expect one to three weeks for an engineer who already knows your API, depending mostly on authentication and how many tools you start with. App369 builds it as a ",[25,1316,866],{"href":865},": $2,500 for up to 8 tools over your existing documented API with API-key or token auth, deployed to your own cloud account and tested in Claude and ChatGPT, with a directory-submission packet. OAuth sign-in is a $1,000 add-on. For broader AI work, see ",[25,1319,1321],{"href":1320},"/service/ai-integration/","AI integration services",[31,1323,442],{"id":441},[444,1325,1327],{"id":1326},"do-i-need-to-build-new-api-endpoints-for-an-mcp-server","Do I need to build new API endpoints for an MCP server?",[14,1329,1330],{},"No. If your REST API is documented and already supports the actions you want, the MCP server calls those endpoints on behalf of the signed-in user. New endpoints are only needed when an action people want doesn't exist in your API yet.",[444,1332,1334],{"id":1333},"does-one-mcp-server-work-in-both-claude-and-chatgpt","Does one MCP server work in both Claude and ChatGPT?",[14,1336,1337],{},"Yes. MCP is a shared protocol, so one remote server can be added as a connector in both. Test in each client, because they can choose and display tool calls differently.",[444,1339,1341],{"id":1340},"should-i-use-oauth-or-api-keys-for-my-mcp-connector","Should I use OAuth or API keys for my MCP connector?",[14,1343,1344],{},"Start with API keys or personal access tokens if your users are technical or internal. Use OAuth if non-technical customers will connect it themselves or you plan to list it in public directories. Either way, each call must run with the signed-in user's own permissions.",[444,1346,1348],{"id":1347},"is-it-safe-to-let-an-ai-assistant-write-data-in-my-app","Is it safe to let an AI assistant write data in my app?",[14,1350,1351],{},"It can be, if you limit what tools can do. Start with read tools, make write tools create drafts rather than send or delete, run every call with the user's own permissions, and log tool calls so you can see what happened.",[444,1353,1355],{"id":1354},"how-many-tools-should-an-mcp-server-have","How many tools should an MCP server have?",[14,1357,1358],{},"Start with 5 to 10 that cover the questions users actually ask. Too many similar tools make the model pick the wrong one. Add more once you have seen real usage.",[480,1360,1361],{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":138,"searchDepth":169,"depth":169,"links":1363},[1364,1365,1366,1367,1368,1369,1370,1371,1372,1373],{"id":869,"depth":169,"text":870},{"id":905,"depth":169,"text":906},{"id":929,"depth":169,"text":930},{"id":956,"depth":169,"text":957},{"id":1247,"depth":169,"text":1248},{"id":1277,"depth":169,"text":1278},{"id":1284,"depth":169,"text":1285},{"id":1305,"depth":169,"text":1306},{"id":765,"depth":169,"text":766},{"id":441,"depth":169,"text":442,"children":1374},[1375,1376,1377,1378,1379],{"id":1326,"depth":187,"text":1327},{"id":1333,"depth":187,"text":1334},{"id":1340,"depth":187,"text":1341},{"id":1347,"depth":187,"text":1348},{"id":1354,"depth":187,"text":1355},"How to make your SaaS work inside Claude and ChatGPT with an MCP server over your existing REST API: choosing tools, authentication, hosting, testing and directory submission.",{"readingTimeStats":1382},{"text":1383,"minutes":1384,"time":1385,"words":1386},"6 min read",5.735,344100,1147,"/blog/how-to-add-an-mcp-server-to-your-saas-2026","9 min",{"title":849,"description":1380},"blog/how-to-add-an-mcp-server-to-your-saas-2026","mcp server, model context protocol, claude connector, chatgpt connector, saas ai integration, mcp oauth","https://app369.com/blog/how-to-add-an-mcp-server-to-your-saas-2026/","Q-_2beQi4FrOwPFO9yryBnLKDtw1D5shQXBsFXdZsI8",[1395,1397,1400,1403,1406],{"keyword":5,"url":520,"title":5,"relevance":1396},55,{"keyword":1398,"url":1399,"title":1398,"relevance":1169},"App Store Optimization (ASO) Guide (2026)","https://app369.com/blog/app-store-optimization-aso-guide-2026/",{"keyword":1401,"url":1402,"title":1401,"relevance":1097},"Firebase Security Rules: The Complete Guide for App Developers (2026)","https://app369.com/blog/firebase-security-guide-2026/",{"keyword":1404,"url":1405,"title":1404,"relevance":1080},"GEO: The Complete Guide to Generative Engine Optimization (2026)","https://app369.com/blog/geo-generative-engine-optimization-guide-2026/",{"keyword":1407,"url":1408,"title":1407,"relevance":1080},"Best App Development for Customer Service (2026)","https://app369.com/blog/best-app-development-company-customer-service-2026/",1791800181985]