Update:
To give more detail on the problem, put_records are charged based on the number of records (partition keys) submitted and the size of the records. Any record that is smaller than 25KB is charged as one PU (Payload Unit). Our individual records average about 100 Bytes per second. If we put them individually we will spend a couple orders of magnitude more money on PUs than we need to.
Regardless of the solution we want a given UID to always end up in the same shard to simplify the work on the other end of Kinesis. This happens naturally if the UID is used as the partition key.
One way to deal with this would be to continue to do puts for each UID, but buffer them in time. But to efficiently use PUs we'd end up with a delay of 250 seconds introduced in the stream.
The combination of the answer given here and this question gives me a strategy for mapping multiple user IDs to static (predetermined) partition keys for each shard.
This would allow multiple UIDs to be batched into one Payload Unit (using the shared partition key for the target shard) so they can be written out as they come in each second while ensuring a given UID ends up in the correct shard.
Then I just need a buffer for each shard and as soon as enough records are present totaling just under 25KB or 500 records are reached (max per put_records call) the data can be pushed.
That just leaves figuring out ahead of time which shard a given UID would naturally map to if it was used as a partition key.
The AWS Kinesis documentation says this is the method:
Partition keys are Unicode strings with a maximum length limit of 256 bytes. An MD5 hash function is used to map partition keys to 128-bit integer values and to map associated data records to shards.
Unless someone has done this before I'll try and see if the method in this question generates valid mappings. I'm wondering if I need to convert a regular Python string into a unicode string before doing the MD5.
There are probably other solutions, but this should work and I'll accept the existing answer here if no challenger appears.