A tiny REST API for creating short links and QR codes. If you can send a JSON request, you can use it. No SDK required.
/api/shorten. You get back a short code (e.g. AB3XK9PQ7M) and a passcode.https://www.quickresponsehub.com/AB3XK9PQ7M gets redirected to your URL.Paste this whole block into your terminal. It creates a short link, repoints it to a new URL, then verifies the redirect — using jq to capture the code and passcode automatically.
# 1. Set your API key (get it from /dashboard)
export QRH_KEY="YOUR_API_KEY"
# 2. Create a short link to https://example.com/v1
# -> capture "code" and "passcode" from the JSON response
RESPONSE=$(curl -s -X POST https://www.quickresponsehub.com/api/shorten \
-H "Content-Type: application/json" \
-H "X-API-Key: $QRH_KEY" \
-d '{"url":"https://example.com/v1"}')
CODE=$(echo "$RESPONSE" | jq -r '.code')
PASSCODE=$(echo "$RESPONSE" | jq -r '.passcode')
echo "Created: https://www.quickresponsehub.com/$CODE"
echo "Passcode (save this!): $PASSCODE"
# 3. Update the destination to https://example.com/v2
curl -s -X POST https://www.quickresponsehub.com/api/update \
-H "Content-Type: application/json" \
-H "X-API-Key: $QRH_KEY" \
-d "{\"code\":\"$CODE\",\"passcode\":\"$PASSCODE\",\"url\":\"https://example.com/v2\"}" \
| jq
# 4. Test the redirect — should print "Location: https://example.com/v2"
curl -sI https://www.quickresponsehub.com/$CODE | grep -i '^location:'
# 5. (Optional) Look up the current destination without following the redirect
curl -s https://www.quickresponsehub.com/api/lookup/$CODE | jqCreated: https://www.quickresponsehub.com/AB3XK9PQ7M
Passcode (save this!): K8M2QW9XLR4P7VN3JZB6CDFH
{
"code": "AB3XK9PQ7M",
"url": "https://example.com/v2",
"short_url": "https://www.quickresponsehub.com/AB3XK9PQ7M",
"updated": true
}
location: https://example.com/v2
{
"code": "AB3XK9PQ7M",
"url": "https://example.com/v2",
"created_at": "2026-04-28T10:00:00.000Z",
"hit_count": 1
}No jq? Install it with brew install jq (macOS) or apt install jq (Debian/Ubuntu) — or skip it and read the JSON by eye.
A short, public identifier (6–20 characters, uppercase letters and numbers only, e.g. AB3XK9PQ7M). It's what goes in the short URL.
A 24-character secret returned once when you create or reserve a code. Required to change the destination URL. Store it like a password.
Identifies your account. Sent on every create or update request via the X-API-Key header.
Send your API key in the X-API-Key header. Read endpoints (lookup, QR images, redirects) are public and need no key.
curl https://www.quickresponsehub.com/api/shorten \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com"}'Missing or wrong key? You'll get a 401 Unauthorized.
A passcode is a per-code 24-character secret. It's separate from your API key and is the only credential that lets you change a code's destination URL or delete it. Treat it like a one-time-issued password.
Exactly once, in the JSON response of the request that created the code. After that, no endpoint, dashboard view, or support agent can ever show it to you again — it's hashed at rest with Argon2id and we genuinely cannot recover it.
| Endpoint | Returns passcode? | Field |
|---|---|---|
| POST /api/shorten | ✅ Yes — once | passcode |
| POST /api/reserve | ✅ Yes — once | passcode |
| POST /api/bulk-shorten | ✅ Yes — once per item | results[].passcode |
| GET /api/lookup/{code} | ❌ Never | — |
| GET /api/list | ❌ Never | — |
| Dashboard UI | ❌ Never (after create) | — |
You can still delete the code from your dashboard (account ownership is enough), but you cannot change its destination URL. The recovery path is: delete the old code → create a new one → reprint or re-share the new short URL or QR.
The passcode is a long-lived bearer secret. Capture it from the create response immediately and put it somewhere your application can read but humans normally can't.
qrh/passcode/AB3XK9PQ7M.crypto_secretbox) before INSERT. Never store plaintext..env file outside source control with chmod 600, or your OS keychain (security add-generic-password on macOS, secret-tool on Linux).localStorage.# Create the code, pull the passcode straight into a secrets store.
# Nothing is ever printed to the terminal or written to disk in plaintext.
RESPONSE=$(curl -s -X POST https://www.quickresponsehub.com/api/shorten \
-H "Content-Type: application/json" \
-H "X-API-Key: $QRH_KEY" \
-d '{"url":"https://example.com/launch"}')
CODE=$(echo "$RESPONSE" | jq -r '.code')
PASSCODE=$(echo "$RESPONSE" | jq -r '.passcode')
# AWS Secrets Manager
aws secretsmanager create-secret \
--name "qrh/passcode/$CODE" \
--secret-string "$PASSCODE"
# Or: macOS Keychain
security add-generic-password -a "qrh" -s "passcode-$CODE" -w "$PASSCODE"
unset RESPONSE PASSCODE// Node.js — encrypt with libsodium before storing in Postgres.
import sodium from "libsodium-wrappers";
import { sql } from "./db";
await sodium.ready;
const key = sodium.from_base64(process.env.PASSCODE_ENCRYPTION_KEY!);
const res = await fetch("https://www.quickresponsehub.com/api/shorten", {
method: "POST",
headers: { "Content-Type": "application/json", "X-API-Key": process.env.QRH_KEY! },
body: JSON.stringify({ url: "https://example.com/launch" }),
});
const { code, passcode } = await res.json();
const nonce = sodium.randombytes_buf(sodium.crypto_secretbox_NONCEBYTES);
const ciphertext = sodium.crypto_secretbox_easy(passcode, nonce, key);
await sql`
insert into qr_codes (code, passcode_nonce, passcode_ciphertext)
values (${code}, ${Buffer.from(nonce)}, ${Buffer.from(ciphertext)})
`;
// 'passcode' goes out of scope here — never logged, never serialized.[A-Z0-9]{24} next to your X-API-Key scrubber.https://www.quickresponsehub.comContent-Type: application/json)The same POST /api/shorten call in six common languages — copy, set QRH_KEY in your environment, and run.
// Node 18+ / Bun / Deno — uses built-in fetch.
const res = await fetch("https://www.quickresponsehub.com/api/shorten", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": process.env.QRH_KEY!,
},
body: JSON.stringify({ url: "https://example.com/spring-sale" }),
});
if (!res.ok) {
const { error } = await res.json().catch(() => ({}));
throw new Error(`QRH ${res.status}: ${error ?? res.statusText}`);
}
const { code, short_url, qr_url, passcode } = await res.json();
console.log({ code, short_url, qr_url });
// IMPORTANT: persist 'passcode' in your secrets store now — it's only returned here.No SDK required. Every example uses each language's standard HTTP primitives so you can drop them into any codebase without adding dependencies (except Python's requests, which most projects already have).
/api/shorten🔑 API key requiredThe everyday endpoint. Send a URL, get back a short code, a short URL, a QR code, and a passcode.
urlstring (URL)requiredThe destination — where visitors should land. Must include http:// or https://.
curl -X POST https://www.quickresponsehub.com/api/shorten \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"url":"https://example.com/spring-sale"}'{
"code": "AB3XK9PQ7M",
"url": "https://example.com/spring-sale",
"passcode": "K8M2QW9XLR4P7VN3JZB6CDFH",
"short_url": "https://www.quickresponsehub.com/AB3XK9PQ7M",
"qr_url": "https://www.quickresponsehub.com/qr/AB3XK9PQ7M",
"created_at": "2026-04-28T10:00:00.000Z",
"reused": false
}400The URL is missing or not a valid http/https URL.401Your API key is missing or wrong.429Rate limit exceeded — back off and retry per the Retry-After header.500Something went wrong on our side. Try again shortly.POST /api/bulk-shorten instead — one request consumes one slot./api/reserve🔑 API key requiredWant a branded code like MYBRAND2026? Claim it now, decide where it points later. You get a passcode back; use /api/update when you're ready to set the URL.
codestring (6–20 chars)requiredThe custom code you want to claim. Letters and numbers only — no spaces, no dashes, no symbols. Stored uppercase.
curl -X POST https://www.quickresponsehub.com/api/reserve \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"code":"MYBRAND2026"}'{
"code": "MYBRAND2026",
"passcode": "K8M2QW9XLR4P7VN3JZB6CDFH",
"url": null,
"short_url": "https://www.quickresponsehub.com/MYBRAND2026",
"reserved": true,
"created_at": "2026-04-28T10:00:00.000Z"
}/api/update first.400Code is the wrong length, contains a space, or has a non-alphanumeric character.401Your API key is missing or wrong.409That code is already in use — pick a different one.429Rate limit exceeded — back off and retry per the Retry-After header./api/update🔑 API key requiredRepoint an existing code somewhere new — for example, swap a printed QR's destination after a campaign ends. Requires the passcode you got at create/reserve time.
codestringrequiredThe existing code (e.g. MYBRAND2026).
passcodestringrequiredThe 24-character passcode returned when the code was created or reserved.
urlstring (URL)requiredThe new destination URL. Must include http:// or https://.
curl -X POST https://www.quickresponsehub.com/api/update \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{
"code": "MYBRAND2026",
"passcode": "K8M2QW9XLR4P7VN3JZB6CDFH",
"url": "https://example.com/new-destination"
}'{
"code": "MYBRAND2026",
"url": "https://example.com/new-destination",
"short_url": "https://www.quickresponsehub.com/MYBRAND2026",
"updated": true
}400URL is missing or not valid, or fields are missing.401Your API key is missing or wrong.403Code doesn't exist, the passcode is wrong, or this code belongs to a different account.429Rate limit exceeded — back off and retry per the Retry-After header./api/delete🔑 API key requiredPermanently remove a code. The short URL stops working immediately and the code becomes available for someone else to claim. Requires the matching passcode.
codestringrequiredThe code to delete.
passcodestringrequiredThe 24-character passcode for that code.
curl -X POST https://www.quickresponsehub.com/api/delete \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"code":"MYBRAND2026","passcode":"K8M2QW9XLR4P7VN3JZB6CDFH"}'{ "code": "MYBRAND2026", "deleted": true }401API key is missing or wrong.403Code doesn't exist, passcode is wrong, or you're not the owner.429Rate limit exceeded — back off and retry per the Retry-After header./api/bulk-shorten🔑 API key requiredSend an array of URLs in one request. You get back an array of results in the same order — perfect for migrations, batch imports, or generating QR codes for a whole product catalogue.
urlsarray of strings (1–100)requiredEach item must be a valid http:// or https:// URL.
curl -X POST https://www.quickresponsehub.com/api/bulk-shorten \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"urls":["https://example.com/a","https://example.com/b"]}'{
"count": 2,
"results": [
{
"ok": true,
"reused": false,
"code": "AB3XK9PQ7M",
"url": "https://example.com/a",
"passcode": "K8M2QW9XLR4P7VN3JZB6CDFH",
"short_url": "https://www.quickresponsehub.com/AB3XK9PQ7M",
"qr_url": "https://www.quickresponsehub.com/qr/AB3XK9PQ7M",
"created_at": "2026-04-28T10:00:00.000Z"
},
{
"ok": true,
"reused": true,
"code": "ZK4MNQR9P2",
"url": "https://example.com/b",
"passcode": "L9N3RW0YMS5Q8WO4KAC7DEGI",
"short_url": "https://www.quickresponsehub.com/ZK4MNQR9P2",
"qr_url": "https://www.quickresponsehub.com/qr/ZK4MNQR9P2",
"created_at": "2026-04-20T14:00:00.000Z"
}
]
}reused: true./api/lookup/{code}PublicUseful for checking a code's destination from your own backend without following the redirect. No API key needed.
curl https://www.quickresponsehub.com/api/lookup/AB3XK9PQ7M{
"code": "AB3XK9PQ7M",
"url": "https://example.com",
"created_at": "2026-04-28T10:00:00.000Z",
"hit_count": 42
}url is null for codes that have been reserved but not yet pointed anywhere.
/api/list🔑 API key requiredProgrammatically fetch the codes on your account, newest first. Great for building your own dashboards or syncing with another system.
limitinteger (1–200)How many items to return. Default 50.
offsetintegerSkip this many items. Use with limit for paging.
searchstringPartial, case-insensitive match on code or URL.
curl "https://www.quickresponsehub.com/api/list?limit=20&search=brand" \
-H "X-API-Key: YOUR_API_KEY"{
"total": 73,
"limit": 20,
"offset": 0,
"count": 20,
"items": [
{
"code": "AB3XK9PQ7M",
"url": "https://example.com",
"hit_count": 42,
"created_at": "2026-04-28T10:00:00.000Z",
"short_url": "https://www.quickresponsehub.com/AB3XK9PQ7M",
"qr_url": "https://www.quickresponsehub.com/qr/AB3XK9PQ7M"
}
]
}/api/qr/{code}PublicReturns a PNG (default) or SVG QR image that encodes the short URL. Customize size and colors with query parameters. No API key needed.
formatpng | svgImage format. Default is png.
sizeinteger (64–2048)Pixel size for PNG. Default is 512. Ignored for SVG.
darkhex colorForeground (the dots). Default is #1a2e4f. URL-encode the # as %23.
lighthex colorBackground. Default is #ffffff.
# Plain PNG (default 512px)
curl -o qr.png "https://www.quickresponsehub.com/api/qr/AB3XK9PQ7M"
# 1024px PNG with custom colors
curl -o qr.png "https://www.quickresponsehub.com/api/qr/AB3XK9PQ7M?size=1024&dark=%23000000&light=%23ffffff"
# Vector SVG (scales infinitely)
curl -o qr.svg "https://www.quickresponsehub.com/api/qr/AB3XK9PQ7M?format=svg"HTTP/1.1 200 OK
Content-Type: image/png
Cache-Control: public, max-age=86400, immutable
Content-Length: 4821
<binary PNG data — write to file with -o or pipe to a viewer><img src="https://www.quickresponsehub.com/api/qr/AB3XK9PQ7M?size=256" alt="QR code" width="256" height="256" />/qr/{code}PublicRedirects to a CDN-cached PNG of the QR. Use this in <img> tags and emails — it's faster than /api/qr/{code} for repeated views.
curl -L -o qr.png "https://www.quickresponsehub.com/qr/AB3XK9PQ7M"HTTP/1.1 302 Found
Location: https://cdn.quickresponsehub.com/qr/AB3XK9PQ7M.png
Cache-Control: public, max-age=31536000, immutable<img src="https://www.quickresponsehub.com/qr/AB3XK9PQ7M" alt="QR code" width="256" height="256" />/api/qr-image/{code}PublicReturns the canonical short URL, the cached QR URL, and the underlying storage URL. Handy when you only have a code and want to grab the image links.
curl https://www.quickresponsehub.com/api/qr-image/AB3XK9PQ7M{
"code": "AB3XK9PQ7M",
"url": "https://example.com",
"short_url": "https://www.quickresponsehub.com/AB3XK9PQ7M",
"qr_url": "https://www.quickresponsehub.com/qr/AB3XK9PQ7M",
"qr_storage_url": "https://cdn.quickresponsehub.com/qr/AB3XK9PQ7M.png"
}/{code}PublicThe redirect itself. This is the URL encoded in your QR image and the one you share publicly.
curl -I https://www.quickresponsehub.com/AB3XK9PQ7MHTTP/1.1 302 Found
Location: https://example.com
Cache-Control: no-store
X-QRH-Code: AB3XK9PQ7MReturns 404 if the code doesn't exist or has no destination URL set yet.
/api/statusPublicQuick check that the API and database are online. Use it for uptime monitoring.
curl https://www.quickresponsehub.com/api/status{
"status": "operational",
"database": "ok",
"total_redirects": 12873,
"response_time_ms": 24,
"timestamp": "2026-04-28T10:00:00.000Z",
"version": "1.5.0"
}All errors return JSON with an error message and the matching HTTP status code:
{ "error": "Code already in use" }| Status | What it means |
|---|---|
| 400 | Your request body is invalid (bad URL, missing field, wrong code format). |
| 401 | API key is missing or wrong. |
| 403 | You don't own this code, or the passcode doesn't match. |
| 404 | Code doesn't exist (or has no destination URL set yet). |
| 409 | Code is already taken (only on /api/reserve). |
| 500 | Something failed on our side — safe to retry. |
Need every status with cause, fix, and example payloads? See the full Error Reference →
Limits are enforced per API key on a sliding 60-second window. Read endpoints (lookup, list, QR images, redirects) and write endpoints have separate budgets so a heavy read workload won't block your ability to create new codes.
| Plan | Writes / min | Reads / min | Bulk batch size |
|---|---|---|---|
| Free | 60 | 300 | 100 |
| Pro | 600 | 3,000 | 1,000 |
| Enterprise | Custom | Custom | Custom |
X-RateLimit-Limit — your budget for this endpoint group.X-RateLimit-Remaining — requests left in the current window.X-RateLimit-Reset — Unix seconds when the window resets.Retry-After — only on 429 and 503; seconds to wait before retrying.429, 500, 502, 503, 504, plus network errors (DNS, TCP reset, TLS).400, 401, 403, 404, 409, 422 — the request will fail again identically. Fix the input first.Retry-After when present — it always overrides your backoff schedule.// Drop-in fetch wrapper with rate-limit-aware retries.
const RETRYABLE = new Set([408, 425, 429, 500, 502, 503, 504]);
export async function qrhFetch(input: string, init: RequestInit = {}, attempt = 0): Promise<Response> {
const res = await fetch(input, init);
if (res.ok || !RETRYABLE.has(res.status) || attempt >= 5) return res;
const retryAfter = Number(res.headers.get("Retry-After"));
const backoff = Number.isFinite(retryAfter) && retryAfter > 0
? retryAfter * 1000
: Math.min(30_000, 2 ** attempt * 1000) * (0.75 + Math.random() * 0.5); // jitter
await new Promise((r) => setTimeout(r, backoff));
return qrhFetch(input, init, attempt + 1);
}POST /api/bulk-shorten instead of looping POST /api/shorten — one request counts as one, not N.GET /api/lookup/{code} responses for at least 60s; destinations rarely change minute-to-minute.GET /api/status at most every 30 seconds.GET /{code}) through the API — link to them directly.Passcodes are 24 uppercase letters/digits, returned only once when a code is created or reserved. They unlock the right to change the destination URL via /api/update. They're never visible in any read endpoint or in the dashboard. Lose it and you'll need to create a new code — see the dedicated passcodes section above for safe-storage guidance.