user@gitdiot:~/blog/how-i-operate/google-admin-email-analytics$
● online ~/index ~/about
$ cat ./content/operating/google-admin-email-analytics.md --render
~/blog/how-i-operate/google-admin-email-analytics
how-i-operate google-admin reports-api analytics Mar 25, 2026 · 11 min read

What Google's Admin API Actually Knows About Your Emails

Most of this data is locked behind an Admin Console UI with no export button. Here's how to get at it through the Reports API and BigQuery.

The Three Layers of Gmail Analytics

Google Workspace gives admins three distinct ways to look at email data, each with different depth, retention, and programmability:

Layer Access Method Retention Granularity Programmatic?
Email Log Search Admin Console UI 30 days (limited after) Per-message delivery steps No
Reports API REST API (activities.list) 180 days Per-event (35 event types) Yes
BigQuery Export SQL queries Unlimited Full message metadata + routing Yes

Most admins only use the first one — clicking through the Admin Console to troubleshoot "where did this email go?" That's the equivalent of using tail -f on your production logs. The other two layers are where the real analytics live.

Layer 1: Email Log Search (The Flashlight)

Email Log Search (ELS) is the tool most Workspace admins already know. You open the Admin Console, type in a sender or recipient, and see what happened to their messages.

What it shows:

What it doesn't:

ELS is useful for one-off troubleshooting ("did this email arrive?") but useless for systematic analytics. You can't answer "what's our delivery rate this month?" by clicking through 200 individual message lookups.

Layer 2: The Reports API (The Telescope)

The Admin SDK Reports API is where things get interesting for programmatic access. You call activities.list with applicationName=gmail and get back structured event data for every email in your organization.

The API Call

GET https://admin.googleapis.com/admin/reports/v1/activity/users/all/applications/gmail
  ?eventName=delivery
  &startTime=2026-03-01T00:00:00Z
  &endTime=2026-03-25T00:00:00Z
  &maxResults=200

Required scope: https://www.googleapis.com/auth/admin.reports.audit.readonly

The Event Model

Gmail's Reports API uses a single event name — delivery — with a nested event_info.mail_event_type integer that distinguishes 35 different actions:

mail_event_type Event
1 Message sent
2 Message received
3 User manually classified as spam
4 Gmail flagged as spam post-delivery
5 Message quarantined
6 Message released from quarantine
7 Message opened (first time)
9 Message replied to (first time)
10 Message forwarded (first time)
11 Message auto-forwarded
15 Link in message body clicked
17 Attachments downloaded
26 Message archived
27 Message permanently deleted
29 Message saved as draft
30 Message bounce (delivery failure)
31 Message viewed (subsequent reads)

That's not just delivery data. That's a behavioral analytics engine. Google is tracking opens, replies, forwards, link clicks, attachment downloads, archival, and deletion — all surfaced through a single API endpoint.

Constraints

Filtering

The API supports server-side filtering on event parameters:

filters=event_info.mail_event_type==1    // Only "sent" events
filters=event_info.mail_event_type==30   // Only bounces

Operators: ==, <>, <, <=, >, >=. Multiple filters are comma-separated (AND logic).

Aggregate Usage Reports

Beyond per-event activity data, there's a separate usage reports endpoint that gives you daily domain-level metrics:

Metric What It Counts
num_emails_sent Outbound emails for the day
num_emails_received Inbound emails for the day
num_outbound_delivered_emails Successfully delivered outbound
num_outbound_rejected_emails Rejected by recipient server
num_outbound_rerouted_emails Rerouted (compliance rules, etc.)
num_outbound_encrypted_emails Sent over TLS
num_outbound_unencrypted_emails Sent without TLS
num_inbound_spam_emails Inbound classified as spam
num_inbound_non_spam_emails Inbound that passed spam filters
num_1day_active_users Users who accessed Gmail today

This is the 10,000-foot view — useful for dashboards and trend monitoring, not per-message tracking.

Layer 3: BigQuery Export (The Microscope)

If the Reports API is a telescope, BigQuery is a microscope. When you enable Gmail log exports to BigQuery in the Admin Console, every message transaction gets written as a row with 80+ fields of metadata.

This is the full schema. Every field here is queryable with SQL.

Message Metadata

Field What It Contains
message_info.rfc2822_message_id The Message-ID header — the key for matching to your own systems
message_info.subject Subject line (may be truncated for very long subjects)
message_info.payload_size Message size in bytes
message_info.num_message_attachments Attachment count
message_info.is_spam Boolean spam classification
message_info.description Human-readable description of what happened
message_info.action_type Delivery action (71 possible values covering every routing step)

Source and Destination

Field What It Contains
source.address Sender's envelope address
source.from_header_address The From: header (what the recipient sees)
source.from_header_displayname Display name in the From: header
destination.address Recipient's email address
destination.rcpt_response SMTP RCPT command response code
destination.service Destination mail service
flattened_destinations All recipients in a single queryable string

Connection and Encryption

Field What It Contains
connection_info.client_ip IP of the sending mail client
connection_info.smtp_in_connect_ip Remote IP for inbound SMTP connections
connection_info.smtp_out_connect_ip Remote IP for outbound SMTP connections
connection_info.smtp_out_remote_host Destination domain or mail server
connection_info.smtp_reply_code SMTP reply code (250 = success, 550 = rejected, etc.)
connection_info.smtp_tls_state 0 = not TLS, 1 = TLS
connection_info.smtp_tls_version TLS version (e.g., TLSv1.3)
connection_info.smtp_tls_cipher Cipher suite used
connection_info.tls_required_but_unavailable TLS was required but recipient had no valid cert
connection_info.failed_smtp_out_connect_ip IPs where Gmail failed to connect (MX fallback trail)

Authentication

Field What It Contains
connection_info.dkim_pass DKIM signature verified
connection_info.spf_pass SPF record matched
connection_info.dmarc_pass DMARC policy passed
connection_info.is_internal Sent within your org's domains
connection_info.is_intra_domain Sent within the exact same domain
connection_info.ip_geo_city Geo-located city of sending IP
connection_info.ip_geo_country ISO country code of sending IP

Spam Analysis

Field What It Contains
spam_info.disposition Outcome: delivered, spam folder, rejected, quarantined
spam_info.classification_reason Why: sender reputation, content rules, custom rule, blatant spam (14 possible reasons)
spam_info.ip_whitelist_entry If an IP allowlist influenced the decision

Attachments and Security

Field What It Contains
attachment.file_name File name
attachment.file_extension_type Extension (without the dot)
attachment.sha256 SHA-256 hash
attachment.malware_family Malware classification if detected

Post-Delivery Behavior

Field What It Contains
post_delivery_info.action_type What the user did after delivery (26 possible actions)
post_delivery_info.interaction.link_url URL of clicked link
post_delivery_info.interaction.drive_id Google Drive item accessed
post_delivery_info.interaction.attachment.file_name Attachment that was downloaded/previewed

Routing Rules and Compliance

Field What It Contains
triggered_rule_info.rule_name Name of the compliance/routing rule that fired
triggered_rule_info.rule_type Rule category (28 types: content compliance, routing, DLP, etc.)
triggered_rule_info.consequence.action What the rule did (21 actions: quarantine, reject, modify headers, reroute, etc.)
triggered_rule_info.string_match.matched_string The content that triggered a DLP or compliance rule

Example Queries

Delivery rate for outbound email this month:

SELECT
  COUNTIF(connection_info.smtp_reply_code BETWEEN 200 AND 299) AS delivered,
  COUNTIF(connection_info.smtp_reply_code >= 500) AS hard_bounced,
  COUNT(*) AS total
FROM `project.dataset.gmail_logs`
WHERE message_info.action_type = 1  -- sent
  AND event_info.timestamp_usec > UNIX_MICROS(TIMESTAMP '2026-03-01')

Unencrypted outbound messages (compliance risk):

SELECT
  source.address AS sender,
  destination.address AS recipient,
  connection_info.smtp_out_remote_host AS destination_server
FROM `project.dataset.gmail_logs`
WHERE connection_info.smtp_tls_state = 0
  AND connection_info.is_internal = false
  AND message_info.action_type = 1
ORDER BY event_info.timestamp_usec DESC

Messages that failed DMARC:

SELECT
  source.from_header_address,
  connection_info.dkim_pass,
  connection_info.spf_pass,
  connection_info.dmarc_pass,
  connection_info.client_ip
FROM `project.dataset.gmail_logs`
WHERE connection_info.dmarc_pass = false
  AND event_info.timestamp_usec > UNIX_MICROS(TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 7 DAY))

What Google Can't Tell You

The API has clear boundaries:

How We Use This for Outbound Sales

We run AgentCRM on Google Workspace. Every outreach email goes through Gmail — AI-drafted, human-reviewed, sent manually. That means every message we send flows through Google's logging infrastructure, and we can query it.

The Pipeline

Draft created (Gmail API)
        |
        v
Human reviews + sends
        |
        v
Reports API polls for delivery events (hourly)
        |
        v
Match to outreach_log via Message-ID header
        |
        v
Update delivery_status: delivered / rejected / spam / bounced
        |
        v
Logo tracking endpoint logs opens (separate system)
        |
        v
Gmail thread polling detects replies

What We Get From the Admin API Specifically

1. Delivery Confirmation

The Reports API tells us whether the recipient's mail server accepted the message. This is the mail_event_type=1 (sent) event followed by the SMTP response. A 250 from the recipient server means accepted. A 550 means rejected. A bounce notification (mail_event_type=30) means it was accepted but later failed.

This is data Gmail's user-facing API doesn't expose. You can check if a message is in your Sent folder, but you can't ask Gmail "did the recipient's server accept it?" That lives in the Admin Reports API.

2. Bounce Classification

Gmail sends bounce notifications as reply messages in the original thread — our polling cron catches those. But the Admin API catches bounces that don't generate reply messages: silent drops, temporary failures that never recovered, and rejections that happen after the initial SMTP ACK.

Cross-referencing both gives us a more complete picture than either source alone.

3. Encryption Verification

For B2B sales, knowing that your email was transmitted over TLS matters — especially when emailing into healthcare, finance, or government. The BigQuery schema gives us smtp_tls_state, smtp_tls_version, and smtp_tls_cipher for every outbound message. We can verify that sensitive communications were encrypted in transit without trusting the recipient's claim.

4. Authentication Audit

SPF, DKIM, and DMARC results for every message — both inbound and outbound. If our domain's DKIM signing breaks, we'll see dkim_pass=false in the logs before we see deliverability drop in our open rates. This is an early warning system.

The Implementation

Our delivery tracker polls the Reports API via the Google Workspace CLI tool (gws), using the admin profile with admin.reports.audit.readonly scope:

const { stdout } = await execFileAsync(GWS_BIN, [
  'admin-reports', 'activities', 'list',
  '--params', JSON.stringify({
    userKey: 'all',
    applicationName: 'gmail',
    startTime: oneHourAgo,
    maxResults: 200,
  }),
], { timeout: 30_000, env: gwsAdminEnv() });

Events come back as structured JSON with nested parameters. We parse out the message_id, sender, recipient, and disposition, then match against our outreach_log table by google_message_id:

const { data: rows } = await supabase
  .from('outreach_log')
  .select('id, tracking_id, delivery_status')
  .eq('google_message_id', event.messageId)
  .limit(1);

Matched events update delivery_status and delivered_at on the outreach record, and insert a corresponding email_events row for the audit trail.

What's Next: BigQuery

The Reports API gives us event-level delivery data, but the BigQuery export gives us the full picture — TLS cipher suites, DMARC results, spam classification reasons, routing rule triggers. We're planning to enable the BigQuery export and build:

The data is already being generated. Google is logging every field in that 80-column schema for every message we send. We just need to point BigQuery at it.

The Bigger Picture

Most sales teams treat email as fire-and-forget. Send the message, hope it arrives, check if they replied. The analytics gap between "sent" and "replied" is a black box.

Google Workspace makes that gap transparent — but only if you go beyond the Admin Console UI. The Reports API and BigQuery export turn Gmail from a messaging tool into an instrumented delivery platform. You get SMTP-level delivery confirmation, encryption verification, authentication auditing, spam classification, and post-delivery behavioral analytics — all from the same infrastructure that sends the mail.

The irony is that most organizations paying for Google Workspace Business Plus or Enterprise already have access to this data. They're just not querying it.


Built with AgentCRM — an AI-powered CRM agent running on Google Workspace, Supabase, and Claude.

subscribe.sh

Get the field notes

Weekly dispatches from an aging tech worker's refactoring. No spam, no thought leadership.

no spam · only high-signal logic