API Reference
QR Chameleon API: Programmatic Short Links, QR Codes & Analytics
A clean REST API for developers. Create short links and QR codes in bulk, pull click and scan analytics, and build integrations — all with a single Bearer token and generous free-tier rate limits.
Getting started
Authenticate every request with an API key. Generate one in your dashboard under Account → API Access. Keys are shown only once at generation time — store yours in a password manager or environment variable.
Base URL
https://qrchameleon.com/api/v1
Authentication
Pass your key in the Authorization header on every request:
Authorization: Bearer qrc_your_api_key_here
Missing, malformed, or revoked keys return 401 Unauthorized.
Rate limits
Two limits apply to every key: a monthly quota and a per-minute burst cap. Current caps by plan:
| Plan | Monthly requests | Burst (per minute) |
|---|---|---|
| Free | 1,250/mo | 60/min |
| Blend | 6,500/mo | 120/min |
| Adapt | 30,000/mo | 300/min |
| Transform | 60,000/mo | 600/min |
Every response includes these headers so you can self-throttle:
X-RateLimit-Tier: blend
X-RateLimit-Limit-Minute: 120
X-RateLimit-Remaining-Minute: 118
X-RateLimit-Limit-Month: 7500
X-RateLimit-Remaining-Month: 7491
When either cap is exceeded, requests return 429 Too Many Requests with a Retry-After header (seconds until reset).
Response format
All responses are JSON. Success responses use this envelope:
{
"success": true,
"data": { ... }
}
List endpoints include a meta object for pagination:
{
"success": true,
"data": [ ... ],
"meta": { "page": 1, "limit": 20, "total": 150, "total_pages": 8 }
}
Errors
Error responses use status: "fail" for client errors (4xx) and status: "error" for server errors (5xx):
{
"status": "fail",
"message": "URL not found"
}
| Code | Meaning |
|---|---|
400 | Malformed request body or missing required field |
401 | Missing, invalid, or expired API key |
403 | API key lacks required permission, or your plan doesn't include this endpoint (see plans) |
404 | Resource not found or not owned by this account |
429 | Rate limit exceeded (see Retry-After header) |
500 | Server error — retry with exponential backoff |
Pagination
List endpoints accept page and limit query parameters. limit defaults to 20 and caps at 100.
Data retention
Analytics endpoints clamp the requested timeframe to your plan's data-retention window. If you ask for 90d on Free (7-day retention), the response returns 7 days and reports the clamp:
{
"timeframe_requested": "90d",
"timeframe_applied": "7d",
"retention_limit_days": 7,
"was_clamped": true,
...
}
| Plan | Retention |
|---|---|
| Free | 0 days |
| Blend | 0 days |
| Adapt | 0 days |
| Transform | 0 days |
The timeframe query parameter accepts 7d, 30d, 90d, or all on every analytics endpoint. all resolves to your retention window.
Need deeper history? Upgrade your plan to extend retention up to 1 year.
Short links
GET/v1/urls
List the authenticated account's short links.
Query params: page (default 1), limit (default 20, max 100)
curl -H "Authorization: Bearer qrc_..." \
"https://qrchameleon.com/api/v1/urls?limit=10"
{
"success": true,
"data": [
{
"id": "Lp8cgZgbkgH9faH1",
"short_url": "https://qrch.am/my-link",
"short_code": "my-link",
"custom_back_half": "my-link",
"destination_url": "https://example.com/landing",
"title": "Spring Sale",
"is_active": true,
"created_at": "2026-04-01T12:00:00.000Z",
"expires_at": null,
"last_clicked_at": "2026-04-22T17:33:12.000Z",
"click_count": 1420,
"linked_qr_code_id": null,
"utm": {
"source": "email", "medium": "newsletter",
"campaign": "spring-sale", "term": null, "content": null
}
}
],
"meta": { "page": 1, "limit": 10, "total": 42, "total_pages": 5 }
}
GET/v1/urls/:id
Fetch a single short link by ID. Returns 404 if the link doesn't exist or belongs to another account.
curl -H "Authorization: Bearer qrc_..." \
https://qrchameleon.com/api/v1/urls/Lp8cgZgbkgH9faH1
GET/v1/urls/:id/analytics
Click analytics with country, device, daily, and referrer breakdowns.
Query params: timeframe — one of 7d, 30d (default), 90d, all
{
"success": true,
"data": {
"url_id": "Lp8cgZgbkgH9faH1",
"timeframe": "30d",
"total_clicks": 1420,
"unique_visitors": 1103,
"by_country": [{"country": "United States", "count": 840}, ...],
"by_device": [{"device": "mobile", "count": 920}, ...],
"by_day": [{"date": "2026-04-01", "count": 34}, ...],
"by_referrer":[{"source": "Google", "category": "search_engine", "count": 210}, ...]
}
}
POST/v1/urls/bulk
Create up to 100 short links in one request. Each item is validated independently and results report per-item status (created, failed, or skipped if plan limit reached).
curl -X POST -H "Authorization: Bearer qrc_..." \
-H "Content-Type: application/json" \
-d '{
"urls": [
{"destination_url":"https://example.com/a","title":"Variant A","utm_source":"email"},
{"destination_url":"https://example.com/b","custom_back_half":"my-link"}
]
}' \
https://qrchameleon.com/api/v1/urls/bulk
{
"success": true,
"data": {
"total": 2,
"created": 2,
"failed": 0,
"skipped": 0,
"items": [
{"index": 0, "status": "created", "id": "Lp...", "short_code": "ab12cd", "short_url": "https://qrch.am/ab12cd", "destination_url": "https://example.com/a"},
{"index": 1, "status": "created", "id": "Lp...", "short_code": "my-link", "short_url": "https://qrch.am/my-link", "destination_url": "https://example.com/b"}
]
}
}
Request body fields:
| Field | Type | Notes |
|---|---|---|
destination_url | string (required) | Must be a valid http(s) URL |
title | string | Human-readable label |
custom_back_half | string | Desired slug; must be unique across the system |
expires_at | ISO 8601 | Link stops working after this timestamp |
utm_source, utm_medium, utm_campaign, utm_term, utm_content | string | Campaign tracking parameters |
QR codes
GET/v1/qr
List the authenticated account's QR codes.
curl -H "Authorization: Bearer qrc_..." \
"https://qrchameleon.com/api/v1/qr?limit=10"
{
"success": true,
"data": [
{
"id": "Qp8cgZgbkgH9faH1",
"name": "Spring Sale Flyer",
"qr_type": "url",
"destination_url": "https://example.com/landing",
"is_active": true,
"created_at": "2026-04-01T12:00:00.000Z",
"scan_count": 318,
"download_count": 2,
"linked_shortlink_id": "Lp...",
"has_logo": true,
"qr_config": { "dotsOptions": {"color": "#123351", "type": "dot"}, ... }
}
],
"meta": { "page": 1, "limit": 10, "total": 18, "total_pages": 2 }
}
GET/v1/qr/:id
Fetch a single QR code by ID.
GET/v1/qr/:id/analytics
Scan analytics with country, device, and daily breakdowns. Accepts timeframe same as URL analytics.
{
"success": true,
"data": {
"qr_id": "Qp8cgZgbkgH9faH1",
"timeframe": "30d",
"total_scans": 318,
"unique_scanners": 287,
"by_country": [{"country": "United States", "count": 198}, ...],
"by_device": [{"device": "mobile", "count": 310}, ...],
"by_day": [{"date": "2026-04-01", "count": 9}, ...]
}
}
POST/v1/qr/bulk
Create up to 100 QR codes in one request. Same partial-success semantics as /v1/urls/bulk.
curl -X POST -H "Authorization: Bearer qrc_..." \
-H "Content-Type: application/json" \
-d '{
"qr_codes": [
{"destination_url":"https://example.com/1","name":"Flyer 1"},
{"destination_url":"https://example.com/2","name":"Flyer 2","qr_config":{"dotsOptions":{"color":"#3eeda2","type":"dot","size":10}}}
]
}' \
https://qrchameleon.com/api/v1/qr/bulk
Account analytics
Aggregated across everything you own — all tiers, retention-clamped.
GET/v1/analytics/overview
Total clicks and scans across the account for the timeframe, plus inventory counts.
{
"success": true,
"data": {
"timeframe_applied": "30d",
"retention_limit_days": 30,
"was_clamped": false,
"totals": {
"total_clicks": 1420,
"total_scans": 318,
"total_engagements": 1738,
"unique_click_visitors": 1103,
"unique_scan_visitors": 287
},
"inventory": {
"total_links": 59, "active_links": 59,
"total_qr_codes": 19, "active_qr_codes": 19
}
}
}
GET/v1/analytics/top-urls
Top short links by clicks in the timeframe. Accepts limit (default 10, max 50).
GET/v1/analytics/top-qr
Top QR codes by scans in the timeframe. Accepts limit (default 10, max 50).
GET/v1/analytics/campaigns
UTM-aggregated campaign performance. Groups by utm_campaign × utm_source × utm_medium and sums clicks per group.
{
"success": true,
"data": [
{
"utm_campaign": "spring-sale",
"utm_source": "email",
"utm_medium": "newsletter",
"link_count": 4,
"total_clicks": 320,
"unique_visitors": 245
}
]
}
Advanced insights
Deeper engagement data — scores, heatmaps, audience cross-tabs, and optimal-posting matrices — unlocked on higher plans. Each endpoint below shows the required tier.
GET/v1/insights/engagement-score/:type/:id
Available on Blend+ plansReturns a 0–100 engagement score for any single resource (:type = url or qr), blending volume, audience uniqueness, geographic spread, and recency. Useful for ranking your links/QRs programmatically.
{
"success": true,
"data": {
"engagement_score": 76,
"breakdown": {
"volume_score": 35, "uniqueness_score": 20,
"geo_spread_score": 12, "recency_score": 9
},
"signals": {
"total_events": 1420, "unique_visitors": 1103,
"unique_countries": 14, "hours_since_last_event": 3
}
}
}
GET/v1/insights/heatmap
Available on Transform planCity-level geographic heatmap across the entire account. Returns up to 500 points with lat/lng and engagement count — perfect for plotting on Mapbox, Leaflet, or your own visualization.
GET/v1/insights/audience
Available on Transform planDevice × referrer cross-tabs, browser × OS combinations, and mobile/tablet/desktop form-factor split. Answers "who's actually engaging?" in one call.
GET/v1/insights/optimal-times
Available on Transform planHour-of-day × day-of-week engagement matrix with the top 5 windows. Feed it into your scheduler to automatically post when your audience is most active.
{
"success": true,
"data": {
"matrix": [
{"weekday": 3, "weekday_name": "Tue", "hour": 14, "count": 187},
...
],
"top_5_windows": [
{"weekday": 3, "weekday_name": "Tue", "hour": 14, "count": 187}
]
}
}
Weekday values follow MySQL DAYOFWEEK: 1=Sunday … 7=Saturday.
Unlock every insight endpoint
Transform includes the full advanced-insights suite plus 60,000 API requests/mo, unlimited data retention, webhooks, and mobile deep linking.