Register your interest: Tag @Cody, get an agent
BlogResources

How to connect SendGrid to Google Sheets without landing every event

Email event volume is far larger than send volume, and a spreadsheet is the wrong shape for it. Aggregating first, what to keep per message, and the deliverability view worth building.

Rithul PalazhiRithul Palazhi11 min read

Summarize with AI

How to connect SendGrid to Google Sheets without landing every event
On this page

One email produces several events. Processed, delivered, opened once or five times, clicked, perhaps bounced or reported. A modest campaign of twenty thousand messages can generate well over a hundred thousand event records, and a spreadsheet will hit its ceiling long before the campaign has finished reporting.

So the question is not how to get events into a sheet. It is what you want to know, and then what shape of summary answers it — because the answer is almost always a few hundred rows rather than a few hundred thousand.

What we'll cover

Events outnumber messages

Every message produces at least two events and frequently many more.

Opens repeat. A message opened repeatedly produces an event each time, and prefetching by privacy features multiplies this further.

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.

The event webhook is a stream, not a report, and treating it as rows to append is what fills a sheet.

A single campaign can exceed a spreadsheet's total capacity on its own, which is usually discovered the first time somebody runs a real send rather than a test.

The mechanism to build is therefore an aggregation that runs on a schedule, not a pipe from the webhook to a sheet.

Decide the grain before anything else

Three grains, each answering different questions, and picking one is the whole design.

One row per campaign or send, with counts and rates. A few rows a week, and it answers almost every question anybody actually asks.

One row per recipient per send, with a status. Larger, and useful when somebody needs to know what happened to a specific person.

One row per event. Only ever appropriate for a short debugging window on a small volume, and never as a standing arrangement.

Start at the coarsest grain that answers the question, and add detail when somebody demonstrates they need it — which is less often than the request suggests.

State the grain in the sheet, since a reader cannot tell and will sum a column that means something other than what they assume.

Building it with CodeWords

CodeWords connects to more than 3,000 integrations, and the connection is made once and reused.

  1. Open CodeWords and start a new automation.
  2. Describe what should happen in plain language to Cody, the automation builder: which sends, what to summarise, and which sheet it lands in.
  3. Authorize the connection with a SendGrid API key restricted to read access, plus access to the target spreadsheet.
  4. Describe the exceptions: a send with no events yet, an event type you have not seen before, a result larger than the sheet can hold.
  5. Run it against a completed send whose numbers you already know before trusting it.

You describe the outcome; Cody builds it, connects it, and deploys it. The free plan covers light use, with Pro at $39 per month and Business at $100 per month as usage grows; details are on the pricing page.

The deliverability view worth having

The report that earns its place, and it is not the one most people build first.

Delivery rate by mailbox provider, not in aggregate. 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 one requires action.

Deferrals by provider, which move before delivery rates do and are therefore the earlier warning.

Spam reports as a rate, watched closely, since this is the number that affects everything else.

Compared against your own recent history, not against published benchmarks, which are not measurements of anything relevant to your sending.

One row per day per provider is a manageable grain that supports all of it and stays small indefinitely.

What to keep per message

If you do need recipient-level rows, keep the final state rather than the journey.

One row per recipient per send, updated as events arrive, rather than appended.

A single status column — delivered, bounced, complained, dropped — which is what somebody looking up a recipient wants.

The timestamp of the last meaningful event, not every event's time.

The bounce reason where there is one, since that is the actionable part.

Nothing about opens at this grain, or the row is rewritten constantly for information that means little individually.

Opens and clicks, which are not what they look like

Worth stating plainly, because these are the columns people build reports on.

Open tracking is a loaded image, and privacy features prefetch images without a person reading anything, which registers opens that did not happen.

Open rates are therefore inflated and inflated unevenly across providers, which makes provider comparisons on open rate meaningless.

Click tracking rewrites links, which is more reliable than opens and still catches automated scanners following links in messages.

Treat both as trends, not facts. A change in click rate week on week says something; a specific person's open does not.

Never present an open as evidence somebody read a message, particularly in a sales context, 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. Somebody removed from your systems remains in the spreadsheet, which matters if you are subject to erasure requests.

Bounce and complaint data is sensitive in its own way — it says something about an individual's behaviour towards your mail.

Keep a retention rule on the sheet, and actually apply it, since an export nobody deletes accumulates indefinitely.

Categories and custom arguments, which make the summary possible

The thing to set up before the reporting rather than after, because it cannot be applied retroactively.

Attach a category to every send, so events can be grouped by what the message was rather than by when it went.

Use custom arguments for your own identifiers — the campaign, the template version, the segment — which 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 with four hundred one-row groups, which is the same as no grouping at all.

Separate transactional from marketing at this level too, so the summary never averages a password reset against a newsletter.

Record the template version where you iterate on templates, since a change in performance after a template edit is otherwise invisible.

None of this is retroactive. Events already sent carry whatever they carried, so the cost of setting it up late is a gap in the history you cannot fill.

Making it survive

Aggregate before writing, always, rather than landing events and summarising in the spreadsheet.

Make writes idempotent by replacing a dated range rather than appending, since 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.

Handle the unknown event type by reporting it rather than dropping it.

Report the outcome. Sends summarised, rows written, and the period recomputed.

Limits worth knowing about

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, and per-message detail comes from the event webhook rather than 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

A daily deliverability summary at one row per day per mailbox provider: delivered, bounced hard and soft, deferred, and complained, as rates and counts. It is small, it stays small, and it is the view that 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, since events arrive late and yesterday's numbers are still moving. And split bounces into hard and soft, because they mean different things and a combined bounce rate hides the one that requires action.

Frequently asked questions

Why does the sheet fill up so fast?

Because every message produces several events, and opens repeat. A single campaign can exceed a spreadsheet's total capacity on its own. Aggregate before writing rather than landing raw events.

What grain should the sheet use?

The coarsest that answers the question — usually one row per send, or one row per day per provider. Recipient-level rows only when somebody needs to look up an individual, and event-level effectively never.

Why does yesterday's number keep changing?

Events arrive late, so a day's figures are still moving for a while after it ends. Recompute recent days on every run rather than writing each day once and considering it final.

Are open rates reliable?

No, not individually. Privacy features prefetch tracking images, registering opens nobody made, and unevenly across providers. Use them as a trend and never as evidence that a specific person read a message.

Why report delivery by provider?

Because problems start at one provider. An aggregate delivery rate stays healthy-looking while a significant share of your recipients quietly stop receiving mail, which is exactly the situation you want to detect early.

What is the earliest warning of a deliverability problem?

Deferrals by provider. They rise before delivery rates fall, which makes them a more useful thing to watch than the number most reports lead with.

Is a recipient-level sheet a privacy problem?

It is a list of email addresses outside your normal controls, that no deletion request will reach. Use aggregate grains where the reader only needs rates, check the sharing, and apply a retention rule you actually enforce.

How do I group events by campaign?

Attach a category and custom arguments at send time — campaign, template version, segment — which come back on the events. It cannot be applied retroactively, so setting it up late leaves a permanent gap in the history.

Should transactional and marketing mail share a report?

No. Averaging a password reset against a newsletter produces a number that describes neither, and the transactional figures are the ones you cannot afford to have obscured.

Should the event webhook write directly to the sheet?

No. It is a stream, not a report, and piping it at 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.

How far back can I rebuild a report?

Only as far as the event data is retained, which is limited. Your own stored summaries are what give you history, which is an argument for starting the daily summary before you need the history rather than after.

Get started today

Your first workflow is free to build.

Describe what you need. Cody handles the build, the connections, and the deployment.