On-Page SEO Audit in Google Sheets for 100 URLs
Run an on-page SEO audit in Google Sheets. Paste 120 URLs, add five formulas, and flag every bad title, description, and H1 in about 10 minutes.
Phil Nguyen · 15 min read

A new client hands you a 120-page site and asks what is wrong with it. You need every page title, every meta description, and every H1, with the character counts, before you can say anything useful. Exporting a crawl and pivoting it into something a client can read costs you most of an afternoon.
There is a shorter path. Paste the URL list into column A of a Google Sheet, add five formulas, and let the sheet fill itself while you write the summary.
Here is the formula the whole audit is built on.
Formula
=GETMETATITLE(A2)Fetches the page at the URL in A2 and returns its title tag. One call, one integration credit, one cell.
What comes back
| A (URL) | B (=GETMETATITLE result) |
|---|---|
| https://fennwoodflooring.com/ | Fennwood Flooring - Hardwood, Vinyl and Tile in Columbus OH |
| https://fennwoodflooring.com/services/hardwood-refinishing | Hardwood Floor Refinishing Services |
| https://fennwoodflooring.com/services/vinyl-plank | Vinyl Plank |
| https://fennwoodflooring.com/about | Fennwood Flooring | Family Owned Since 1994 | Serving Central Ohio and the Surrounding Counties |
You can already see two problems in four rows. Row 3 is a bare service name with no location. Row 4 runs far past what Google will show. Now do that for all 120 pages.
Do it for the whole column
Paste your URL list into A2 and down. Put three formulas across row 2, then select them and drag to the last row.
Formula
=GETMETADESCRIPTION(A2)Returns the meta description. Put this in E2.
Formula
=GETH1(A2)Returns the H1 heading. Put this in H2. If the page has more than one H1, they come back joined by line breaks in the same cell.
Three formulas, 120 rows, 360 cells that fill themselves. Each cell is one page fetch and one integration credit, so a 120-page audit of all three fields costs about 360 credits. Solo's 10,000 credits a month covers about 27 audits that size.
Two things to know before you drag.
First, these formulas run right in the cell and give you the answer straight away. They are not background jobs, so there is no placeholder to wait on and nothing lands in a separate tab.
Second, they are not frozen. The Keep Formula setting does not apply to the page-scraping formulas, so a recalculation can fetch the pages again. Results are cached for 30 minutes per formula and URL, so a quick re-run costs nothing. Once the columns look right, select B through I, copy, then Edit, Paste special, Values only. Now the audit is data and it will never re-bill.
Tip
Sort column A before you drag. Grouping by section (blog, services, locations) makes the flags far easier to read later, and the formulas do not care about order.
Flag what is broken
Raw titles are not an audit. What the client wants is a column that says what to fix. That part is free, because it is plain Google Sheets.
Formula
=LEN(B2)Character count of the title in B2. Put it in C2. Costs nothing, no credits, no tokens.
Formula
=IF(C2=0,"Missing",IF(C2>60,"Too long",IF(C2<30,"Too short","OK")))Turns the count into a verdict. Put it in D2 and drag it down with the rest.
What comes back
| B (Title) | C (=LEN) | D (Flag) |
|---|---|---|
| Fennwood Flooring - Hardwood, Vinyl and Tile in Columbus OH | 58 | OK |
| Hardwood Floor Refinishing Services | 35 | OK |
| Vinyl Plank | 11 | Too short |
| Fennwood Flooring | Family Owned Since 1994 | Serving Central Ohio and the Surrounding Counties | 94 | Too long |
The same two formulas work on the description in F and G, with 155 and 70 as the cut-offs instead of 60 and 30. Pick your own numbers. The point is that the sheet applies them the same way to every row, which is the part a human audit gets wrong around page 40.
The H1 needs a different test. Google is fine with a page having no H1, but your client's template probably should not skip one, and two H1s on a page usually means a stray heading in a banner.
Formula
=IF(H2="No elements found.","Missing",IF(ISNUMBER(SEARCH(CHAR(10),H2)),"More than one","OK"))GETH1 returns the exact text 'No elements found.' when a page has no H1, and joins multiple H1s with line breaks. This reads both cases. Put it in I2.
What comes back
| A (URL) | H (=GETH1 result) | I (Flag) |
|---|---|---|
| /services/hardwood-refinishing | Hardwood Floor Refinishing | OK |
| /services/vinyl-plank | Vinyl Plank Flooring | OK |
| /about | No elements found. | Missing |
| /locations/dublin-oh | Dublin OH Flooring Get a Free Estimate | More than one |
Filter column D or I to "Too long" and you have the fix list. That is the deliverable the client actually reads.
Four fields in one call
When you only need a quick look at a handful of pages, there is a cheaper way to get all of it at once.
Formula
=PAGEDATA(A2, A3, A4, A5)Fetches each page once and returns the title, meta description, H1, and status for all of them as one table. One credit per URL, not one per field.
What comes back
| URL | Title | Meta Description | H1 | Status |
|---|---|---|---|---|
| https://fennwoodflooring.com/ | Fennwood Flooring - Hardwood, Vinyl and Tile in Columbus OH | Hardwood, vinyl and tile installation across Central Ohio since 1994. | Flooring That Lasts | Success |
| https://fennwoodflooring.com/services/vinyl-plank | Vinyl Plank | Vinyl Plank Flooring | Success | |
| https://fennwoodflooring.com/about | Fennwood Flooring | Family Owned Since 1994 | Serving Central Ohio and the Surrounding Counties | Meet the Fennwood family and our install crews. | No H1 | Success |
| https://fennwoodflooring.com/blog/vinyl-vs-laminate | Vinyl vs Laminate: Which Holds Up Better? | A side by side comparison for busy households. | Vinyl vs Laminate | Success |
That is one page fetch per URL instead of three, so it is a third of the credits for the same four fields. It comes with a catch: PAGEDATA takes each URL as its own argument and does not accept a range. =PAGEDATA(A2:A121) will not audit 120 pages, so use it for a batch you can list by hand and use the drag-down columns for the full site.
The result spills across five columns and down one row per URL, plus a header row. Leave that space empty or Sheets shows a #REF! error.
Rewrite the flagged titles
Now the part that used to be the actual afternoon. Every flagged row needs new copy, and there are usually 30 of them.
Formula
=IF(D2="OK","",AITEXT("Rewrite this page title for SEO in under 60 characters. Keep the brand name Fennwood Flooring. Reply with the title only: " & B2))Only fires on rows you flagged, so you never pay to rewrite a title that was already fine. Put it in J2.
What comes back
| B (Title) | D (Flag) | J (=AITEXT result) |
|---|---|---|
| Fennwood Flooring - Hardwood, Vinyl and Tile in Columbus OH | OK | |
| Vinyl Plank | Too short | Vinyl Plank Flooring Installation | Fennwood Flooring |
| Fennwood Flooring | Family Owned Since 1994 | Serving Central Ohio and the Surrounding Counties | Too long | Family Owned Flooring in Central Ohio | Fennwood |
| Flooring Blog | Too short | Flooring Care and Buying Guides | Fennwood Flooring |
The IF wrapper matters more than it looks. Without it you run 120 AI calls to rewrite 30 titles. With it you run 30.
More prompts for this kind of work are in the AI for SEO guide. The same pattern writes the missing descriptions into column K. Feed it the title rather than the old description, because the pages that need a description usually do not have one to improve.
Formula
=IF(G2="OK","",AITEXT("Write a meta description under 155 characters for a page titled: " & B2 & ". Plain language, one clear reason to click. Reply with the description only."))Put it in K2. Spends AI tokens, not integration credits.
Note
Every AI formula runs on every plan, including Free, which comes with 20,000 one-time tokens to try it. Solo's 3.25M tokens a month is roughly 7,500 to 15,000 short pieces of copy like these titles and descriptions.
See what you are up against
A title can pass every character check and still lose. Before you hand the rewrites over, look at what currently ranks for the page's main keyword.
Formula
=GSEARCH("hardwood floor refinishing columbus oh", "us")Pulls the live Google organic results for one query. 10 results per call, 3 integration credits.
This one behaves differently from everything above. The cell shows ⏳ Scraping... while the job runs, then the results land in a new tab called Google Search Results, and the cell you typed in ends up reading ✅ 10 results → Google Search Results.
What comes back (first columns of the results tab)
| Source | Position | Title | URL | Snippet |
|---|---|---|---|---|
| Sheet1!M2 | 1 | Hardwood Floor Refinishing in Columbus, OH | Buckeye Floors | https://buckeyefloors.com/refinishing | Dustless refinishing for Columbus homes, finished in 3 days. |
| Sheet1!M2 | 2 | Columbus Hardwood Refinishing Costs 2026 | https://ohiofloorguide.com/costs | What homeowners paid last year, by square foot. |
| Sheet1!M2 | 3 | Refinish or Replace? A Columbus Guide | https://fennwoodflooring.com/blog/refinish-or-replace | When refinishing is worth it and when it is not. |
More columns follow, including the displayed URL, highlighted snippet words, and sitelinks. The Source column names the cell that asked for each row, so a whole column of GSEARCH formulas collects into one tab and you can still tell the queries apart. Rows arrive in the order the jobs finish, not the order you typed them, so sort by Source before you read it.
Three lookups at 3 credits each is 9 credits. Run it for your 10 priority pages, not all 120.
Turn it into a client summary
The fix list is for you. The client wants one number per problem. Build that on a second tab with native Sheets formulas, which cost nothing to run.
Formula
=COUNTIF(Audit!D:D, "Too long")Counts one flag across the whole audit. Repeat it for each flag value and each flag column to build the scoreboard.
What comes back
| Check | Too long | Too short | Missing | OK |
|---|---|---|---|---|
| Title | 11 | 24 | 0 | 85 |
| Meta description | 6 | 9 | 31 | 74 |
| H1 | 4 | 112 |
That table is the first slide of the onboarding deck, and it took one formula copied across a grid. It is the same reporting loop the rest of the agency workflows are built on. Next month you paste a fresh URL list into the audit tab and the same counts update.
For the handful of pages that look thin, one more formula tells you whether the page has any structure at all.
Formula
=GETHEADINGS(A2)Returns every heading on the page as a table of level and text. Run it on a single URL in an empty area, not down a column, because it spills.
A service page with an H1 and nothing else is the one your client will ask about first. Now you can point at it. For anything the named formulas do not cover, such as a canonical tag or a schema block, pull it with a CSS selector.
Compared to Screaming Frog
Screaming Frog is the tool most SEO teams reach for here, and it is very good at this. Being fair about it is more useful than pretending otherwise.
| SheetMagic in Sheets | Screaming Frog SEO Spider | |
|---|---|---|
| What it is | Formulas inside a Google Sheet | A desktop app for Windows, macOS and Linux |
| Best for | A known URL list, client-facing output, rewriting as you flag | Crawling a whole site and finding what you did not know to look for |
| Coverage | The fields you write a formula for | Over 300 SEO issues, per its site |
| Price shape | Monthly plan with a usage allowance | Free up to a 500 URL crawl, then a licence at £199 or $279 per year |
| Output | Already a shareable sheet | A crawl you export |
| Fixes the copy | Yes, AITEXT column next to the flag | No, it reports |
Screaming Frog's own pages describe it as "the industry leading website crawler" and say it finds broken links, redirects, duplicate content, and structural problems, with JavaScript rendering and Search Console integrations on the paid licence. Nothing in this post replaces that. If you need to know every 404 and every redirect chain on a site you have never seen, crawl it.
What the sheet gives you is the other half of the job. The crawler tells you the title is 94 characters. It does not write you a better one. Here the flag and the rewrite sit in adjacent columns, the client can open the link without installing anything, and next month you paste a fresh URL list into the same file.
Use both. Crawl with the Frog, audit and fix in the sheet.
Copy this template
Eleven columns. Column A is your URL list. Row 2 holds the formulas, and you drag row 2 down to your last URL.
| Column | Header | Formula in row 2 |
|---|---|---|
| A | URL | your list, pasted |
| B | Title | =GETMETATITLE(A2) |
| C | Title length | =LEN(B2) |
| D | Title flag | =IF(C2=0,"Missing",IF(C2>60,"Too long",IF(C2<30,"Too short","OK"))) |
| E | Description | =GETMETADESCRIPTION(A2) |
| F | Description length | =LEN(E2) |
| G | Description flag | =IF(F2=0,"Missing",IF(F2>155,"Too long",IF(F2<70,"Too short","OK"))) |
| H | H1 | =GETH1(A2) |
| I | H1 flag | =IF(H2="No elements found.","Missing",IF(ISNUMBER(SEARCH(CHAR(10),H2)),"More than one","OK")) |
| J | Suggested title | =IF(D2="OK","",AITEXT(...)) |
| K | Suggested description | =IF(G2="OK","",AITEXT(...)) |
Download the template CSV. Import it into a blank sheet (File, Import, Upload) and the formulas are ready to drag down.
What to watch for
- PAGEDATA needs separate arguments.
=PAGEDATA(A2, A3, A4)works. A range like=PAGEDATA(A2:A121)does not, because the whole range arrives as one value. For a long list, use the drag-down columns instead. - Nothing here is frozen automatically. Keep Formula does not apply to the page-scraping formulas, so paste your results as values once the audit looks right. Otherwise a recalculation can fetch every page again.
- Some pages cost more than one credit. A page that needs JavaScript rendering costs 2, and a heavily bot-protected page can settle as high as 9. You are charged what the fetch actually cost, not a flat rate.
- Spilling results need room. PAGEDATA and GETHEADINGS spread across columns and down rows. If anything is in the way, Sheets shows
#REF!instead of the table.
Note
A 120-page audit across title, description and H1 is about 360 integration credits. The free allowance is 100 one-time credits, enough to run one column over roughly 33 pages and see the whole thing work. Solo's 10,000 credits a month covers about 27 audits that size.
Run this on your own site first, before a client's. You will find a "Too long" row you have been looking at for two years. When you want the rest of the page, not just its metadata, extract the full content into a cell and ask AITEXT about it, or read the deeper guide to bulk meta extraction.
Every formula in this article runs on the free allowance. Install from the Google Workspace Marketplace, no card, no expiry.
Install free for Google SheetsAbout the author
Phil NguyenPhil builds SheetMagic, the Google Sheets add-on that brings AI and web scraping into the spreadsheet, used by 6,300+ people. He writes these guides from the formulas he runs on his own sheets.
