Reading time on this site is computed twice, on purpose. The editor widget shows a live estimate while you type; a webhook writes the authoritative value when the entry is published. Both share one formula — max(1, round(words / 200)) over the body text only — so the preview never disagrees with the stored value.
The loop problem
The webhook fires on publish, updates the entry, and re-publishes it. That second publish fires the webhook again. Left alone, this is a perfectly efficient way to spend your rate limit forever.
The fix is a guard, not cleverness:
Recompute the value from the incoming payload.
If the stored
estimatedReadingTimealready equals the computed one, return 200 and do nothing.Only write and re-publish when the value actually changed.
The second webhook invocation finds the value already correct and stops. The loop terminates in exactly one extra call — bounded, boring, correct.
Why not compute it in the frontend?
Because then it isn't data. Stored on the entry, reading time is queryable, sortable, and available to any consumer — feeds, search, the mobile app that doesn't exist yet. Derived-at-render values evaporate the moment a second consumer shows up.
If two systems need the same derived value, derive it once and store it where both can see it.