All guides
Vouchers and tokens

How to make single-use QR codes that redeem only once

A QR code cannot limit itself to one use; whatever scans it must check a record. Free Google Sheets checks, their limits, and refusing a second scan.

By the QR to Sheets team 10 min readUpdated 8 October 2026

Short answer

A QR code is printed text and cannot stop itself being used twice, so a single-use voucher works only if whatever scans it checks a record of the codes already redeemed.

The free route is a numbered code per voucher, logged through a Google Form or a Google Sheet with a COUNTIF flag, which spots a second redemption afterwards but cannot stop it at the counter.

QR to Sheets can refuse it at the scanner: with Accept duplicate scans switched off, a link records each code once, and a repeat shows Already scanned with the time it was first scanned.

Drink tokens at a fundraiser, meal tickets for a staff lunch, prize draw entries, food stall tokens at a school fair, one free coffee per conference badge: the request is always the same. Each code should work once, and a second attempt should fail. The part most guides skip is where that rule lives. It cannot live in the QR code.

Written by QR to Sheets, which sells the scanner link described in the second half, so weigh that accordingly. The free routes come first and work with any QR code. Google and vendor facts were read on their own pages on 2026-10-08.

Why can't a QR code limit itself to one use?

A QR code is a picture of some text, such as V-0042-7F3A. It has no memory and no way of knowing it was scanned before. A photocopy or a screenshot of it is exactly as valid as the original. So single use is never a property of the code: it is a property of whatever reads it, which has to look the code up in a record of redemptions and refuse it if it is already there. Every working single-use system, free or paid, is that record plus a check.

This is also why the scan limits offered by dynamic QR code generators solve a different problem. A dynamic code points to a web address the generator controls, and the limit counts visits to that address. QR TIGER's help page puts it plainly: "Once a QR code reaches its scan limit, it will no longer redirect to its embedded link." It counts scans of the code, not redemptions, so a guest checking the code at home uses one up, and the person at the counter learns nothing about whether this voucher was already used. Uniqode's own article on one-time-use QR codes describes redemption the same way: the system "marks that credential as consumed, so any further attempts are automatically rejected." The check happens where the code is redeemed.

Make one numbered code per voucher

Whatever records the redemptions, every voucher needs its own value. A hundred copies of the same code cannot be told apart, so one redemption would use up all of them. Build the list in a Google Sheet, on a tab called Issued:

Paste column B into the free bulk QR code generator, up to 500 codes at a time, and print them on label sheets or straight onto the tickets. It needs no account, and the codes contain only the text you pasted. Keep the Issued tab: it is the list every redemption is checked against.

Free route: a Google Form, checked with COUNTIF afterwards

The cheapest record is a Google Form with one short-answer question, Voucher code, opened on the phones at the counter. Staff type or paste the code from the voucher, submit, and hand over the drink or the meal. Each submission becomes a row in the linked response sheet, with Google's timestamp in column A and the code in column B. A shared Google Sheet the staff type into directly works the same way, with the same columns. Google Forms adds each response as a new row, so a formula filled down beside the responses does not reach new rows. Put each flag as one formula in the header cell of an empty column instead, and it covers every row:

COUNTIF ignores upper and lower case, so v-0042-7f3a and V-0042-7F3A count as the same code, which is what you want here. Google's help notes that a COUNTIF criterion can contain the wildcards ? and *, so keep those characters out of voucher codes.

Where the free route breaks

For a fundraiser with a dozen tokens and one table, that may be entirely fine, and it costs nothing. It stops being fine when vouchers are worth something, when there is more than one counter, or when the queue is long.

How QR to Sheets refuses a second redemption

In QR to Sheets, the admin signs in with Google once, connects a spreadsheet and creates a scanner link that writes to one tab. Counter staff open the link in Safari or Chrome on their phones, allow the camera once and scan vouchers, with no app and no account. Each link has a setting called Accept duplicate scans. For vouchers, switch it off.

The trade-off is deliberate: a refused scan leaves no row. The Sheet shows each voucher's first redemption and nothing about later attempts, so it cannot tell you who tried a voucher twice. If you need every attempt on record, leave the setting on and flag repeats in the sheet instead, as handling duplicate QR scans describes.

If the signal drops, the open scanner keeps saving scans on the phone and sends them when the signal is back, each with its original scan time. Without a signal, though, a phone can refuse only a code it has already scanned itself on that link. Any other code shows as recorded on the device, so the counter cannot tell it will be refused later. When the signal returns, the first redemption to reach QR to Sheets is kept and the later one is refused, after the voucher has already been honoured. For vouchers, keep the counter phones online.

The once-ever rule is counted per scanner link. The same code scanned on two different links is recorded on both. That is what lets one printed card serve several redemptions, one per link:

The free plan has one scanner link. Several links, one per service or voucher type, need Premium, which has unlimited links. For more than one counter on the same link, several phones scanning into one Google Sheet covers how rows are told apart.

When not to switch it off

Accept duplicate scans is on by default, and it should stay on for anything a person scans more than once on purpose. Switched off for daily attendance, it would refuse every person's scan tomorrow; switched off on a check-in and check-out link, it would refuse every return. With it on, every scan is written, and a code scanned again on the same link within 12 hours shows an amber Already scanned badge with the count and the words Saved anyway, so the person scanning knows it is a repeat and the record still holds every scan.

Google Form + COUNTIF flag (free) compared with QR to Sheets
CapabilityGoogle Form + COUNTIF flag (free)QR to Sheets
CostFreeFree for one phone and 300 scans in total, then $7 per phone per month
Second redemption at the counterAccepted, then flagged REPEAT in the sheetRefused on the phone with Already scanned and the first scan time
Two counters, same voucher, same momentBoth acceptedOne row kept, the other refused
How the code is enteredTyped into the formScanned with the phone camera
Record of refused attemptsEvery attempt is a rowNone: a refused scan leaves no row
Codes nobody issuedCOUNTIF against the Issued tabCOUNTIF against the Issued tab
Who can see the spreadsheetWhoever you share it with; form users see only the formCounter staff scan through the link and never get access to the spreadsheet

What it costs, and when a guest list fits better

Each phone that scans counts as one device. Free covers one phone; Premium is $7 per phone per month. People being scanned never open the link and need nothing. The free plan also has one link and 300 scans in total, not per month, so it suits a trial or a small one-off; 400 drink tokens at two counters is two phones on Premium, $14 for the month. QR to Sheets writes each scan time as text in the form YYYY-MM-DD HH:MM:SS, so formulas that do arithmetic on it wrap it in VALUE().

Vouchers are anonymous: the code says what it is worth, not who holds it. When each code belongs to a named person who registered, the job is check-in against a list instead. Giving every form submission its own QR code covers issuing a personal code to each registration, and the event check-in apps compared page sets the guest-list tools side by side, including QR to Sheets' Events add-on, which costs $25/month and requires Premium.

Step by step

  1. 1

    Give every voucher its own code

    Number the vouchers in a sheet, for example V-0001 to V-0400, add a short random part so the next number cannot be guessed, and keep that list as your record of what was issued.

  2. 2

    Print one QR code per voucher

    Paste the list into a free bulk QR code generator and print the codes on label sheets or on the tickets themselves. Each code contains only its voucher number.

  3. 3

    Decide where redemptions are recorded

    Either a Google Form the counter staff fill in, checked afterwards with a formula, or a scanner link set to record each code once, which refuses the second redemption on the spot.

  4. 4

    Use one scanner link per voucher type

    A link records each code once, ever. Give each kind of voucher or each meal service its own link, so a lunch ticket redeemed today does not block tomorrow's.

  5. 5

    Check the log against the issued list

    A formula on a separate tab marks any redeemed code that was never issued, and counts how many vouchers have been used so far.

Frequently asked questions

Can a QR code be made to work only once?+

Not by itself. A QR code is printed text with no memory, and a copy of it is as valid as the original. It works once only when whatever scans it checks a record of codes already redeemed and refuses a repeat.

Does a scan limit on a dynamic QR code stop a voucher being used twice?+

No. A dynamic code's scan limit counts scans of the web address behind it, so a guest checking the code at home uses one up. It tells the person at the counter nothing about whether that voucher was already redeemed.

How do I find vouchers redeemed twice in Google Sheets?+

With voucher codes in column B, put ={"Repeat"; ARRAYFORMULA(IF(B2:B="", "", IF(COUNTIFS(B2:B, B2:B, ROW(B2:B), "<="&ROW(B2:B))>1, "REPEAT", "")))} in the header cell of an empty column, so it also covers responses that arrive later. It marks every redemption after the first. It finds repeats after they happen; it cannot stop one at the counter.

Can QR to Sheets refuse a voucher that was already scanned?+

Yes. Switch off Accept duplicate scans on the scanner link. That link then records each code once, ever, and a repeat shows Already scanned with the time it was first scanned, with nothing added to the Sheet, even when two phones scan the same code at the same moment.

Is a refused scan recorded anywhere?+

No. With Accept duplicate scans off, a refused scan leaves no row, so the Sheet holds each code's first redemption only. To keep every attempt, leave the setting on: repeats are then written and flagged with an amber Already scanned badge.

Can the same card be redeemed once a day?+

Yes, with one scanner link per day or per meal service. The once-only rule is counted per link, so a card scanned on the Lunch 12 Oct link can still be scanned once on the Lunch 13 Oct link. Several links need Premium; the free plan has one.

Try it on your own Google Sheet

Connect Google, create a scanner link, and share the URL with your team. The free tier covers 300 scans, and nobody you share it with has to install anything.

Start for free

No credit card required

Keep reading