I was skipping some complexity. Something like b/scrypt are more than just hashing. They are key derivation functions, which look similar to hash functions on their inputs.
bcrypt for instance generates a random salt on the input and stores it in the output.
A lot of password hashing APIs have two calls - one where you provide the password (and possibly work factor) and it returns a hash that includes a randomly generated salt, and another where you pass in the hash (which includes the salt and work factor if any) and the password which returns true/false.