Skip to content

QR code image API

One HTTP GET, no key, no sign-up: put a URL after makeqrco.de/qr/ and you get the QR code back — in your size, your colours, as PNG or SVG.

Request

GET https://makeqrco.de/qr/https://example.com?size=1024&ec=h&format=svg

Response

200 OK · Content-Type: image/svg+xml · cached 24 hours

How it works

Everything after /qr/ is the payload, including its own query string. The server URL-decodes it, encodes it as a QR code and streams back an image. With no options that is a 512-pixel black-on-white PNG at error correction M with a 2-module quiet zone. There is no shortener and no redirect: the code contains your URL literally, so it keeps working even if this site does not.

Options are query parameters: ?size=1024, ?dark=7c3aed&light=transparent, ?format=svg. Nothing is stored and nothing is trusted — every value is checked against a fixed range, so a bad one returns 400 with a message naming the parameter rather than being silently ignored.

Responses carry Cache-Control: public, max-age=86400 and an open CORS header, so a repeated link is served from cache and the image can be fetched from your own JavaScript. Because the cache key is the entire URL, two different option sets are two different cached images.

Parameters

All optional, all validated. Out-of-range values are rejected rather than quietly clamped.

Parameter Type Default Values
data string any payload
Explicit payload. When present it replaces the path entirely and every other query parameter is ignored, which sidesteps the collision between your URL’s query string and these options. Use it for anything that is not a plain URL: WiFi strings, phone numbers, vCards.
size integer 512 64–1024 (png) · 64–2048 (svg)
Output width and height in pixels, for both formats: on an SVG it becomes the root element's width and height attributes, so it sets the intrinsic size the vector is laid out at (it still scales without blurring, but it is not ignored). PNG stops at 1024 because rasterising a bigger bitmap is real single-threaded CPU on a keyless endpoint; ask for more and the 400 points you at format=svg, which costs the same to render at every size and goes to 2048. Anything outside the range is a 400, not a clamp, so a typo fails loudly.
margin integer 2 0–16
Quiet zone in modules — the blank border around the symbol. The specification asks for 4; the default of 2 trades a little standards purity for a tighter image and has been this endpoint's behaviour since it launched. Ask for a value between 1 and 3 explicitly and the image still renders but carries an X-QR-Warning header. Use 4 for anything printed, and 0 when you are placing the code on your own padded background.
ec enum m l | m | q | h
Error correction level: about 7%, 15%, 25% and 30% of the symbol may be damaged and still read. Higher levels mean a denser code. Case-insensitive.
dark colour black #rgb, #rrggbb, #rrggbbaa, black, white, transparent, purple
Colour of the modules. The leading # is optional (and awkward in a URL, so `dark=7c3aed` is the usual form). Keep it dark: scanners need contrast against the light colour.
light colour white #rgb, #rrggbb, #rrggbbaa, black, white, transparent, purple
Background colour. `light=transparent` produces a genuinely transparent PNG (and an SVG with no background rectangle), which is what you want when the code sits on a coloured design.
format enum png png | svg
Response format. SVG is a few hundred bytes, scales to any size without blurring, costs the same to render whatever size you ask for and is what print and large-format work wants — it is also the only way past the 1024px PNG cap; PNG is what an <img> in an email needs.
download boolean false 1 | true | yes | 0 | false | no
Adds Content-Disposition: attachment, so a browser saves the file as qrcode.png or qrcode.svg instead of displaying it. A bare `?download` counts as true.

Your URL's query string versus these options

The payload lives in the path, so a URL with its own ? and & works without escaping: /qr/https://shop.example/p?id=42&ref=poster encodes exactly that address. Only the eight names in the table above are taken as options, matched exactly and case-sensitively; every other parameter stays part of what gets encoded.

That leaves one honest collision: a target URL that itself carries size, margin, ec, dark, light, format, download or data. In /qr/https://shop.example/p?size=XL the request cannot tell your parameter from ours, and size=XL is read as an option — which fails with a 400. Two ways out, both exact:

Percent-encode the target

/qr/https%3A%2F%2Fshop.example%2Fp%3Fsize%3DXL

Or pass it as ?data=

/qr/?data=https%3A%2F%2Fshop.example%2Fp%3Fsize%3DXL

?data= is the reliable form for anything that is not a plain URL, and the one to reach for from a script: when it is present it is the whole payload, the path is ignored, and no other parameter can leak into the code. Percent-encode the payload if it contains a literal #, which a browser would otherwise strip as a fragment before the request is sent.

Live examples

Every code below is a real request to this endpoint, made by your browser as this page loaded. Scan any of them and you land on this site.

Default black-on-white QR code for makeqrco.de
/qr/https://makeqrco.de/?size=256
Purple QR code for makeqrco.de on a white background
?dark=7c3aed
Purple QR code for makeqrco.de on a transparent background
?dark=a855f7&light=transparent
QR code for makeqrco.de rendered as SVG at error correction H
?format=svg&ec=h&margin=4

Examples

Plain code
https://makeqrco.de/qr/https://example.com
Large code for print — SVG, because a vector goes past the 1024px PNG cap and costs nothing extra to render
https://makeqrco.de/qr/https://example.com?format=svg&size=2048&margin=4
Largest PNG (1024px is the raster cap; above it, add format=svg)
https://makeqrco.de/qr/https://example.com?size=1024&margin=4
SVG — scales to any size, a few hundred bytes
https://makeqrco.de/qr/https://example.com?format=svg
Transparent background, brand colour
https://makeqrco.de/qr/https://example.com?dark=7c3aed&light=transparent
Maximum error correction (survives a logo or a scuff)
https://makeqrco.de/qr/https://example.com?ec=h
Any payload with ?data= — WiFi credentials
https://makeqrco.de/qr/?data=WIFI%3AT%3AWPA%3BS%3ACafe%3BP%3Ahunter2%3B%3B
Any payload with ?data= — a phone number
https://makeqrco.de/qr/?data=tel%3A%2B49301234567
HTML embed
<img src="https://makeqrco.de/qr/https://example.com?size=256&dark=7c3aed"
     alt="QR code for example.com" width="256" height="256">
Markdown
![QR code for example.com](https://makeqrco.de/qr/https://example.com)
Shell — download straight to a file
curl -o qr.svg "https://makeqrco.de/qr/https://example.com?format=svg&download=1"
JavaScript
const res = await fetch(`https://makeqrco.de/qr/?data=${encodeURIComponent(payload)}&ec=h`);
const blob = await res.blob();

Endpoint reference

Method GET (and OPTIONS for CORS preflight)
Path /qr/<url> · /qr/?data=<payload>
Response image/png (default) or image/svg+xml
Defaults 512×512 PNG, error correction M, 2-module quiet zone, black on white
Size limits PNG 64–1024 px · SVG 64–2048 px
Options data, size, margin, ec, dark, light, format, download
Auth None — no key, no quota
CORS Access-Control-Allow-Origin: *
Cache public, max-age=86400 (per full URL, options included)
Indexing X-Robots-Tag: noindex, nofollow

Status codes

200 The image. Cached for 24 hours by shared caches and browsers; the cache key is the whole URL, so a different option set is a different image.
400 A parameter is out of range or not understood, the payload is missing, or the payload is too long to fit in a QR symbol. The body is plain text and names the parameter and its allowed values.
X-QR-Warning Not a status: a header on an otherwise fine 200. Sent when the output is legal but risky — a quiet zone under 4 modules, or a dark and light colour with no contrast between them.

A 400 body reads like Invalid `size`: 999999 is out of range. Allowed: 64–2048. — plain text, never a broken image. OPTIONS on the same path answers the CORS preflight, so a fetch that sends a custom header works too.

/qr/<url> versus /<url>

There are two routes and they return different content types. Picking the wrong one is the single most common mistake, so:

makeqrco.de/qr/<url>

Returns image/png or image/svg+xml. Use this in <img> tags, Markdown, HTML email, PDFs and scripts.

makeqrco.de/<url>

Returns text/html: a small page showing the code, with Open Graph tags so it unfurls with a preview. Use this when you are sending a link to a person.

Both are generated per request and neither stores what you encoded. Both send X-Robots-Tag: noindex, nofollow, and the HTML page loads no analytics, because the URL you encoded is part of its path.

Limits and fair use

The endpoint is free and unauthenticated, which only works if people are reasonable about it. It is sized for embedding codes in documents, dashboards and emails — not for bulk-rendering hundreds of thousands of images. If you need that volume, the same library that powers this endpoint, node-qrcode, runs happily on your own machine, and this site's terms explain what counts as abuse.

A PNG stops at 1024 pixels because rendering is not free and nobody has to log in to ask for it: a bitmap is drawn pixel by pixel on a single-threaded server, so the cost climbs with the area and a few large concurrent requests would slow the whole site down. An SVG has no such cost — the size only lands in a width and height attribute — so format=svg runs to 2048 pixels and scales past that without blurring, which is what print work wants anyway. Ask for a larger PNG and you get a 400 that says so and names format=svg. The one thing the API deliberately does not do is the centred logo overlay: that needs your image file, so it lives in the QR code generator, where compositing happens in your browser. Pair it with ec=h so the code survives the covered modules.

Need a logo in the middle, or just want to click instead of type?

Open the QR code generator