How to Send Stripe Payments to Google Sheets (Free CSV, Apps Script Webhook or Zapier)
Send Stripe payments to Google Sheets with a free CSV export, an Apps Script webhook, or Zapier. Steps, full code, limits and fixes. Checked 13 Oct 2026.
Quick answer: The fastest method is Stripe's CSV export (Method 1). For automatic rows, use the free Apps Script webhook (Method 2) or Zapier (Method 3). Free? Yes for Methods 1 and 2. Time: about 2 minutes for the export, about 20 minutes for the webhook.
Stripe lets you export payments to a CSV file from the Dashboard, and that file opens in Google Sheets. To log each new payment as it happens, Stripe can push an event to a web address, and a short Google script can turn that event into a row. Zapier's Free plan includes 100 tasks per month as of 13 October 2026.
What do you need before you start?
You need three things:
- A Stripe account with at least one payment, ideally made through Stripe Checkout.
- A Google account with a blank Google Sheet.
- Permission to create API keys and webhooks in Stripe. If you are not the account owner, ask the owner.
Try everything in Stripe's test mode (called a sandbox in the Dashboard) first. Stripe says live and test mode use different keys and different webhook signing secrets, so repeat the setup for live mode when you are done.
How do you export Stripe payments to Google Sheets? (Method 1: CSV, free)
Open the Transactions page in the Stripe Dashboard, filter the list, click Export, then import the downloaded CSV into Google Sheets. Stripe's support page describes this flow. It is manual, but it needs no code and no keys.
- Sign in to the Stripe Dashboard and open the Transactions page.
- Filter by date range, payment method or payment type (for example successful or refunded).
- Click Export.
- Pick your settings and the columns you want, then confirm. A CSV file downloads to your device.
- Open your Google Sheet and click File > Import.
- Click Upload, drop in the CSV, and choose Append to current sheet or Replace current sheet.
- Click Import data.
For accounting, Stripe recommends its Balance Summary Report instead of a payments export, saying it "functions similarly to a bank statement". Use the payments export when you want one row per payment.
How do you log every new Stripe payment in Google Sheets automatically? (Method 2: Apps Script webhook, free)
Create a Google Apps Script web app, give Stripe its URL as a webhook endpoint, and have the script add one row per paid Checkout Session. This works for payments made through Stripe Checkout. Stripe says that when someone pays, Checkout creates a checkout.session.completed event.
One design choice matters here. Stripe recommends checking each webhook with the Stripe-Signature header and the raw request body. Google's web apps page documents the request object as having parameters, a query string and post data, with no header field. So this script does not trust the incoming message. It reads only the session ID from it, then asks Stripe directly for the real session. A fake request can then add at most a row for a genuine, paid session.
Part A: Make a restricted Stripe key
- In the Stripe Dashboard, open the API keys page and click Create restricted key.
- Enter a Key name, such as "Sheets logger".
- Set the permission for Checkout Sessions to Read, and leave everything else on None. Verify this step: the exact resource label in your Dashboard may differ.
- Click Create key and complete the two-factor check.
- Copy the key. Stripe says a restricted key starts with
rk_live_orrk_test_and you cannot retrieve it later.
Part B: Add the script
- Open your Google Sheet and name the first tab Payments.
- In row 1, type these headers:
Session ID,Created (UTC),Amount,Currency,Email,Status. - Click Extensions > Apps Script.
- Delete the sample code and paste the script below.
- Click the Project Settings gear, then Script Properties, then Add script property. Name it
STRIPE_KEYand paste your restricted key as the value.
// Stripe -> Google Sheets. Paste into Extensions > Apps Script.
// Stripe key lives in Script Properties as STRIPE_KEY (never in the code).
const SHEET_NAME = 'Payments';
function doPost(e) {
try {
const event = JSON.parse(e.postData.contents);
const okTypes = [
'checkout.session.completed',
'checkout.session.async_payment_succeeded'
];
if (okTypes.indexOf(event.type) === -1) {
return reply('ignored');
}
// Do not trust the payload. Ask Stripe for the real session.
const sessionId = event.data.object.id;
const session = fetchSession(sessionId);
if (session.payment_status !== 'paid') {
return reply('not paid yet');
}
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(SHEET_NAME);
if (alreadyLogged(sheet, session.id)) {
return reply('duplicate');
}
const email = (session.customer_details && session.customer_details.email)
|| session.customer_email || '';
sheet.appendRow([
session.id,
new Date(session.created * 1000).toISOString(),
session.amount_total / 100, // check: this assumes a currency with 2 decimals
(session.currency || '').toUpperCase(),
email,
session.payment_status
]);
return reply('ok');
} catch (err) {
console.error(err);
return reply('error');
}
}
function fetchSession(id) {
const key = PropertiesService.getScriptProperties().getProperty('STRIPE_KEY');
const res = UrlFetchApp.fetch('https://api.stripe.com/v1/checkout/sessions/' + id, {
headers: { Authorization: 'Bearer ' + key },
muteHttpExceptions: true
});
if (res.getResponseCode() !== 200) {
throw new Error('Stripe returned ' + res.getResponseCode() + ': ' + res.getContentText());
}
return JSON.parse(res.getContentText());
}
function alreadyLogged(sheet, sessionId) {
const last = sheet.getLastRow();
if (last < 2) return false;
const ids = sheet.getRange(2, 1, last - 1, 1).getValues().flat();
return ids.indexOf(sessionId) !== -1;
}
function reply(text) {
return ContentService.createTextOutput(text);
}
The amount_total field is an integer in the Checkout Session object; Stripe's example shows 2198 with currency usd. Dividing by 100 gives dollars and cents for two-decimal currencies. Check your own currency before trusting the totals.
Part C: Publish it as a web app
- In the Apps Script editor, click Deploy > New deployment.
- Next to "Select type", click the gear and choose Web app.
- Set Execute as to Me. Google describes this as the script always running as the owner.
- Under who has access, choose the option that lets anyone reach the URL without signing in. Verify this step: Google's page does not list the option names, and your Workspace admin may block public access.
- Click Deploy, approve the permission prompts, and copy the Web app URL.
Use the /exec URL. Google says the /dev URL only works for people with edit access and always runs the latest saved code.
Part D: Point Stripe at the script
- Open the Webhooks tab in Workbench (dashboard.stripe.com/webhooks).
- Click Create an event destination.
- Select Your account.
- Choose the Snapshot payload format (not Thin), because the script reads the session ID from the event's
data.object. - Select the events
checkout.session.completedandcheckout.session.async_payment_succeeded, then click Continue. - Choose Webhook endpoint as the destination type and click Continue.
- Paste your Apps Script URL as the Endpoint URL and finish.
- Make a test payment in test mode, then check the Payments tab.
The signing secret (starts with whsec_) is not needed by this script. Stripe's webhook docs say the secret is used for signature checks.
How do you connect Stripe to Google Sheets with Zapier? (Method 3: no code)
Create a Zap with the Stripe trigger New Payment and the Google Sheets action Create Spreadsheet Row. Zapier lists New Payment as "Triggers when a new payment is completed" and Create Spreadsheet Row as "Create a new row in a specific spreadsheet".
- In Zapier, click Create > Zap.
- For the trigger, choose Stripe, then the event New Payment, and connect your Stripe account.
- For the action, choose Google Sheets, then the event Create Spreadsheet Row.
- Select your spreadsheet and worksheet.
- Map Stripe fields (amount, currency, customer email, ID) to your sheet columns.
- Test, then turn the Zap on.
Zapier also lists Checkout Session Completed, New Charge, Failed Payment and New Refund as Stripe triggers, which is useful if you want refunds logged on a second tab.
Which method should you choose?
Pick the CSV export for one-off reports, the Apps Script webhook for free live logging, and Zapier if you want no code and can live with its limits. Prices are as of 13 October 2026.
| Method | Cost | Difficulty | Limits | Best for |
|---|---|---|---|---|
| 1. CSV export | Free | Easy | Manual, one file per export | Monthly reports |
| 2. Apps Script webhook | Free | Medium | Checkout payments only as written; you maintain the code | Live logging at no cost |
| 3. Zapier | Free plan, then paid | Easy | Free: 100 tasks/month, two-step Zaps, 15-minute polling | Non-coders |
What's free and what costs money?
Stripe's export and webhooks have no extra fee listed on the pages used here, and Apps Script is part of your Google account. Zapier is the only paid piece, as of 13 October 2026:
- Free: 100 tasks per month, unlimited Zaps, two-step Zaps (one trigger, one action), 15-minute polling. Zapier says premium apps are paid-only.
- Professional (750 tasks): $19.99 per month billed annually, or $29.99 billed monthly.
- Trial: 14 days of Professional for new accounts, no card required.
Triggers do not count as tasks; each row written to Sheets does.
What can go wrong, and how do you fix it?
- Rows appear twice. Stripe says the same event can arrive more than once and live-mode retries run for up to three days. The script's
alreadyLoggedcheck prevents repeats. If you changed the sheet layout, keep Session IDs in column A. - Nothing arrives. In Workbench, open your endpoint and check Event deliveries (Delivered, Pending or Failed). Also confirm you published a new version after editing code, and that you used the
/execURL. - Stripe shows deliveries as Failed. Stripe says your endpoint must return a 2xx quickly and that 3xx redirects count as failures. If Failed shows even though rows appear, rely on the duplicate check, or switch to Zapier.
- "Stripe returned 401" in the logs. The key is wrong or missing. Re-check the
STRIPE_KEYscript property, and remember test and live keys are separate. - "Stripe returned 403 or a permissions error". Stripe says a missing permission returns an error that explains which permission to add. Edit the restricted key and re-save.
- Amounts look 100 times too big. You are seeing the raw integer. Keep the
/ 100for two-decimal currencies. - Delayed payments are missing. Methods like ACH send a later
checkout.session.async_payment_succeededevent. Make sure that event is selected in Stripe.
Is this allowed?
Yes, with care about customer data. Payment rows contain customer emails, so limit sheet sharing to people who need it and never paste a full secret key into a cell. Stripe's docs advise using restricted keys as drop-in replacements for secret keys, which keeps access narrow. If you plan to email these customers from the sheet, see how to send emails from Google Sheets automatically and check your local marketing-email rules first.
What's still unclear?
- Header checks. Google's documentation does not describe request headers in
doPost, which is why this script re-fetches the session instead of verifyingStripe-Signature. If Google adds header access, signature checks would be stronger. - Delivery status. Whether Stripe marks Apps Script deliveries as Failed in your account depends on the response your deployment sends back. Check your own Event deliveries tab after a test.
- Scope. The script handles Checkout Sessions only. Payments from Payment Links, invoices or subscriptions renewals use other events, and the script does not log them.
Looking for similar logging jobs? See how to connect Gmail to Google Sheets with Apps Script and how to get Google Sheets alerts on WhatsApp to be notified of each new payment row.
Last checked: 13 October 2026.
Frequently asked questions
Can I send Stripe payments to Google Sheets for free?
Yes. Stripe's Dashboard lets you export payments to a CSV file at no extra charge, and you can import that file into Google Sheets. For automatic rows, a free Apps Script web app can receive Stripe webhook events. Zapier also has a Free plan with 100 tasks per month, as of 13 October 2026.
Which Stripe event should I listen for to log a payment?
For Stripe Checkout, listen for checkout.session.completed. Stripe says this event is created when someone pays. For delayed payment methods such as ACH direct debit, also listen for checkout.session.async_payment_succeeded, which fires when the money arrives later. Always check the session's payment_status before logging it as paid.
Why did my Google Sheet get the same payment twice?
Stripe says endpoints might occasionally receive the same event more than once, and it retries failed deliveries for up to three days in live mode. Store each Checkout Session ID in the sheet and skip any ID that already exists. The script in this guide does that check before adding a row.
Is it safe to put my Stripe key in Apps Script?
Store it in Script Properties, never in the code or a cell. Use a restricted key (it starts with rk_live_ or rk_test_) that can only read Checkout Sessions, not your full secret key. Stripe says a restricted key cannot be revealed again after you first see it, so save it in a password manager.
Do I need a developer to set up a Stripe webhook?
Not for this guide. You create the webhook endpoint in the Stripe Dashboard by pasting a URL, and the Apps Script code is copy-paste. You do need to be comfortable pasting code and clicking through menus. If that sounds like too much, the CSV export or Zapier method needs no code.
Does the Zapier method count against my free tasks?
Yes. Zapier says one task is one successful action step, so each new row created in Google Sheets uses one task. Triggers do not count. The Free plan includes 100 tasks per month as of 13 October 2026, so about 100 payments a month fits before you must upgrade.
Sources
- Exporting Payment data, Stripe Support (2026-10-13)
- Webhooks, Stripe (2026-10-13)
- Fulfill orders (Checkout), Stripe (2026-10-13)
- Checkout Session object, Stripe (2026-10-13)
- Retrieve a Checkout Session, Stripe (2026-10-13)
- Restricted API keys, Stripe (2026-10-13)
- Web Apps | Apps Script, Google for Developers (2026-09-03)
- Class Sheet (appendRow), Google for Developers (2026-10-13)
- Class UrlFetchApp, Google for Developers (2026-05-15)
- Zapier pricing, Zapier (2026-10-13)
- Stripe + Google Sheets integrations, Zapier (2026-10-13)