How to connect SendGrid to Google Sheets without landing every event
Connect SendGrid to Google Sheets by aggregating email events first, with what to keep per message and a deliverability view.
On this page
- What we'll cover
- Key takeaways
- Events outnumber messages
- Decide the grain before anything else
- Building it with CodeWords
- The deliverability view worth having
- What to keep per message
- Why opens and clicks mislead
- Recipient data in a spreadsheet
- Categories and custom arguments
- How to make the export reliable
- Limits of the export
- What to set up first
- Frequently asked questions
- Related reading
To connect SendGrid to Google Sheets, aggregate the email events on a schedule and write only the summary to the sheet, never the raw events. One email produces several events: processed, delivered, opened once or five times, clicked, and perhaps bounced or reported. A campaign of twenty thousand messages can produce well over a hundred thousand event records (an illustrative case), and a spreadsheet reaches its ceiling before the campaign finishes reporting.
The question is therefore not how to get events into a sheet. The question is what you want to know and which summary answers it. The answer is usually a few hundred rows, not a few hundred thousand.
What we'll cover
- Events outnumber messages
- Decide the grain before anything else
- Building it with CodeWords
- The deliverability view worth having
- What to keep per message
- Why opens and clicks mislead
- Recipient data in a spreadsheet
- Categories and custom arguments
- How to make the export reliable
- Limits of the export
- What to set up first
- Frequently asked questions
Key takeaways
- Choose the coarsest grain that answers the question, usually one row per send or one row per day per mailbox provider.
- Report delivery by mailbox provider, because a problem at one provider hides inside a healthy overall rate.
- Treat open and click rates as trends, because privacy features and link scanners create events nobody caused.
- Set categories and custom arguments at send time, because they cannot be added to events already sent.
- Recompute the last few days on every run, because events arrive late.
Events outnumber messages
Events outnumber messages because every message produces several events, and the event webhook is a stream, not a report.
Every message produces at least two events and often many more.
Opens repeat. A message opened repeatedly produces an event each time, and prefetching by privacy features multiplies the count.
Events arrive out of order and late. A delivered event can follow a bounce event for the same message, and events can arrive well after the send.
Treating the webhook as rows to append is what fills a sheet.
A single campaign can exceed a spreadsheet's total capacity. Teams usually discover this the first time they run a real send instead of a test.
Build an aggregation that runs on a schedule, not a pipe from the webhook to a sheet.
Decide the grain before anything else
The grain is what one row represents, and picking it is the whole design. Three grains answer different questions.
| Grain | What it answers | When to use it |
|---|---|---|
| One row per campaign or send, with counts and rates | Almost every question anyone asks | Default; a few rows a week |
| One row per recipient per send, with a status | What happened to a specific person | When someone needs to look up an individual |
| One row per event | Debugging detail | A short debugging window on small volume, never a standing arrangement |
Start at the coarsest grain that answers the question and add detail when someone shows they need it. This happens less often than requests suggest.
State the grain in the sheet. A reader cannot tell, and will sum a column that means something other than what they assume.
Building it with CodeWords
CodeWords connects to 3,000+ integrations (CodeWords integrations page, 2026), and each connection is made once and reused.
- Open CodeWords and start a new automation.
- Describe what should happen in plain language to Cody, the automation builder: which sends, what to summarise, and which sheet it lands in.
- Authorize the connection with a SendGrid API key restricted to read access, plus access to the target spreadsheet.
- Describe the exceptions: a send with no events yet, an event type you have not seen before, and a result larger than the sheet can hold.
- Run it against a completed send whose numbers you already know before you trust it.
You describe the outcome; Cody builds it, connects it, and deploys it. The Free plan costs $0, Pro costs $39 per month and Business costs $100 per month (CodeWords pricing page, 2026). Details are on the pricing page.
The deliverability view worth having
The deliverability view that earns its place reports delivery by mailbox provider, not in aggregate. Most people do not build it first.
Delivery rate by mailbox provider. Problems start at one provider, and an overall number stays healthy while a large share of your recipients stop receiving mail.
Bounce rate split into hard and soft. They mean different things, and only hard bounces need action.
Deferrals by provider. Deferrals move before delivery rates do, so they are the earlier warning.
Spam reports as a rate. Watch this number closely, because it affects everything else.
Comparison against your own recent history, not published benchmarks, which do not measure your sending.
One row per day per provider is a manageable grain that supports all of the above and stays small indefinitely.
What to keep per message
If you need recipient-level rows, keep the final state of each message, not its journey.
One row per recipient per send, updated as events arrive instead of appended.
A single status column: delivered, bounced, complained or dropped. This is what someone looking up a recipient wants.
The timestamp of the last meaningful event, not every event's time.
The bounce reason, where there is one. This is the actionable part.
Nothing about opens at this grain. Otherwise the row is rewritten constantly for information that means little individually.
Why opens and clicks mislead
Open and click data mislead because both count machine activity as reader activity. People build reports on these columns, so the limits matter.
Open tracking is a loaded image. Privacy features prefetch images without a person reading anything, which registers opens that did not happen.
Open rates are inflated unevenly across providers, so provider comparisons on open rate are meaningless.
Click tracking rewrites links. Clicks are more reliable than opens, but automated scanners that follow links in messages still register as clicks.
Treat both as trends, not facts. A week-on-week change in click rate says something; one person's open does not.
Never present an open as evidence someone read a message, especially in sales, because it will eventually be wrong in an embarrassing way.
Recipient data in a spreadsheet
A recipient-level export is a list of email addresses, which is personal data by definition.
Check the sheet's sharing, and prefer aggregate grains where the reader only needs the rate.
Deletion does not propagate. A person removed from your systems remains in the spreadsheet, which matters if you receive erasure requests.
Bounce and complaint data is sensitive. It describes an individual's behaviour towards your mail.
Keep a retention rule on the sheet and apply it. An export nobody deletes accumulates indefinitely.
Categories and custom arguments
Categories and custom arguments make the summary possible, so set them up before the reporting. They cannot be applied retroactively.
Attach a category to every send, so events group by what the message was instead of when it went.
Use custom arguments for your own identifiers: the campaign, the template version and the segment. They come back on the events and make the summary joinable to your own systems.
Keep the vocabulary small and agreed. Categories invented per send produce a report of hundreds of one-row groups, which equals no grouping.
Separate transactional from marketing mail at this level, so the summary never averages a password reset against a newsletter.
Record the template version when you iterate on templates, because a change in performance after a template edit is otherwise invisible.
Expect a gap if you start late. Events already sent carry what they carried, and the history cannot be filled in later.
How to make the export reliable
A reliable export aggregates before writing and recomputes recent days.
Aggregate before writing, instead of landing events and summarising in the spreadsheet.
Make writes idempotent by replacing a dated range instead of appending. Events arrive late, and a day's figures change after the day ends.
Re-summarise recent days on each run, because late events change yesterday's numbers.
Report the unknown event type instead of dropping it.
Report the outcome: sends summarised, rows written, and the period recomputed.
Limits of the export
The export has five limits.
Sheets has a total cell limit, which event-level data reaches almost immediately.
Event webhook delivery is at-least-once, with retries, so deduplication is required.
Statistics endpoints are aggregated. Per-message detail comes from the event webhook, not from them.
Rate limits apply, and retrieving large volumes needs pagination and pacing.
Event data availability is time-limited, so your own stored summaries are what give you history.
What to set up first
Set up a daily deliverability summary at one row per day per mailbox provider: delivered, hard and soft bounces, deferred and complained, as rates and counts. The summary is small, stays small, and shows a problem at one provider while the overall number still looks fine.
Two habits make the difference. Recompute the last few days on every run, because events arrive late and yesterday's numbers are still moving. And split bounces into hard and soft, because a combined bounce rate hides the type that needs action.
Frequently asked questions
Why does the sheet fill up so fast?
Every message produces several events, and opens repeat, so a single campaign can exceed a spreadsheet's total capacity. Aggregate the events before writing to the sheet instead of landing raw events, and write only the summary rows. Check the row count after the first run to confirm the grain.
What grain should the sheet use?
Use the coarsest grain that answers the question, usually one row per send or one row per day per provider. Use recipient-level rows only when someone needs to look up an individual, and avoid event-level rows as a standing arrangement.
Why does yesterday's number keep changing?
Events arrive late, so a day's figures keep moving after the day ends. Recompute recent days on every run instead of writing each day once and treating it as final. A rolling window of a few days handles this. Three to seven days is a sensible window for most senders.
Are open rates reliable?
No, not for individuals. Privacy features prefetch tracking images and register opens nobody made, unevenly across providers. Use open rate as a trend over time, and never as evidence that a specific person read a message. Click rates are more reliable than open rates, though link scanners affect them too.
Why report delivery by provider?
Problems start at one provider. An aggregate delivery rate looks healthy while a significant share of your recipients quietly stop receiving mail at a single provider, which is exactly the situation you want to detect early and act on. A split by provider takes one extra column and shows the problem early.
What is the earliest warning of a deliverability problem?
Deferrals by provider are the earliest warning. They rise before delivery rates fall, so they are more useful to watch than the headline number most reports lead with. Track deferrals daily for each major provider. Compare each provider against its own recent average, not against a published benchmark.
Is a recipient-level sheet a privacy problem?
It can be. A recipient-level sheet is a list of email addresses outside your normal controls, and no deletion request will reach it. Use aggregate grains where the reader needs only rates, check the sharing, and apply a retention rule you enforce.
How do I group events by campaign?
Attach a category and custom arguments at send time, such as campaign, template version and segment. They come back on the events and make grouping possible. They cannot be applied retroactively, so starting late leaves a permanent gap. Agree the category names with your team first, so every send uses the same vocabulary.
Should transactional and marketing mail share a report?
No. Averaging a password reset against a newsletter produces a number that describes neither. Report them separately so the transactional figures, which you cannot afford to have obscured, stay visible and comparable over time. Use separate categories at send time, and the report can then split them with no extra work.
Should the event webhook write directly to the sheet?
No. The webhook is a stream, not a report, and piping it into a spreadsheet fills the sheet within one campaign. Land events somewhere that can hold them, or aggregate on a schedule and write only the summary. Aggregate on a schedule, and write one row per group instead of one row per event.
How far back can I rebuild a report?
Only as far back as the event data is retained, and that period is limited. Your own stored summaries give you history, so start the daily summary before you need the history, not after. Store each day's summary as you go, and the history stays available after the source data expires.