Automation

Why your auto-update keeps looping on the same 25 rows

Key takeaways

  • A sync that keeps re-processing the same top rows has a state problem, not a timing or rate-limit problem: it never records what it already finished.
  • The fix is a skip list that marks rows already done this run or in a terminal status, so each batch picks the next rows that still need work.
  • Pair the skip list with auto-chaining, where each batch finishes, waits a short delay, then starts the next, so one click walks the whole sheet.
  • Terminal-status rows (Delivered, Expired, Cancelled and Refunded) should never be re-fetched, since their status is final and re-checking wastes API quota.
  • Fetch Order Tracking ships this skip-list-plus-auto-chaining pattern by default, processing about 25 orders per click until the sheet is done.

You hit “update tracking”, watch 25 rows refresh, click again… and it processes the same 25 rows. And again. The bottom 400 orders never get touched. If this is your life, you don't have a broken tool — you have a missing skip list.

This is one of the most common and most maddening problems in DIY tracking automation. Let's pull it apart: why it happens, why the obvious fixes don't work, and the pattern that makes a run start where it left off and finish.

Why batches loop instead of advancing

Most homemade tracking scripts process in batches because the AliExpress API and the Sheets API both punish you for hammering them — so you do, say, 25 orders per click with a small delay between calls. The bug is in how the script chooses which 25.

A naive script picks “the first 25 rows that have an order ID”. But the first 25 rows always have an order ID. So every click re-fetches the same top slice. The script has no memory of what it already finished, so it keeps starting from the top.

The loop isn't a timing problem or a rate-limit problem. It's a state problem: the run doesn't know what “done” looks like, so every batch is batch one.

The fixes that feel right but fail

People reach for a few intuitive patches first. They mostly make things worse:

  • “Just process more per click.” You raise the batch to 100 and immediately hit rate limits, lose orders mid-run, and still loop — now on a bigger slice.
  • “Sort so unprocessed rows float to the top.” Fragile. One manual edit, one new order inserted, and your ordering assumption collapses.
  • “Delete rows as I finish them.” Now you've destroyed your order history to work around a logic bug. Don't.

None of these give the run a way to say “I already handled this row, move on.” That's the only thing that actually fixes it.

The real fix: a skip list plus auto-chaining

You need two pieces working together.

1. A skip list

Before a batch starts, build a set of rows to ignore. A row goes on the skip list when it's either already up to date this run or in a terminal status — Delivered, Expired, or Cancelled & Refunded. Terminal rows never change again, so re-checking them is pure waste. The batch then picks the next 25 rows not on the skip list.

2. Auto-chaining

Instead of you clicking 16 times, the run finishes a batch, waits its delay (about 1.5 seconds is a safe, polite gap), then automatically kicks off the next batch — advancing through the sheet because the skip list keeps growing. One click, the whole sheet, done in one go.

That pairing is exactly what Fetch Order Tracking ships with out of the box: batch processing of roughly 25 orders per click, a 1.5-second delay to stay under the limits, auto-chaining so it walks the entire sheet, and a JavaScript skip-list so a run finishes instead of looping the same rows.

Why terminal-status rows belong on the skip list

This deserves its own callout because it's where a lot of “smart” scripts still leak time. Once an order is Delivered, Expired, or Cancelled & Refunded, its status is final. Re-fetching it costs an API call, eats into your rate budget, and can even flicker a clean row back to a stale value if the upstream response is briefly inconsistent.

  1. Mark terminal rows so the collector never touches them again.
  2. Keep your working set to the orders that can actually still move.
  3. Your batches get smaller and faster every day as more orders settle.

If those terminal rows are also clogging the sheet visually, the companion move is archiving them — see keeping cancelled and refunded orders from clogging your sheet.

How to tell your run is healthy

A correctly chained run has a clear signature:

  • The row count it touches shrinks across consecutive runs as orders reach terminal states.
  • Every active order gets refreshed exactly once per run — no row processed twice, none skipped that should've updated.
  • You click once and walk away. The progress indicator marches to the bottom and stops.

If you're still clicking the same button watching the same rows refresh, you're paying in time and API quota for a logic bug you can delete entirely.

Stop babysitting the button

A tracking sync should be a one-click chore, not a click-wait-click ritual that never reaches the bottom of the sheet. The skip-list-plus-auto-chaining pattern is the whole difference, and it's the kind of plumbing you really don't want to maintain by hand against two moving APIs.

Fetch Order Tracking handles it for you, and because there are no per-order fees, a run that clears 300+ orders costs you nothing extra — just a single click and the time you used to spend babysitting it back in your pocket.

Frequently asked questions

Why does my tracking sync keep processing the same 25 rows every click?

Because the script has no memory of what it already finished. A naive run picks the first 25 rows that have an order ID, but those rows always have an order ID, so every click re-fetches the same top slice. The cure is a skip list that records which rows are already done so the next batch advances to fresh rows instead of restarting from the top.

Will raising the batch size or sorting unprocessed rows to the top fix the loop?

No, those intuitive patches make things worse. A bigger batch just hits rate limits and loops on a larger slice, and a sort assumption collapses the moment someone edits or inserts a row. Deleting finished rows destroys your order history to work around a logic bug. Only a skip list gives the run a real way to say a row is handled and move on.

Should delivered and refunded orders be re-checked on every run?

No. Once an order is Delivered, Expired, or Cancelled and Refunded its status is final, so re-fetching it wastes an API call and can flicker a clean row back to a stale value. Put terminal-status rows on the skip list so the collector never touches them again, which makes your batches smaller and faster as more orders settle. Fetch Order Tracking does this automatically.

Related guides


Try Fetch Order Tracking More guides