API keys / Keeping keys secure

If a key leaks

What to do when a key ends up somewhere public, how to rotate without downtime, and why the browser is the wrong place to hold one.

A key is a bearer credential. Anyone holding it can spend your quota, and nothing else — the API is read-only, so a leaked key can't modify your account, your billing, or your data. That limits the blast radius to your bill and your rate limit. It doesn't make a leak fine.

If a key is exposed right now#

  1. Create a replacement first

    You can hold 10 active keys, so make the new one before you kill the old one. Doing it in this order is the difference between a rotation and an outage.

  2. Deploy the new key

    Update your secret store and let the change reach every worker.

  3. Revoke the exposed key

    In the dashboard. Revocation takes effect on the next request and cannot be undone.

  4. Check what it did

    The dashboard shows last_used_at per key and a 30-day daily request breakdown. A leaked key being used shows up as request volume you can't account for.

If the key was committed to git, revoking it is the fix — rewriting history is not. The commit is already cloned, forked, and indexed. Treat the key as public from the moment it was pushed and move on to rotation.

We never ask for your API key, and support has no use for it — everything we can do for your account, we can do from our side. If you've pasted a key into a ticket, a screenshot, or a chat, revoke it.

Rotating on a schedule#

There's no automatic rotation and no key expiry. Keys live until you revoke them. If you rotate periodically, the four steps above are the whole procedure — the ten-key ceiling exists precisely so the old and new keys can overlap while the change propagates.

Wait for last_used_at on the old key to stop moving before revoking it. That's the signal that nothing is still holding it, and it's more reliable than your memory of which services read that secret.

Don't call the API from a browser#

This is the most common way keys leak, and it doesn't announce itself.

A key in browser JavaScript is a published key

Anything shipped to the browser is readable by anyone who opens devtools. It doesn't matter whether it's in the source, in an environment variable at build time, or fetched at runtime — if the browser can send it, the user can read it. And it carries your whole account's quota, not a slice of it.

Call the API from your server and expose your own endpoint to your frontend. That gives you a place to put caching, your own rate limiting, and your own auth — none of which you can do with a key sitting in a bundle.

The API makes this awkward on purpose. There's no OPTIONS handler, so sending an Authorization header from page JavaScript triggers a CORS preflight that fails. The ?api_key= query fallback does work from a browser, which is exactly why it deserves the warning: it working is not the same as it being safe. See authentication for the mechanics.

Where to keep keys instead#

  • A secret manager — your platform's, or a dedicated one. Not a .env file in the repo.
  • Environment variables at runtime, injected by the platform, not baked into an image or a client bundle.
  • Server-side only. No browsers, no mobile apps, no desktop clients. Anything you hand to a user is published.

Scan your repos for hj_live_ before you ship. It's a distinctive prefix, which makes it easy for you to grep for — and easy for everyone else to grep for too.

Reporting a vulnerability#

If you've found a security issue in the API or the dashboard, write to [email protected]. If it's your own key that's exposed, you don't need us — revoke it yourself; it's immediate.

Was this page helpful?