In the password reset example, something is stored: The new password hash. Hence the example of including the old password hash, as a value that will be invalidated when the request has been processed.
Otherwise you need to bump a value on accessing the URL, but you still benefit by not having to create book-keeping information when creating the URL.
This is solved by rate limiting, I suppose. Feels like something that should've been included in the article.
What does this have to do with whether you're storing tokens or using a hash?
Of corse, I'm not arguing against rate limiting.
The question is not how to make links that expire after a time, the question is how to make links that expire after a fixed number of uses (e.g. one use).
Suppose your link expires in 30 minutes (or any other time). What stops me from using that link 30 times, once per minute?
0. https://news.ycombinator.com/item?id=9055749
1. https://github.com/django/django/blob/master/django/contrib/...
It's still not clear to me, however, how to generalize this technique to create limited-use links that are not necessarily for resetting passwords.