Getting Magento 2 data into Google Sheets

Magento's sales data exports beautifully. Its catalogue does not, and the difference between those two facts is where most Magento spreadsheets go wrong.

Why half your data exports cleanly and half doesn't

Start with the good news, because it's genuinely good: sales_order and sales_order_item are proper tables with proper columns. base_grand_total means what you'd expect, order state is a single field, and a revenue export is a straightforward query. If all you need is orders, Magento is one of the easier platforms to get out.

The catalogue is the opposite. Magento stores product attributes in EAV: catalog_product_entity holds the skeleton, and names, prices, statuses and everything else live in catalog_product_entity_varchar, _decimal, _int and _text, keyed by an attribute id you look up in eav_attribute. "Products with their names and prices" is a multi-join query, not a table. Export the tables raw into a spreadsheet and you've moved the puzzle, not solved it.

The admin's own grid export sidesteps some of that — it gives you the columns the grid shows — but it runs inside Magento, on production, under PHP's memory and execution limits. On a large order grid it's the kind of export that either takes several minutes or quietly fails at 30,000 rows. And whatever route you take, Sheets caps a spreadsheet at 10 million cells, which a raw Magento export reaches far sooner than anyone expects.

Three ways to get there

All three end in a spreadsheet. They differ in whether the EAV joins are your problem and where the query runs.

The admin grid export

Sales → Orders (or the product grid), filter, then Export as CSV from the grid itself.

  • No setup at all, available to anyone with admin access
  • The grid has already flattened the columns it displays
  • Runs on production, inside PHP limits — large exports stall or fail
  • Only the columns the grid offers, which is rarely the join you want
  • Entirely manual, every single time

A script against MySQL

Apps Script with a JDBC connection, or a cron job running SQL and pushing CSV, with the EAV joins written by hand.

  • Any shape you like, including flattened catalogue attributes
  • Can be scheduled so the sheet updates itself
  • Your production database must accept connections from Google's IP ranges
  • The EAV flattening is real SQL that someone has to own
  • Every refresh queries the live store

Query a PostgreSQL copy, publish the answer

BeQuery

BeQuery clones Magento into an isolated PostgreSQL copy. You aggregate in the SQL editor and publish the result as a CSV link the sheet refreshes on its own.

  • Heavy catalogue joins run against a copy, so no export can slow the storefront
  • No PHP time limits — the query takes as long as it takes
  • Magento insight packs already handle the sales model: revenue, AOV, LTV, repeat rate, top products
  • Publish once and the spreadsheet keeps itself current
  • The sheet pulls from us rather than being written to, so refreshes happen on Sheets' clock — roughly hourly
  • EAV is cloned as-is for anything you write yourself, so catalogue joins are still joins

The path that stays usable, step by step

  1. 1

    Connect your store to BeQuery

    Read-only MySQL credentials, host and port. BeQuery clones the database into an isolated PostgreSQL copy that only your team can reach — the shop itself is only ever read.

  2. 2

    Write the question as a query, not an export

    This is the step that decides whether the spreadsheet survives. In the SQL editor, aggregate first: revenue by month, margin by product, customers by cohort. You want the answer, which is hundreds of rows, not the raw orders table, which is hundreds of thousands.

  3. 3

    Publish it as a link, or export it once

    Save the query, then either export CSV or XLSX from the editor for a one-off, or publish the query as a CSV link. XLSX exports are capped at 500,000 rows; CSV will happily hand you more than a spreadsheet can hold, which is another reason to aggregate first.

  4. 4

    Paste the formula and let Sheets refresh it

    Drop =IMPORTDATA("…your link…") into a cell. Sheets fetches it on its own — roughly once an hour — so the tab stays current without anyone exporting anything again. Build your charts and pivots on top of that range. The link is a password in URL form: anyone who can see the formula can read the results, and you can revoke it from BeQuery at any time.

Is a spreadsheet the right home for this?

For Magento reporting, often yes — the sales tables suit it. The catalogue side is where it starts to strain.

Yes — Sheets earns its place

  • You're building margin or forecast models with columns Magento doesn't hold
  • Finance or buying want to work the numbers by hand
  • The output is a summary a person reads, not a dataset a tool consumes

You've outgrown it — use a dashboard instead

  • The report is really a dashboard: several people open it and nobody edits a cell
  • You're exporting order lines by the hundred thousand and pivoting in the sheet
  • Two exports of the same month have disagreed because the order states differed

Common questions

Why don't my product names come through in the export?

Because there is no name column on catalog_product_entity. The name is a row in catalog_product_entity_varchar whose attribute_id points at the 'name' attribute in eav_attribute, so it only appears if your query joins for it explicitly. The admin grid does that work for you, which is why grid exports have names and raw table exports don't.

Which order states should I count as revenue?

In practice: state in ('complete','processing') on sales_order, summed on base_grand_total. That excludes canceled, closed and pending_payment, which otherwise inflate every figure — and it's the definition BeQuery's Magento insights use, so the numbers stay consistent between the app and anything you export.

Can a sheet refresh itself, or is this manual every time?

It can refresh itself. Publish a saved query as a CSV link, paste =IMPORTDATA("…") into a cell, and Sheets refetches it on its own — about once an hour. Worth being precise about the direction: BeQuery doesn't write into your spreadsheet, the spreadsheet pulls from BeQuery. Nothing is pushed, nothing is stored in Google on our side, and revoking the link stops it immediately.

How safe is that link?

Treat it as a password. It's fetched anonymously by Google, so the URL itself is the credential — anyone holding it, including anyone who can open your spreadsheet and read the formula, can see that query's results. It's deliberately narrow: one saved query, read-only, row-capped, and revocable. We store only a hash of it, which is why it's shown once when you create it and can't be retrieved afterwards. For anything sensitive shared widely, use Looker Studio on a read-only connection instead.

Why not connect Sheets straight to my shop's database?

Google Apps Script can open a JDBC connection to MySQL, so it is technically possible. It means exposing your production database to Google's IP ranges, writing and maintaining the script, and having every refresh query the same server that serves your customers. It's a lot of moving parts to end up with a spreadsheet that occasionally times out.

How much data can a spreadsheet actually hold?

Google Sheets caps a spreadsheet at 10 million cells across all its tabs. That sounds enormous until you divide by columns: a 20-column export runs out at around 500,000 rows, and performance gets unpleasant well before the hard limit. Aggregated results are never a problem; raw order lines from a busy store are.

See it on your own store first

Free tier, no credit card, read-only access. Production is never written to — and you can explore the demo store before connecting anything.