Build an AI proofreading and style checker
Flag grammar and tone issues in a column of Northwind copy — every row gets feedback.
Published Oct 30, 2025
Northwind writes a lot of short copy — product blurbs, email snippets, social captions — and most of it lands in a spreadsheet before it lands anywhere else. A second pair of eyes would catch the typos and the off-brand phrasing, but a second pair of eyes is rarely free at the moment the copy is due.
This script gives every row that second read. It walks a column of draft
copy, asks Claude to check each entry for grammar and for Northwind’s house
tone — friendly, direct, no hype — and writes a one-line verdict into a
feedback column. Most rows come back “OK”; the ones that do not get a
specific note you can act on in seconds.
What you’ll need
- A Google Sheet with your draft copy in a column headed
textand an empty column headedfeedbackfor the results. - An Anthropic API key saved as
ANTHROPIC_API_KEYin Script Properties — see Store API keys and secrets securely.
The script
// The spreadsheet that holds the draft copy.
const COPY_SHEET_ID = '1abcCopyId';
// Northwind's house style — kept in one place so the prompt stays
// consistent and is easy to tweak.
const BRAND_TONE = 'friendly, direct, no hype';
/**
* Walks the copy column and writes a one-line proofreading verdict
* into the feedback column for every unchecked row.
*/
function proofreadColumn() {
const sheet = SpreadsheetApp.openById(COPY_SHEET_ID).getSheets()[0];
// 1. Read the whole sheet and split off the header row.
const [h, ...rows] = sheet.getDataRange().getValues();
if (!rows.length) {
Logger.log('No copy to proofread — nothing to do.');
return;
}
// 2. Map header names to column indexes so the code is not
// tied to a fixed column order.
const col = Object.fromEntries(h.map((k, i) => [k, i]));
// 3. Check each row: skip ones already done or with no text.
rows.forEach((r, i) => {
if (r[col.feedback] || !r[col.text]) return;
// 4. Ask Claude for a single line of feedback, or "OK".
const prompt =
'Proofread for grammar and Northwind brand tone (' + BRAND_TONE + '). ' +
'Return one line of feedback, or exactly "OK" if there is nothing to fix.' +
'\n\n' + r[col.text];
sheet.getRange(i + 2, col.feedback + 1).setValue(callClaude(prompt));
});
Logger.log('Proofreading pass complete.');
}
/**
* Minimal Anthropic API call. Haiku is plenty for short copy checks,
* and the key lives in Script Properties — never in the code.
*/
function callClaude(prompt) {
const key = PropertiesService.getScriptProperties()
.getProperty('ANTHROPIC_API_KEY');
const res = UrlFetchApp.fetch('https://api.anthropic.com/v1/messages', {
method: 'post',
contentType: 'application/json',
headers: { 'x-api-key': key, 'anthropic-version': '2023-06-01' },
payload: JSON.stringify({
model: 'claude-haiku-4-5-20251001',
max_tokens: 100,
messages: [{ role: 'user', content: prompt }],
}),
muteHttpExceptions: true,
});
return JSON.parse(res.getContentText()).content[0].text.trim();
}
How it works
proofreadColumnopens the copy spreadsheet and reads the full data range, splitting the header row from the data.- If there are no data rows, it logs a message and stops.
- It builds a name-to-index map from the header so the script keeps working even if columns are reordered.
- It walks each row, skipping any that already have feedback or have no text — so a re-run only processes new copy and never burns API calls twice.
- For each remaining row it builds a prompt carrying the house tone from
BRAND_TONE, and asks for a single line of feedback or"OK". - It calls Claude Haiku — fast and cheap, which matters when you are scoring one row at a time — and writes the verdict straight into the feedback cell.
Example run
Say the text column holds these drafts:
| text | feedback |
|---|---|
| Our brand new platform revolutionises how you work! | |
| The booking form takes about two minutes to fill in. | |
| We was unable to process you’re request. |
After a run, the feedback column fills in:
| text | feedback |
|---|---|
| Our brand new platform revolutionises how you work! | Drop “revolutionises” — too much hype for Northwind’s tone. |
| The booking form takes about two minutes to fill in. | OK |
| We was unable to process you’re request. | Grammar: “We were unable to process your request.” |
The “OK” rows you skip; the two flagged rows take a few seconds each to fix.
Run it
This is an on-demand job — run it whenever a batch of copy is ready:
- In the Apps Script editor, select
proofreadColumnand click Run. - Approve the authorisation prompt the first time.
- Read down the
feedbackcolumn and act on anything that is not “OK”.
To let writers run it themselves, add a custom menu to the sheet:
function onOpen() {
SpreadsheetApp.getUi()
.createMenu('Copy tools')
.addItem('Proofread column', 'proofreadColumn')
.addToUi();
}
Watch out for
- The verdict is one line, by design. It flags the issue but does not rewrite the copy — a human still makes the edit, which keeps the writer in control.
- Already-filled
feedbackcells are skipped. If you edit a row’s text and want a fresh check, clear its feedback cell first. - Tone is subjective. Claude follows
BRAND_TONE, but if its calls feel off, give it a worked example of good and bad Northwind copy in the prompt. - A very large sheet means a long run. Apps Script caps execution at roughly six minutes, so for hundreds of rows process them in batches.
Related
Build an AI keyword-clustering tool
Group Northwind's tracked search terms into topic clusters — for SEO content planning.
Updated Feb 19, 2026
Build an AI customer-churn predictor
Flag at-risk Northwind accounts from behavioural signals — usage, support tickets, billing.
Updated Feb 15, 2026
Build a context-aware AI data validator
Catch values that look wrong in context — '£10' for a Northwind retainer is suspicious.
Updated Feb 7, 2026
Auto-categorize a photo library
Tag Northwind Drive images by visual content — product, team, event, behind-the-scenes.
Updated Feb 3, 2026
Build an AI bug-triage system
Categorise and prioritise Northwind's reported issues automatically — type, severity, owner.
Updated Jan 22, 2026