Adding a logo to API-generated QR codes without breaking scannability
By Qrop Team ·
Putting a brand mark in the centre of a QR code is one extra field on a request: logoUrl or logoDataUrl. The API takes care of the parts that are easy to get wrong by hand, namely how big the logo is allowed to be and how much error correction the code needs to survive being partly covered. That makes it tempting to assume a logo can't break anything.
It can. The code itself will always be generated correctly, but the artwork you supply decides whether a phone camera can still read it, and nothing in the API can check that for you. This guide explains what the API does with your logo, where the limits are, the cases where a logo is silently dropped, and how to test a batch before it goes to print.
What the API does with your logo
Send either logoUrl (a link the server fetches) or logoDataUrl (the image inline as a base64 data URL) on POST /api/v1/generate, or once on a batch request. The logo is resized to fit inside a square whose sides are 25% of the output width, then composited into the centre of the finished PNG. That is a hard ceiling set by Qrop, not a value you can raise per request, so a logo can never cover more than about 6% of the code's area.
The logo is fitted, not stretched. A wide or tall image is scaled down to fit the square and the leftover space is filled with white, and a transparent logo gets that same white backing. In practice every logo sits on a white tile in the middle of the code. If your code uses a coloured or dark background, expect a white square in the centre, and design the logo to look right on white.
Because the centre is covered, the API forces the error correction level to H whenever a logo is present, whatever you passed in errorCorrection. H is the level that can recover from the most damage, which is what pays for the hidden modules. Our guide on error correction levels explains the trade in detail, but the short version is that you don't need to manage this yourself.
Which image to send
The logo must be PNG, JPEG, or WebP and no larger than 2MB. SVG logos are rejected on purpose: an SVG is XML and can carry scripts, so it never reaches the compositing step. If your brand mark only exists as a vector file, export a PNG of it first.
Send the logo at a useful size. It is scaled to 25% of your output resolution, so at 2048px the logo box is 512px wide. A 128px favicon scaled up to that box will look soft, and a soft edge on the white tile is exactly where a decoder gets confused. As a rule, supply a logo at least as wide as resolution multiplied by 0.25.
Simple marks survive; detailed ones don't. A solid icon or monogram with strong contrast against white stays readable and leaves a clean border of white around it. A logo with fine text, thin lines, or a busy photograph pushes detail into the zone right next to the code's modules, and some cameras will treat that detail as part of the pattern.
- Use a square or near-square mark so the fitted tile has little empty padding.
- Keep a margin of white inside the image itself, rather than running artwork to the edge of the tile.
- Avoid logos that are mostly black or very dark at the edges, since they blur into the dark modules around them.
logoUrl or logoDataUrl
If both fields are sent, logoDataUrl wins. The practical difference is the network: logoUrl makes the server fetch your image before generating, with a 5 second timeout and the same 2MB limit, while logoDataUrl is decoded in memory with no fetch at all. On a warm request the fetch adds roughly 11 to 22 milliseconds, and it depends on how quickly your host answers.
For a pipeline that generates thousands of codes, logoDataUrl gives you steadier timing and removes a dependency on your own image host being up. For occasional use, or when your logo already lives on a stable public URL, logoUrl is simpler. Either way, a bad image returns a 422 with a message saying what was wrong, so a typo in a URL or an oversized file shows up on the first test call rather than in the middle of a run.
Where a logo is dropped or refused
A logo only renders in PNG and PDF output. On a single POST /api/v1/generate call with format set to svg, the logo is skipped and you get a valid, scannable SVG with an empty centre and no error. That is easy to miss in a pipeline, so check your format before debugging your artwork. Our SVG, PNG, and PDF guide covers why the logo step doesn't apply to vector output.
Batch is stricter. POST /api/v1/generate/batch returns a 422 and consumes no credit if you combine a logo with svg, rather than quietly dropping it across up to 25 items.
A batch also takes one logo for the whole request. It is fetched or decoded once and applied to every item, so there is no way to give each item its own logo. If different customers or products need different marks, make a separate request per logo.
What the colour check does and doesn't cover
Qrop rejects foreground and background colour pairs that fall below a 3:1 contrast ratio, before any credit is used. That check applies to the code's own colours only. It cannot look inside your logo, so a logo can pass every API check and still make a code harder to read.
Keep the code itself high contrast when a logo is present. Dark modules on a light background with the default or near-default colours leave the most room for the logo to cost you a little reliability. A low-contrast colour scheme combined with a logo stacks two sources of difficulty on the same camera.
Test before you run the batch
- Generate one real code with your actual logo at the actual resolution and format your pipeline will use.
- Print it at the smallest size it will ever appear, on the real material if you can, and scan it with at least two different phones, including an older one.
- Scan it again at an angle, in poor light, and from the distance a customer would realistically hold their phone.
- If any scan hesitates, simplify the logo before touching anything else. Shortening the encoded URL also helps, because fewer characters means larger modules at the same print size.
- Only then submit the full run, and keep the tested logo file under version control so every later batch uses the same artwork.
Frequently asked questions
How big can the logo be on a Qrop API QR code?
The logo is fitted into a square whose sides are 25% of the output width, and that limit is fixed. The source image can be up to 2MB, but it is always scaled to that box. Supplying an image at least as wide as the box avoids a blurry result.
Why did my SVG come back without the logo and without an error?
On a single generate call, logo compositing is skipped for format svg and the request still succeeds. Use png or pdf if the logo needs to appear. On the batch endpoint the same combination returns a 422 instead.
Can I use an SVG file as my logo?
No. The logo must be PNG, JPEG, or WebP. SVG files are rejected because they can contain scripts. Export your vector mark to PNG at the size you need and send that.
Do I need to set errorCorrection to H when I send a logo?
No. Any request with a logo is raised to level H automatically, and you can't force a lower level alongside one, so setting it yourself changes nothing.
Can each item in a batch have its own logo?
No. A batch request applies one logo to all of its items. If items need different logos, send one batch (or one single call) per logo.
Related guides
Ready to create your QR code?
Generate a free QR code in seconds: custom colours, logos, and PNG, SVG or PDF output.
Open the generator