SMS QR Code Generator

One scan opens the messaging app with your number in the "to" field — and, on a phone that honours the pre-filled body, your keyword already typed. SMS reaches every phone with a SIM card, no apps required on either side. The code is built in your browser from the standard sms: URI, and every example on this page is computed by the same engine.

Opens a text-message draft to a number.

SMS details

Pre-filled message support varies by platform: some phones open the composer with the text, others ignore it. The number always works.

Style & export options

A logo hides part of the code, so it is capped at 16% of the code area (a decode-tested limit) and error correction is forced to level H.

Fill in the form — the code appears as you type.

Codes render on light backgrounds for reliable scanning.

What the code contains
 

Keep this code

Optional — the downloads above already work without saving anything.

Device saves stay in this browser; account saves need a free account. Both appear in My codes.

Save this code to an account

A free account keeps your codes in one library you can open from any device. Your code is ready — sign up or sign in, and it will be saved automatically.

Sign up free Sign in

Before this leaves your device

The sms: format — and an honest caveat

The code carries an sms: URI: the scheme, your number in international E.164 form, and — only when you type a message — a ?body= parameter holding the pre-filled text. That is the syntax standardized in RFC 5724, and it is the form the engine's builder was checked against when its spec sources (the ZXing Barcode Contents reference and the RFC for each URI scheme) were verified on 2026-07-20. The number part is universally supported. The pre-filled body is not quite: Pre-filled message support varies by platform: some phones open the composer with the text, others ignore it. The number always works.

Design for that reality: the code should still make sense if the visitor has to type the keyword themselves. Print the keyword next to the code ("Text DEMO to +1 555 013 2000") and the code becomes a convenience, not a dependency.

Companion guide: URI schemes: mailto, tel, sms and WhatsApp sets the four address-like schemes this site encodes side by side, with the same engine building every example. This page stays with sms: alone; the other guides cover the symbol itself and the remaining payload families.

Worked example

A "text DEMO for a callback" campaign code encodes as:

sms:+15550132000?body=DEMO

On a phone that honors ?body=, the composer opens with "DEMO" typed and the number set; on one that does not, the composer still opens to the right number.

What the payload contains, piece by piece

Read the worked example left to right and you have the whole convention. The engine emits three parts in a fixed order, and nothing else:

  1. The scheme, sms:. Lower-case, followed immediately by the number — no slashes, no space. The scheme is what tells whatever decodes the code that the text after it is a messaging address rather than a web link or a number to dial. (A phone call code carries the same number behind a different scheme, tel:.)
  2. The number, in E.164 form. A plus sign followed by seven to fifteen digits whose first digit is not zero: the country code, then the subscriber number, with no punctuation. The generator accepts the way people actually write numbers — spaces, dots, dashes, parentheses, and the 00 international prefix as an alias for + — strips the formatting, and emits + followed by digits. It strips punctuation only: a national trunk zero typed after the country code is not removed, so drop that zero yourself. What it will not accept is a number without a country code: the builder refuses rather than guesses one, because it cannot know where the phone that scans the code will be.
  3. The body, only if you typed one. A non-empty message is appended as ?body= followed by the text, percent-encoded as the next section shows. An empty message field adds nothing — the payload ends at the last digit — and no other parameter is ever emitted.

The number rule in practice: three spellings of one fictional number, each handed to the builder at build time, and one payload out. (The build checks that they agree; if the normalizer ever changed, this page would fail to build rather than show a stale table.)

Typed into the number field Payload the builder emits
001 (555) 013-2000 sms:+15550132000
+1-555-013-2000 sms:+15550132000
+1.555.013.2000 sms:+15550132000

That is also the shortest form an SMS payload takes — scheme plus number, nothing pre-filled:

sms:+15550132000

Rendered by the engine, this is version 2, 25×25 modules, error correction Q. The example was requested at level M, the generator's default, and the engine reports the level it actually encoded: never lower than requested, but sometimes higher, because the reference encoder raises the level whenever doing so does not increase the version. A short payload can get more recovery than it asked for at no cost in size.

Percent-encoding, shown on a fictional service request

The message is where SMS payloads go wrong, because a URI cannot carry a space, a hash or an ampersand as-is. The builder runs the body through strict RFC 3986 percent-encoding: every character outside the unreserved set — letters, digits, -, ., _ and ~ — is replaced by a percent sign and its byte value in hexadecimal. Take a code for a maintenance sticker that pre-fills an asset reference and a fault description, so the text arrives self-identifying:

Message typed: Asset #A-7: lift & door (urgent)!

sms:+15550132000?body=Asset%20%23A-7%3A%20lift%20%26%20door%20%28urgent%29%21

Line the two up character by character and the rule is visible:

Character Typed In the payload
space %20
hash # %23
colon : %3A
ampersand & %26
parentheses () %28%29
exclamation mark ! %21
hyphen - -

The hyphen in A-7 survives because it is unreserved; everything else that is not a letter or a digit is escaped. The escapes are the URI's spelling of your text, the way a space in a web address is written as %20 — they are not characters added to your message, and they do not mean the generator mangled it. The encoded body runs to 55 characters against the 33 you typed, which is why punctuation-heavy messages grow the symbol faster than plain words do.

The word strict matters. A browser's built-in encoder leaves the sub-delimiters !, ', (, ) and * bare, but the RFC 5724 grammar does not allow them bare inside the body value. The engine therefore encodes those too: the fragment (urgent)! becomes %28urgent%29%21 here, where the browser's encoder alone would have left it as (urgent)!. The engine's tests check that output against independently written reference strings, so the behaviour cannot drift without a test failing.

Symbol facts for the service-request code: version 5, 37×37 modules, error correction M.

Reading the symbol facts: version, modules, error correction

A QR code has two independent layers. The payload is the text above; the symbol is the module matrix produced to ISO/IEC 18004 by the vendored reference encoder. Three facts describe the symbol, and this page prints all three for every example below.

  • Version is the symbol's size class. The encoder chooses it from the payload length and the error-correction level; a longer payload needs a higher version.
  • Modules are the squares per side, not counting the quiet zone. Between the keyword example and the sentence example below, each version step adds 4 modules per side.
  • Error correction (L, M, Q or H) is how much damage the symbol can absorb and still decode. More recovery costs modules at the same payload, so a higher level often needs a higher version — the sentence body below steps from version 5 at L to version 10 at H — but when the extra recovery fits in the version already chosen, the encoder grants it for free, as the number-only example above shows.

Here is the same fictional number with bodies of increasing length — none, the keyword JOIN, the service request above, and the sentence JOIN the opening-day list. Reply STOP at any time to leave it. — all requested at the default level M:

Payload Characters Version Modules EC level encoded
Number only 16 2 25×25 Q
Keyword body (JOIN) 26 2 25×25 M
Punctuated body 77 5 37×37 M
Sentence body 106 6 41×41 M

And one payload — the sentence body — requested at each of the four levels:

Requested Encoded Version Modules
L L 5 37×37
M M 6 41×41
Q Q 8 49×49
H H 10 57×57

The practical reading: a keyword-only code stays small, a sentence-long body does not, and asking for more recovery is not free. If you add a logo in the style panel the engine forces level H regardless of the level you chose, and caps the logo at 16% of the symbol area; the error correction and logos guide explains why that cap is decode-tested rather than derived, and how a QR code encodes data covers the version and module facts in more depth.

Print size: what the readout means for an SMS code

The PNG download comes in 3 preset sizes, and the style panel shows what each one measures when printed at 300 DPI. The readouts, computed here by the same function that drives the panel:

PNG preset Printed size
512 px 1.71 in × 1.71 in (43.3 mm × 43.3 mm) at 300 DPI
1024 px 3.41 in × 3.41 in (86.7 mm × 86.7 mm) at 300 DPI
2048 px 6.83 in × 6.83 in (173.4 mm × 173.4 mm) at 300 DPI

Those are the dimensions of the whole image, and the whole image includes the quiet zone: the 4-module white border on every side that the standard requires and that the renderer draws into every download. It cannot be switched off, and it should not be cropped. The export spreads the preset's pixel width across the symbol plus that border, snapping each module edge to a whole pixel, so on average the worked example at the top of the page — 25 modules plus 4 on each side, 33 in all — comes out with these module sizes:

PNG preset Pixels per module (average) Millimetres per module at 300 DPI (average)
512 px 15.5 1.31 mm
1024 px 31.0 2.63 mm
2048 px 62.1 5.25 mm

That arithmetic is the honest extent of what this page can say about print size. A longer body adds modules, and at the same preset each module gets smaller; the SVG download carries the same modules as a vector drawing for any size the presets do not cover. Error correction helps a damaged code scan, but print size and contrast still matter, and the only test that counts is a real device scanning the actual print. The quiet zone and print size guide works the readouts through for every preset.

What the generator cannot verify

The engine validates format, not truth. It checks that the number carries a country code and is all digits once the punctuation is stripped, and it encodes the body so the URI is well-formed. That is the whole of its knowledge. It cannot know:

  • whether the number is reachable, or whether anything at that number can receive a text at all. A mistyped digit encodes perfectly: the symbol is just as valid, and the scan just as clean, as for the right number.
  • whether your keyword means anything. If the service behind the number expects JOIN and you encoded JOINS, the code is correct and the campaign is not.
  • whether the body will be honoured on the phone that scans it. Pre-filled message support varies by platform: some phones open the composer with the text, others ignore it. The number always works.
  • who will scan it, or where. That is why the number must be in international form — the one form that does not depend on the scanner's country.

Two more consequences follow from every code on this site being static. The number and the message live in the image itself, with no redirect through our servers, so the code never expires and cannot be held hostage — but it also cannot be edited after printing, and scans cannot be counted. If the number changes, the printed code is simply wrong, and the only remedy is a new print. How the builders and the renderer are tested — reference strings, hostile inputs, and a round-trip suite that renders each payload type, rasterizes it and decodes it with an independent decoder — is set out on the methodology page.

Common misreadings

Most "the code doesn't work" reports are payload problems, which the sections above cover. The rest are symbol problems, and the same few recur:

  • Cropping the quiet zone. The white border is part of the code. The renderer draws 4 modules of it on every side of every download; placing the code flush against a frame, a colour block or another code takes it away again in the layout.
  • Inverting the colours. Light modules on a dark background are rejected by many scanners. The style panel warns separately about inverted pairs, over and above the contrast check.
  • Thinning the contrast. The panel computes the contrast of your colour pair with the WCAG relative-luminance formula and warns below 4.5:1. The ratio is computed from the colours you chose, not guessed, and it is the number to watch when a brand palette goes on the code.
  • Reading the escapes as damage. %20 and its relatives in the payload are correct — see the encoding section. Do not "fix" them by pasting the payload into a plain-text generator and editing it; the result would be a malformed URI.
  • Trusting the body. The number always works; the pre-filled text may not. A code whose printed instructions only make sense if the message appears is a code that fails quietly — print the keyword.
  • Testing on the screen and not on the print. Treat a freshly generated code as untested until a real device has scanned it — the print, not the preview.

Where SMS codes fit

  • Keyword opt-ins: "Text JOIN for opening-day updates" — with the legally required consent and opt-out language printed alongside, not hidden in the code.
  • Service requests without an app: a code on the machine that pre-fills its asset number, so the maintenance text arrives self-identifying — the service-request example above is exactly this.
  • Reaching non-smartphone workflows: SMS lands on every handset with a SIM — useful where email goes unread and messaging apps are not guaranteed.

SMS QR code FAQ

Does scanning send the message automatically?

No — on every platform, the visitor sees the draft and presses send themselves. A QR code cannot send an SMS on its own, which also means it cannot silently sign anyone up to anything.

Who pays for the message?

The sender, at their normal SMS rate. If your audience may be charged, standard practice (and in many jurisdictions, a requirement) is to say so next to the code.

SMS or WhatsApp code — which should I print?

Know your audience. SMS works on every phone with a SIM and needs no app; WhatsApp needs the app but is free over data, better for media, and dominant in many countries. When in doubt, print both — see the WhatsApp generator.

Will the message be pre-filled when someone scans?

Pre-filled message support varies by platform: some phones open the composer with the text, others ignore it. The number always works. The generator encodes the body exactly as RFC 5724 specifies; whether a given phone acts on it is outside the code’s control, which is why the keyword belongs in print next to the code as well as inside it.

Why does the generator reject my local-format number?

The payload carries the number in international E.164 form — a plus sign, the country code, then the subscriber number — because the builder refuses to guess a country code. Type the number any way you like with the country code included (spaces, dots, dashes, parentheses and the 00 prefix are all accepted) and the generator emits the normalized form, as the spellings table above shows. It strips punctuation only: a national trunk zero typed after the country code is not removed, so drop that zero yourself.

The payload shows %20 and similar codes in my message — is it broken?

No. Those are percent-escapes, the URI spelling of characters a URI cannot carry bare: %20 is a space and %21 an exclamation mark. The engine applies strict RFC 3986 encoding to the body so the sms: URI is well-formed; the encoding section above shows a full message character by character.

Can I change the number or the message after printing?

No. Every code from this site is static: the number and the body are encoded in the image itself, with no redirect through our servers. That is what lets the code work without us and never expire, and it is also why nothing in it can be edited once printed — a new number means a new code. Scans cannot be counted for the same reason.

Does a longer message make the code bigger?

Yes. A longer body means a longer payload, and the encoder chooses the version that holds it: the keyword example is version 2 (25×25 modules) while the sentence example is version 6 (41×41). At the same PNG preset more modules means each module is smaller — the print-size section shows the arithmetic. Print size and contrast still matter, so treat a fresh code as untested until a real device has scanned it.

Related: the phone call code for voice, and the email code when a longer written message fits better.