TechEarl

Push a Google Sheet Edit to WordPress With onEdit

Fire a WordPress update the moment a cell changes: an installable onEdit Apps Script trigger that pushes the edited row to a REST endpoint, filters hard on the column, and paints the status cell red on failure.

Ishan Karunaratne⏱️ 5 min readUpdated
Share thisCopied
An installable onEdit Apps Script trigger pushing the edited Google Sheet row to a WordPress REST endpoint

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 call UrlFetchApp, so it cannot reach your site at all. Wire your push into a function named onEdit and 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

javascript
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.

The Google Apps Script editor showing the teOnEdit installable trigger function that filters on sheet and column then POSTs the edited row to a WordPress endpoint
The teOnEdit handler in the Apps Script editor. Registered as an installable On edit trigger, it can call UrlFetchApp; a simple onEdit cannot.

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 terminal showing the POST that the onEdit trigger sends returning HTTP 200 with updated post 5001, then wp post get confirming the post title changed to the value from the sheet
The request the trigger fires (POST to the endpoint) returns 200, and wp post get confirms the post title now matches the edited cell.

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; onEdit is for the one-row-at-a-time, edit-and-see-it-live workflow.

Almost always because it is a simple trigger. A function named onEdit runs in a restricted context that cannot call UrlFetchApp, so the request never goes out. Rename the handler and register it as an installable On edit trigger under Triggers; that version runs with your authorization and can make external requests.

No. An installable Apps Script trigger runs as you (the script owner) and pushes outward to your WordPress endpoint with a shared secret, exactly like the button version. A service account is only needed for the reverse direction, where WordPress reads the sheet itself, which is the pull model.

Filter early: check the sheet name and the edited column at the top of the handler and return immediately if they are not the ones you care about. Skip the header row. For rapid edits, debounce by storing the last-push timestamp per row in PropertiesService and skipping pushes that arrive too close together.

The handler writes 'failed' to the row's status cell and paints it red, so a failed push is visible in the sheet immediately rather than lost. A 200 from the endpoint paints it green. Because the request uses muteHttpExceptions, a non-200 is handled as data, not an uncaught error.

Push to a staging endpoint while you tune the column filter, and only point it at production once you trust which edits fire. The endpoint should still validate and sanitize every field it receives, since an edit-driven trigger can send a half-typed value mid-edit.

Sources

Authoritative references this article was fact-checked against.

TagsWordPressGoogle SheetsApps ScriptREST APIAutomation

Found this useful? Pass it on.

Copied

Ishan Karunaratne

Software Systems Architect · Senior Software Engineer · Engineering Leadership

Software systems architect and senior software engineer with more than two decades designing, building, and running production software, Linux systems, and DevOps infrastructure, and lately working AI into the stack. Now a CTO, though what I write here is drawn from the full arc of that work, across architecture, engineering, and operations, not any single job.

Keep reading

Related posts