Blog
Export responses keeps the oldest rows, not the newest
Tuesday 09:05 the export says truncated at 10000 of 13482. The 3482 rows it dropped are not the oldest ones. They are everything since 5 July, including every answer about the September metering change.
Tuesday, 3 November 2026, 09:05 UTC. Priya runs support at Tallyboard, a 28-person time-tracking SaaS. Survey product-nps, running since the product launched. Tallyboard migrated three years of Delighted history into UserVane in July 2025, following the same import path as the move-off-Delighted post: 6,200 rows, scores and comments and original timestamps intact. A per-seat metering rewrite shipped Tuesday, 1 September 2026, 14:00 UTC, replacing flat per-workspace billing. Support has been fielding metering complaints since. Priya wants to know whether the survey caught any of it, so she opens the Surveys page and clicks Export responses.
The browser downloads uservane-export.json. The header reads: projectId, exportedAt: "2026-11-03T09:05:00.000Z", count: 10000, total: 13482, truncated: true, and a note: "Export capped at 10000 rows (of 13482). Narrow by survey or respondent, or page via the API." Ten thousand of thirteen thousand four hundred eighty two looks like most of it. She searches the text field across all 10,000 rows for "metering" and "billing." Zero hits.
09:14. Slack, drafted: "Checked the export. Nobody mentioned metering in survey comments. Billing confusion didn't show up here, might be a support-only pattern."
The weak version, and what it costs by 09:20
The read that produced that message looks like this:
10,000 of 13,482 rows is most of the history. Zero hits for metering or billing across that many rows is a real signal, not a gap.
Truncated just means the file is a bit short of complete. Short of complete is still close enough to search.
The read that keeps 09:14 honest looks like this:
The export is ordered oldest first and stops at 10,000. The newest row in this file is
2026-07-05T22:40:00.000Z.exportedAtis2026-11-03T09:05:00.000Z, nearly four months later.The metering rewrite shipped 1 September. Every response dated after 5 July, including every answer about metering, is in the 3,482 rows this file does not contain.
Zero hits for "metering" is not evidence nobody said it. It is evidence the file ends two months before anyone could have said it.
The weak version costs the thread its only reason to stay open. Support's own ticket queue has eleven mentions of metering since the ship. The survey export, read as "basically complete," closes the question in the other direction, and the two sources never get compared.
Oldest first, whole project, no filter
The export route pulls every response in the project with one query, ordered by timestamp ascending, capped at a constant named EXPORT_MAX_ROWS: "Max rows returned by privacy/dashboard JSON export in one response," set to the same 10,000-row ceiling the Worker uses everywhere it reads raw rows, "to prevent unbounded D1 result sets on Scale volumes." It is not scoped to a survey, a channel, a source, or a date. The dashboard's Export responses button calls it with nothing but the project id.
Tallyboard's 13,482 breaks down in time order:
- 6,200 imported Delighted rows, oldest in the project by construction. The import preserved their real timestamps, which is the entire point of that migration path.
- 3,800 measured UserVane rows from July 2025 through 5 July 2026.
- 3,482 measured rows from 5 July 2026 through this morning, including everything about the September rewrite.
The first two groups add up to exactly 10,000. The export fills its ticket with those before it gets anywhere near the third group. Preserving three years of Delighted history was the right call for continuity; it also means that history has first claim on every row the export can hand back, and the live, current answers are what waits at the door.
Narrowing helps only if it fits
The JSON export has one more lever than the dashboard button exposes: GET /v1/privacy/export accepts surveySlug, respondentId, or email to scope the pull (see the privacy export reference). Scoping to one survey or one person shrinks the denominator, and a single respondent's full history, oldest first, is the right order for a DSAR request. It does not change the ordering rule. A single long-running survey with more than 10,000 responses of its own would hit the identical ceiling from the identical end. There is no parameter on this endpoint that asks for the newest rows instead of the oldest, and no cursor to step past the cap. "Page via the API" means re-scope the request, not turn to page two. That is the limit.
Bottom line: before trusting an absence in an export, compare its newest row to exportedAt. A four-month gap between the two means four months of answers are missing, from the end you were about to rely on. Tallyboard's metering complaints were never missing from UserVane. They were only missing from the file.