Coupon Code Generator

Batches of coupon codes from the browser's own randomness, on an alphabet with no character that can be misread, with the guessability stated in bits.

Enable JavaScript to customise; default output below.

Readable label on the front. It is not a secret and it adds nothing to how hard the code is to guess, so it is left out of the entropy figure.

This is the part that resists guessing. Ten characters on the unambiguous alphabet is 47 bits.

Characters to use

Unambiguous drops B, I, L, O, S, U and 0, 1, 5, so a code read off a card or a podcast survives the trip.

Inserts a hyphen every few characters, which halves the typing errors. Zero for one unbroken block.

Live preview coupon-codes.txt
# 10 codes, 10 random characters from 26
# 47 bits each: strong: guessing is not a practical way in
SPRING-GENERATED-IN-YOUR-BROWSER

Output is valid and updates as you type.

Two things make a coupon code good, and neither of them is being memorable.

It has to resist guessing. SPRING20 is a code anybody can find on the third try, and there are large forums whose whole purpose is guessing codes. A code needs enough randomness that trying is pointless, and this tool states how much it has in bits rather than telling you it is secure.

It has to survive being read out. A code that goes on a printed card, a podcast or a phone call meets O against 0 and I against 1, so the alphabet here keeps one of each confusable pair and drops the other. That costs a fraction of a bit per character and saves every support ticket that starts “it says invalid code”.

Codes are generated in your browser, fresh on every load. Nothing is sent anywhere.

How to use

  1. Set a prefix if you want the codes labelled. It is readable, not secret.
  2. Choose how many random characters and how many codes.
  3. Leave the alphabet on unambiguous unless the codes will only ever be copied and pasted.
  4. Copy or download them, then load them into your store before you print anything.

Example

Ten characters, six codes, grouped every five, with a SPRING prefix:

# 6 codes, 10 random characters from 26
# 47 bits each: strong: guessing is not a practical way in
SPRING-639NX-NC3AA
SPRING-RHNHT-4FWV6
SPRING-KJ6DW-CGV8G
SPRING-VF7TJ-Y67CK
SPRING-KPJHT-3A3TP
SPRING-D6398-EXEH8

Yours will be different: they are drawn from crypto.getRandomValues when the page loads. Forty-seven bits is about 140 trillion possible codes, so with six live ones a guess has roughly a one in twenty trillion chance of landing.

Pitfalls

A prefix adds nothing to security. It is printed next to the code on the poster. That is why the entropy figure here counts only the random part; a tool that counts the prefix makes a weak code look strong.

Single-use is a store setting, not a property of the code. In WooCommerce the usage limit per coupon and the limit per user are both off by default. A generated code with no limit is a permanent discount that ends up on a deals site.

Always set an expiry. A code with no end date will be found in a report two years from now, still working.

Load the codes before you print them. Codes that exist on a card and not in the database are a support queue rather than a campaign.

Short codes collide. Four or five characters is fine for a dozen codes and guaranteed to produce duplicates across thousands. The tool refuses duplicates inside one batch, but it cannot know about the codes from last month, so keep the length up and check on import.

Don’t email one code to a whole list and call it unique. If a code is in ten thousand inboxes it is public. Unique codes mean one code per recipient, which is what generating a batch this size is for.

Compatibility

Randomness comes from crypto.getRandomValues, available in every browser since roughly 2013, and the page recomputes on load so what you see is yours. During the build the tool has no randomness at all, on purpose: baking a set of codes into the page would hand every visitor the same codes.

Selection uses rejection sampling. A byte that lands in the short final block of 256 is thrown away rather than taken modulo the alphabet size, because the modulo makes the first few characters of the alphabet slightly more likely, and on a 26-character set that bias is real.

The unambiguous alphabet is ACDEFGHJKMNPQRTVWXY2346789: 26 characters, 4.7 bits each. B is dropped and 8 kept, S and 5 both dropped, I and L and 1 dropped, O and 0 dropped, and U dropped so that codes do not spell words.

Import is a paste into WooCommerce, Shopify, Easy Digital Downloads or whatever else you use. The plain text output has one code per line so it drops straight into a bulk-coupon importer or a spreadsheet column.

Frequently asked questions

How long should a code be?
Ten characters on the unambiguous alphabet, for most cases. That is 47 bits, which is beyond guessing and still short enough to read out. Go to twelve if the codes are worth a lot of money, and to eight only if they are short-lived and rate limited.
Are these codes unique?
Within one batch, yes: the tool refuses duplicates. Across batches, at ten characters the chance of a repeat is negligible, but your store is the thing that actually enforces uniqueness, so let the import tell you.
Can I make the same codes again?
No, and that is deliberate. There is no seed field because a seeded coupon generator means somebody who knows the seed can produce every code you will ever issue. Download the batch and keep it.
Why not just use words people can remember?
Because a memorable code is a guessable code, and because memorable codes are the ones that spread. If you want a memorable public code, that is a different thing from a unique code per customer; use a short branded one for the campaign and generated ones for anything worth protecting.
Does the grouping hyphen matter?
Only for reading. Most stores treat the hyphen as part of the code, so whatever you generate is what the customer types. Keep it if the code will be read by a human, drop it if it will only be clicked.
Weekly drops

New tools, when there are new tools

One email when something worth using ships. No schedule to fill, so no filler.

Your address goes nowhere else, and one click unsubscribes.