Results runner
How runResults schedules work within a time budget, what it returns, and how retention is swept.
runResults from /rsc computes results in-process. You call it from a route you schedule and authenticate; no Payload jobs queue and no HTTP call back into your app is involved.
runResults(payload, { budgetMs?, experimentId?, sweep? }?)Prop
Type
What it returns
| Field | Meaning |
|---|---|
ran | Tests whose results were refreshed (including refreshes with nothing new to write) |
skipped | Tests not processed this run, because of an error or the budget running out |
failed | Non-budget errors, including scheduling and retention-sweep failures |
swept | Events deleted by retention |
sweepAttempted | Whether the sweep ran |
processedIds | Ids of tests that got new snapshots (at most 100 listed) |
budgetExhausted | The budget ran out before all work was done |
unavailableMetrics | Goals whose sources failed or timed out |
Working within the budget
- The oldest snapshots are processed first.
- The remaining budget is checked before each goal is read.
- Waiting for your
queryandcovariatepromises and for database connections is bounded, and SQL runs with a statement timeout set to the remaining budget. Hung work cannot keep the runner past its budget. - Part of the budget (up to one second, or a quarter of short budgets) is reserved for the retention sweep.
- A timed-out source is listed in
unavailableMetrics. That test's new snapshots are not written, its previous snapshots remain, andbudgetExhaustedis set.
Racing your promise does not cancel the work behind it. Give your own data clients timeouts too.
Freezing and grace
Analysis uses each round's frozen configuration and its actual exposures. A finished round's window ends at its end time; later exposures and conversions are excluded. Its final snapshot waits a 24-hour grace period for late browser beacons, then stays unchanged on later runs.
Retention
Events are deleted events.retentionDays (default 180) days after a test's final round ends. Nothing is swept while any round of a test is open, including a long-running holdout round. The sweep runs in batches inside the reserved part of the budget, and logs a warning when the budget ends before it deletes anything.
Recomputing one test
POST /xp/experiments/:id/recompute (needs access.manage) runs results for one test without sweeping and returns { snapshotId }. Failed or budget-exhausted work returns an error. Frozen rounds stay frozen.
Scheduling
Schedule the route at least hourly, because the admin tells editors to expect first results within the hour after Start. Your host owns the schedule; the plugin does not set it up. On Vercel, add the route to your cron configuration and check its authentication in the handler.

