Spotify's timestamp is the end of a play, not the start
A field that every document described the same way, and that nothing in the app could tell you was wrong, because every number it produced still looked entirely reasonable.
In this piece
Quilore reads the data export Spotify gives you and turns it into your complete listening history. It is live at quilore.com, it is free, and it runs entirely in your browser.
This is the bug that took longest to see, because nothing about it looked like a bug. Every screen worked. Every number was plausible. All of them were wrong by about the length of a song.
What the export actually contains
Ask Spotify for your extended streaming history and you get a zip of JSON. One
record per play, going back to the day you joined. Each record carries the track,
the artist, how many milliseconds were played, why playback ended, and a field
called ts.
ts is the timestamp. That is all Spotify says about it.
Everything we had written said it was a start time
The product spec said so. The data engine spec said so. The technical contract
the parser was written against said so. So the parser read ts as the moment the
track started, put the play in the day that timestamp fell in, and built sessions
by measuring the gap to the next one.
It is the moment the track stopped.
Why nothing looked broken
This is the part worth sitting with, because it is the failure mode the whole product exists to avoid.
Reading the wrong end of a play does not produce an error. It shifts every derived number by roughly one track length, and one track length is small enough that everything downstream stays believable. Sessions were slightly wrong. Streaks were slightly wrong. A play just after midnight was filed under the wrong day. The hour-of-day chart leaned a few minutes late.
Nothing in that list announces itself. You would have to already know the answer to notice the question.
How it was settled
Not by reading the documentation, which does not cover it, and not by eye.
Take every pair of plays that ran back to back, the way autoplay does, and
measure the gap between the two timestamps. If ts is a start time, that gap
should match the duration of the previous track. If it is an end time, it
should match the current one.
Across 137,153 such pairs in one real export:
| Reading | Gap matched |
|---|---|
ts is the end of the play | 83.5% |
ts is the start of the play | 0.3% |
The second measurement is the one that removes any doubt. If you treat ts as a
start time, plays begin overlapping each other: the export claims you were
listening to two things at once for 13.6% of your total listening time. Read as
an end time, apparent overlap falls to 0.42%, which is about what you would
expect from rounding and from a device handing over to another device. One person
has one pair of ears. A model that says otherwise for an eighth of their life is
not a model, it is a defect.
What changed
startEpochSec(tsIso, msPlayed) is now the only supported way to get a start
time anywhere in the engine, and it is the single place the subtraction happens.
A track that finishes at four minutes past midnight is attributed to the previous
day, which is what the person who played it would say happened.
Why this particular bug mattered more than most
The app exists because of a specific complaint about services in this category: their numbers are wrong in ways that look right.
That is not a criticism of the people who build them so much as a description of what they are working with. Spotify’s Web API will tell you that a track played. It will not tell you for how long, so listening time has to be inferred. And the endpoint that reports recent plays is capped. Spotify’s own developer documentation says so:
This endpoint has only ever returned 50 items total.
Poll it more slowly than the person listens and the plays in between are gone for good. The export has neither problem: it carries the true millisecond duration of every play and it is complete back to the beginning.
So accuracy is the entire claim this product makes. Shipping it with the timestamps read off the wrong end of every song would have been the same defect we set out to avoid, arrived at by a different route.
We have not published the complaint record behind that yet. The corpus exists as themes rather than as counted, dated, quotable reviews, and until it is gathered properly this stays a stated reason rather than an evidenced one. That teardown is owed before anything on this site treats it as settled.
What we are not claiming
Spotify does not document which end of a play ts marks, so this is a measured
finding rather than a confirmed one. It rests on one export of 160,634 records
from one account. The agreement is strong and the overlap check is hard to argue
with, but if someone has an export where the gaps line up with the previous
track, we would genuinely like to see it.
Quilore is at quilore.com. It is free, it does not ask for an account, and your listening history does not leave your browser. You need your own Spotify export to use it, and that takes a few days to arrive, which is the honest cost of the accuracy this post is about.
Written by Adeboye Oluwatimileyin and Moses-Azuoru George, the two people who are Emberfig. We make money from AdMob inside the apps. This site carries no ads yet. You never pay us and we never sell your data.