---
name: generate-qr-code
description: Generate a PNG QR code for any URL via Lora's QR API. Anonymous calls are free, rate-limited, and encode the destination behind a `lora-links.com` redirect (so Lora can layer analytics and abuse-blocking on the public flow). Authenticated calls with a Lora API key encode the destination verbatim and can hide the Lora logo on Basic / Business / Enterprise plans.
---

# Generate a QR code with Lora

## What this does

`GET https://api.uselora.com/v1/qr?url=<target>` returns a PNG image of a QR code.

Two modes:

- **Anonymous** (no `Authorization` header): the QR encodes a `https://lora-links.com/?u=<destination>` redirect URL. Scanners hit the redirector, which 302s to the original destination. Free; rate-limited per IP.
- **Authenticated** (`Authorization: Bearer lora_sk_…`): the QR encodes the destination URL verbatim — no proxy hop. Subject to the workspace's per-key rate limit. Required for `hideLogo`.

Either mode returns `Content-Type: image/png` and is CDN-cached. Responses vary by `Authorization`, so repeat calls hit the cache only when the auth mode matches — anonymous and authenticated requests for the same `url` + params are stored as separate cache entries.

## When to use this skill

Trigger when a user wants a QR-code image for a URL. Examples: "generate a QR code for this link", "give me a QR image for the meeting URL", "make a QR for my business card".

If the caller doesn't care about the proxy hop, anonymous mode is the simplest path — no signup, no key. Reach for authenticated mode when they need the destination encoded verbatim (e.g. a static printed QR they don't want changing behavior if the proxy ever gates the URL), or when they want to hide the Lora logo.

## Endpoint

`GET https://api.uselora.com/v1/qr`

Optional header: `Authorization: Bearer lora_sk_…`.

## Minimum request

```bash
curl -o qr.png "https://api.uselora.com/v1/qr?url=https://www.uselora.com"
```

Embed the URL directly in HTML/Markdown:

```html
<img src="https://api.uselora.com/v1/qr?url=https://www.uselora.com&size=400" alt="QR code" />
```

Authenticated variant (encodes destination verbatim, no logo):

```bash
curl -o qr.png \
  -H "Authorization: Bearer $LORA_API_KEY" \
  "https://api.uselora.com/v1/qr?url=https://www.uselora.com&hideLogo=true"
```

## Query parameters

| Param      | Default     | Range / values             | Notes                                                                                                    |
| ---------- | ----------- | -------------------------- | -------------------------------------------------------------------------------------------------------- |
| `url`      | —           | string                     | Required. The URL the QR ultimately points at. URL-encode it. Anonymous calls wrap this in a `lora-links.com/?u=…` redirect before encoding into the QR; authenticated calls encode it verbatim. Schema cap 32 000 chars, but encoder caps usable length far lower (see below). |
| `size`     | `600`       | integer, 64–2048           | Output edge length in pixels. Bigger = sharper print, larger file.                                       |
| `level`    | `L`         | `L` \| `M` \| `Q` \| `H`   | Error-correction hint. When the QR carries the Lora logo overlay (any call without a valid `hideLogo=true` on a paid plan), the server bumps the effective level to `H`, so a lower value here doesn't expand the data budget. |
| `fgColor`  | `#000000`   | hex                        | Foreground (modules). Keep contrast high vs. `bgColor` or scanners fail.                                 |
| `bgColor`  | `#FFFFFF`   | hex                        | Background. Solid colour only.                                                                           |
| `margin`   | `2`         | integer, 0–20              | Quiet-zone modules around the code. Going below `2` makes some scanners struggle.                        |
| `hideLogo` | `false`     | boolean                    | Omits the "Powered by Lora" logo overlay. Requires `Authorization: Bearer lora_sk_…` on a Basic, Business, or Enterprise plan. Anonymous calls and keys whose plan lacks `removeWatermark` get `403 plan_requires_upgrade`. |

## Practical capacity

The Lora logo overlay covers ~20% of the QR modules, so error-correction level `H` is required to recover scannable data when the logo is present. Byte-mode capacity at level `H` is ~1 273 bytes — that's the effective `url` cap for any call that doesn't successfully set `hideLogo=true`. With the logo off, capacity climbs back toward the level-`L` ceiling (~2 953 bytes).

URLs longer than the active cap return `400 qr_data_too_long`.

## Common picks

- **Print/poster**: `size=1200&margin=4`.
- **Small inline thumbnail**: `size=200`.
- **Brand-coloured**: `fgColor=%23ff5722` (URL-encode `#` as `%23`).
- **Logo-free, branded**: pass `Authorization` + `hideLogo=true` (paid plan).

## Error handling

Errors return JSON `{ error, code?, requestId }` with `X-Lora-Request-Id` for support.

| Status | `code`                  | What to do                                                                                                |
| ------ | ----------------------- | --------------------------------------------------------------------------------------------------------- |
| 400    | `validation_error`      | A param failed schema validation (bad size, malformed hex, etc.). Fix and retry.                          |
| 400    | `qr_data_too_long`      | The `url` exceeds the encoder's byte-mode capacity for the active level (~1 273 bytes with logo, ~2 953 without). Shorten the URL or authenticate and pass `hideLogo=true`. |
| 401    | —                       | Sent an `Authorization` header but the key is missing, malformed, expired, or disabled. Re-check the key. |
| 403    | `plan_requires_upgrade` | Requested `hideLogo=true` from an anonymous call or a key on a plan without `removeWatermark`. Upgrade to Basic / Business / Enterprise, or drop `hideLogo`. |
| 429    | `rate_limit_exceeded`   | Anonymous: per-IP budget exhausted (60/min). Authenticated: per-key cap exhausted (600/min on Basic, 1 500/min on Business). Honour `Retry-After`. |

The 429 response is `Cache-Control: no-store`, so a single blocked CDN POP can't lock out everyone behind it.

## Related

- Full API reference: <https://uselora.dev>.
