To push a change to WordPress the instant a cell changes (no button, no batch), use an installable onEdit Apps Script trigger that POSTs the edited row to a WordPress REST endpoint. Two things make or break it: it has to be the installable trigger, not the simple one, because a simple onEdit cannot make external requests; and it has to filter hard on the sheet and column, because onEdit fires on every edit and you do not want a stray note in column G pushing a row to your live site.
This is the edit-driven version of pushing from a button. It reuses the same WordPress endpoint; only the trigger changes.
Simple vs installable: the trap
Apps Script has two onEdit triggers and they are not interchangeable:
- A simple trigger (a function literally named
onEdit) runs automatically but in a restricted security context. It cannot callUrlFetchApp, so it cannot reach your site at all. Wire your push into a function namedonEditand it will silently do nothing. - An installable trigger (any function name, registered under Triggers) runs with your full authorization and can make external requests. This is the one you need, and installing it is what prompts the one-time authorization.
Name the handler something other than onEdit (so it is unmistakably the installable one) and register it by hand.
The trigger
const TE_ENDPOINT = PropertiesService.getScriptProperties().getProperty('TE_ENDPOINT');
const TE_KEY = PropertiesService.getScriptProperties().getProperty('TE_KEY');
/** Installable onEdit handler: push the edited row to WordPress. */
function teOnEdit(e) {
const sheet = e.range.getSheet();
// Filter hard. onEdit fires on every edit in the spreadsheet.
if (sheet.getName() !== 'Sheet1') return; // right tab only
const row = e.range.getRow();
const col = e.range.getColumn();
if (row === 1) return; // skip the header
const WATCH = [2, 3]; // only these columns push
if (!WATCH.includes(col)) return;
const head = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0];
const vals = sheet.getRange(row, 1, 1, sheet.getLastColumn()).getValues()[0];
const get = name => vals[head.indexOf(name)];
const ok = tePost({ post_id: get('post_id'), title: get('new_title') });
// Feedback: paint the status cell so a failed push is impossible to miss.
const statusCol = head.indexOf('status') + 1;
if (statusCol > 0) {
const cell = sheet.getRange(row, statusCol);
cell.setValue(ok ? 'updated' : 'failed');
cell.setBackground(ok ? '#d9ead3' : '#f4cccc');
}
}
function tePost(payload) {
const res = UrlFetchApp.fetch(TE_ENDPOINT + '/post', {
method: 'post',
contentType: 'application/json',
headers: { 'X-TE-Key': TE_KEY },
payload: JSON.stringify(payload),
muteHttpExceptions: true,
});
return res.getResponseCode() === 200;
}In the Apps Script editor, paste this, then Triggers → Add Trigger: choose teOnEdit, event source From spreadsheet, event type On edit. The first save prompts you to authorize the script (it needs to read the sheet and make external requests). That authorization is the moment the trigger becomes installable and gains the ability to call your endpoint.

It really fires to WordPress
The endpoint is the same one-file MU-plugin the button version uses. When the trigger fires, it makes exactly this authenticated request, and the post updates. Here is that request landing on the live endpoint and the title changing on the WordPress side:

A 200 paints the status cell green; anything else paints it red, so an editor sees instantly whether their change reached the site. That feedback loop is the whole reason to push on edit rather than silently in the background.
Keep it from firing too much
onEdit is noisy by design, and an unfiltered handler is a way to hammer your endpoint (and your site) on every keystroke-commit. The guard rails:
- Filter on sheet name and column first, before any work. The early returns above cost nothing and stop 95% of irrelevant fires.
- Ignore the header row so reformatting row 1 never pushes.
- For a column a human edits rapidly, consider a tiny debounce (store the last push time per row in
PropertiesService) so ten quick edits become one push. - Reach for the batch push or the server-side pull when you are changing many rows at once;
onEditis for the one-row-at-a-time, edit-and-see-it-live workflow.
Sources
Authoritative references this article was fact-checked against.
- Installable Triggers - Google Apps Scriptdevelopers.google.com
- Simple Triggers - Google Apps Scriptdevelopers.google.com
- UrlFetchApp - Google Apps Script Referencedevelopers.google.com
- register_rest_route() - WordPress Developer Referencedeveloper.wordpress.org





