Skip to content(if available)orjump to list(if available)

Making Beautiful API Keys

Making Beautiful API Keys

96 comments

·January 10, 2025

stitched2gethr

I guess this is an unpopular opinion given the existing comments, but I don't get it. They spent time and energy on this instead of writing features or fixing bugs and I don't think it matters at all. The only interaction I have with my API keys is copying them from one place to another. If I need to manually identify a key then putting the first or last few chars, whatever they are, in my working memory is more than sufficient.

alt227

Agreed. I cant see why anyone thinks this was worth spending time on rather than just using the standards which everybody else does.

Nobody types out api keys so there is no need to make them friendly to say or remember. After you have copied and pasted them once into your db, you are never going to seee or use that string again!

wink

Totally worth spending a single day at some hackathon or slack time event... but not more. And it looks like a lot more.

thiht

Does it? We once discussed making our ids (UUIDs) prettier, came to the same conclusion of using some base64 variant in 5 minutes, and came across base32 10 minutes later, and the final implementation is pretty trivial.

This is a 1 day job at most to get something polished (assuming there’s no migration/retrocompatibility constraints)

Retr0id

All things in moderation. I think they're correct to classify their API keys as part of their user interface. It's less like deciding the colour of the bike shed, and more like deciding whether the metal sides of the new iPhone should have a brushed or polished finish. Sure, it doesn't affect anything practical, but it may well influence how the user feels about the product. Maybe you deny having feelings about APIs but I think you'd be lying ;)

All that said, I'm not sure the juice was worth the squeeze here - I don't think their new API keys look any more beautiful than a standard UUID. I like that they cared all the same, though.

kimi

Agreed. I'll copy and paste it anyway. It's not that instead of an UUID you have "FluffCat47" that is easy to type and remember, it's a freaking UUID as well (ok, 31 characters vs 36 - saves a huuge 15% of your fingertips...)

dewey

Sometimes spending engineering time for content marketing is worth more than implementing some feature that's not needed for the early adopters.

verdverm

content marketing can demonstrate orgs you don't want to work for and can therein be counterproductive

felideon

Their customers are developers, their product is a dev tool. No different than any other company obsessing over great UX. Stripe won because of the developer experience.

alt227

Tangent, but same with Quickbooks Online. Seems most accountants and bookkeepers I have spoken to hate it, however it has a really useable api and simple callback system which makes it super attractive to developers.

No surprise then that it is the worlds most used accounting software, even though most of its users hate it.

fbuitron

Sometimes someone caring about details is what makes everything better.

Nobody is going to choose their product just because their API Keys.

But they are generating a halo effect. Is like going to a restaurant with outstanding bathrooms. If they put a lot of care in that, you immediately assume they do the same in all the other aspects of their product/service.

alt227

IMO formatting an API key string is not equal to an outstanding bathroom. Its more akin to a valve you put under the floor of the bathroom. You put it there once to make things work and you will never see it again.

Will it make you feel better to know that you have the shinier valve which looks prettier? Maybe, but nobody else will ever know and it will work just the same as the valve everyone else has in their bathroom which cost 10% of the price.

a12k

I totally agree. This looks like a pretty new startup too. I would be livid if my team had spent so much time on this instead of building capabilities to get the startup traction.

alt227

It looks like they wanted a reason to make a shiny blog post to get attention for their startup. Judging by the fact its on the front page of HN and we are all commenting on it, looks like they succeeded.

hombre_fatal

Yall are so dramatic.

It's a trivial library to write. 99% of it is just deciding what you want the output to look like + writing the blog post.

great_wubwub

Yeah, the code is easy but navel-gazing and agonizing over the prettiest way to replace a long jumble of dash-separated lowercase characters with a slightly shorter jumble of dash-separated uppercase characters is an insane thing to spend any time on at all. Convince me this has any commercial value and isn't just bikeshedding.

xdennis

Not trying to be mean, but the fact that they spend that much time on ids is an indication that the team could have misplaced priorities in other regards as well.

shortrounddev2

idk why but it's something product people obsess over. I added a table in the database for our platform that used autoincrementing IDs and my boss (the CEO) sent it back to me asking to use nanoids simply because they look nicer

lovasoa

Dashes in API keys are really annoying. Double-clicking doesn’t select the full key, which just adds extra hassle. It would be much better if they used a continuous string without any separators. Makes copying and pasting way easier, and doesn't affect security at all.

xirdstl

They realized that and were pleased with themselves.

> The dashes do remove easy double-click copying, but we think this a fine trade off for readability. We don't want users copying and pasting them everywhere, in fact we want them to be handled with care. Ideally, users copy each key exactly once - when they generate the key from our dashboard - so we added a copy button to our UI to solve that case

Don't even think about copying/pasting that key, you rube!

alt227

This statement makes me never want to use their product.

inexcf

I agree. They seem to focus on completely useless stuff that actively makes it worse to use.

noman-land

Double click and drag is your friend.

OptionOfT

Side note: this seems to work differently on Mac. I had to wait a little bit before the drag function would drag and not change the selection.

verdverm

you're my hero today

BUMBLING_FOOL

[flagged]

dietr1ch

This is the only annoyance with UUIDs, I won't be typing it because its 5 characters shorter. Separators are nice because I can try to visually compare Ids, but it needs to be a visual separator that won't break a `word`/`WORD` (whatever boundary that is by default on most terminals and browsers) select by double clicking.

I want to just double click and copy, dragging is annoying.

boredumb

I really don't care about the aesthetics of a string but this is 100% my issue when interacting with UUIDs.

majkinetor

or make it optional and/or allow other symbols such as _

theamk

One of the best things you can do to your API key is to give it a fixed prefix. Makes it very easy to tell that you have the right string, to detect accidental secret leakage, etc...

IMHO this makes key much more beautiful than any internal structure.

moebrowne

Agreed. I like what GitHub did with their API tokens, not only adding prefixes but also a checksum.

https://github.blog/engineering/platform-security/behind-git...

adriancooney

Agreed. I first seen it at Stripe (along with prefixing every ID). Whoever at Stripe (or where ever it was invented) needs a good pat on that back. It's adoption has been a huge for DX generally.

jjice

I'm a big fan of prefixed keys as well. If I could go back in time, I would've made my current company's API keys prefixed. Sometimes you'll chat with a customer and just notice the same isn't right and realize the key is all wrong. It's a lot easier if it starts with `company_XXXXX`. Also denoting environment (live vs test in Stripe) with a prefix is actually critical in my eyes.

I also like prefixed resource IDs. Stripe is the first one that comes to mind, but I've run into it multiple times where a customer is describing an issue and it turns out the ID they're trying to lookup is for a different resource (often similar). You don't get those accumulated hours of support time back...

dspillett

I would agree with that.

Also, I don't really want API keys and such to be generally pretty. If things that have no business being end-user facing are ugly then they are less likely to be allowed to accidentally become end-user facing.

If being pretty or otherwise user-friendly is a priority, then I'd go with trying to make them readable/pronounceable rather than shorter, even if that actually makes them longer. There are numerous projects out there¹²³ for doing just that. You could even use the 256-word example³ with multiple small dictionaries, and give people a choice from various possibilities that map to the same number.

----

[1] https://github.com/Debdut/uuid-readable

[2] https://github.com/Martichou/uuid-readable-rs

[3] https://github.com/anton-bot/guid-in-words

yread

I like adding a suffix as well: (optional) human readable expiration date

notpushkin

I’ve made a library for that! https://codeberg.org/prettyid

dewey

Why should anyone vendor a dependency in a critical functionality for three lines of code (https://codeberg.org/prettyid/js/src/branch/master/lib/index...)?

notpushkin

To be honest that’s because I didn’t flesh out JS counterpart yet. My plan was to have something similar to the Python version: https://codeberg.org/prettyid/python

Of course, if you like the idea but don’t want to add another dependency, you can copy-paste the three-liner as it is! (And I’m sure it’s not even copyrightable right now :-)

verdverm

only if I can leftpad it first?

HideousKojima

That's the JavaScript/Node ecosystem in a nutshell. See the LeftPad fiasco and the mere existence of the IsOdd and IsEven packages for more poor judgement.

fusspawn

multiple dependencies,

Also requires two other packages to run its 3 lines of code.

"dependencies": { "@scure/base": "^1.1.7", "uuidv7": "^1.0.1" }

xdennis

There's a similar proposal called TypeID: https://github.com/jetify-com/typeid

E.g.: user_2x4y6z8a0b1c2d3e4f5g6h7j8k

progforlyfe

actual functional features in API keys! How dare you! aesthetics over all!!

anticorporate

I love the throwback reference to the Diablo II CD key. There are some CD keys that will be forever etched in my brain, no matter how many PINs I struggle to remember. I suspect a good number of you know far too much of a certain string that starts with FCKGW.

tommica

mossTechnician

This topic looked interesting, so I hunted down a better frontend for that full Medium article[0]. The writing is unsatisfying, doesn't really say anything, and by the last paragraph ("The tale of the product key... is a continuing demonstration of the never-ending struggle that takes place between the technological sector and the unyielding world of hackers") I was convinced the author was just posting a ChatGPT response. Which is sad, because I'd have liked to see the backstory for something apparently shrouded in mystery. But since there appears to be little explanation to how that key was leaked before Windows itself went on sale, I'll just enjoy the memory of the cultural phenomenon[1][2].

[0] https://freedium.cfd/https://medium.com/@cristian.nedelcu/fc...

[1] https://news.ycombinator.com/item?id=19298196

[2] https://marco.org/2007/06/18/wow-fckgw-has-its-own-wikipedia...

mrweasel

That reference to CD keys is nice and when I look at their example vs. the Diablo II CD key I think they fall a bit short of creating something the looks nice. It's simply down to size, the example key they generate is just to long, to the point where I don't see the value.

It's a cute idea, but I really don't like the extra level of indirection, especially as I feel like there's nothing gained. The base32 encoded key is no more beautiful that the uuid7. I think it's to easy for someone to look at this and go uuid7().upper() and assume that's the same thing, if they just look at the key.

remram

I use URLs as API keys, so they are self-descriptive (links to a page that tells you what it is/what service it's for) and self-revocable (there's a button, no need to post it to a GitHub repo to have them revoke it for you with their secret scanner [1]).

I bring this up a lot [2] but I do think there is value in being able to tell if something is a secret and tell where to go to revoke it if found. Most current API keys use some sort of prefix at least (AWS, SendGrid, GitHub, etc).

[1]: https://docs.github.com/en/code-security/secret-scanning/int...

[2]: https://news.ycombinator.com/item?id=28296864

PaulHoule

I invented (more or less) Crockford Base32 back in 2001 which I called "Base32t" (t for Tapir, because it was part of the "Tapir User Management System") for encoding password reset tokens and such. (Minus those squicky check symbols... Right out of the mind that left us the seductive but slightly flawed JSON [1])

I used T.U.M. for a number of sites including one that was in the Alexa top 2000, even though it was open source it got no pickup from anyone else. The standard at the time was to pick up some software like PHPNuke which did a lot of things badly as opposed to my Yahoo-inspired approach of "pick the best of breed software and plug them into a common user management system".

The idea didn't get any traction until 2013 when things like this popped up like mushrooms. Seemed the missing features were "vendor lock-in", "somebody else owns your user database", "they might shut down, get bought by Google or kick you out of the free tier."

[1] I've seen it enough that I'd expect higher uptake if you inject small flaws into a specification like that.

philo23

A bit off topic, but on the copying issue with dashes, you can wrap the whole key in a span styled with “user-select: all” to improve the copying experience. Well, on the web at least!

fusspawn

I really dont see the benefit of these. its just guids with extra steps. its not any easier to read than guids.

its an alphanumeric random string in both systems.

yes theres is kinda symetrical. but im not going to find it easier to communicate/remember say:

38QARV0-1ET0G6Z-2CJD9VA-2ZZAR0X

any easier than i am

d1756360-5da0-40df-9926-a76abff5601d

both are long random strings. both are awkward to have to read over say a phone call.

what am I missing here?

a12k

This is the very definition of bikeshedding. It should be included as a reference in any definition of that term.

progbits

This is missing a checksum which helps secret scanning avoid false positives in other high entropy strings.

See for example: https://docs.github.com/en/code-security/secret-scanning/sec...

majkinetor

Crockford has a checksum though

progbits

https://github.com/agentstation/uuidkey/blob/master/uuidkey....

They don't appear to check validity though? I haven't tested it so maybe someone else can double check.

majkinetor

yes they do. We use it in prod

mtmail

"encodes UUIDs to a readable Key format via the Base32-Crockford codec and also decodes them back to UUIDs."

Example: "d1756360-5da0-40df-9926-a76abff5601d" => "38QARV0-1ET0G6Z-2CJD9VA-2ZZAR0X"

I think now you risk having 0 vs O or I vs 1 readability issues. [edit: good news, I was wrong]

notpushkin

Base32-Crockford doesn’t have 0 and 1 for this exact reason!

(Seems uuidkey authors have decided to remove O and I instead, but the effect is the same)

EDIT: I’ve looked it up and I was wrong! Crockford alphabet does use all digits (0–9), but doesn’t have O, I or L. When decoding, O is mapped back to 0 and both I and L are mapped to 1. Sorry for the confusion!

mrweasel

Given that API keys are likely to simply be copy-pasted, I don't honestly think this matters all that much.

If you're have the risk to users confusing 0 and O, then you can't use either. Your users aren't going to know that you're running Base32-Crockford and that they'll only encounter 0 and 1, never O, I or L.

We did a "password" generator, for people who made a purchase, but didn't want an account. To view an order they'd then need to enter a code, found in their confirmation email. Those codes where really short, 8 or 10 characters, no 0,1,I,O,L,U,V and all upper case. If the user entered the code in lower case, we'd automatically upper case it. You'd never use these as a real password, but for a temporary order tracking page they pretty much removed all of the input mistakes people could make.

copperlight

https://www.crockford.com/base32.html

> We chose a symbol set of 10 digits and 22 letters. We exclude 4 of the 26 letters: I L O U.

I had never heard of Base32 Crockford before. The whole rationale is clever.

zdc1

I feel like they really missed the mark with "Beautiful" as their priority. If I were to make a UUID to API key encoding/serde library I'd definitely prioritise things like human readable characters / phrases (e.g. BIP-39 style with hyphens), and as jjice said, a fixed prefix.

Edit: okay, good to know were at least covered for 1 vs I issues.

smashed

I don't quite understand the need for a timestamp. This only reduces entropy? You wouldn't think of using the current date in a password prefix for example.

Aren't you going to track the keys in a database, where you can keep the tenant id and creation time, scope of the key and any other significant metadata anyway?

A static prefix + checksum, maybe a version number so you can future-proof the system sounds like best practice. For example `ASKEY1-(128bit random base32 encoded)-(chksum)`.