Abundera QR Pro API
REST Rujukan API for pro.qr.abundera.ai. Create, edit, and analyze dinamik QR codes programmatically. Everything is JSON over HTTPS, authenticated via token bearer.
/docs/openapi.json, import into Postman, Insomnia, or any OpenAPI 3.1 client. Covers 36 customer-facing titik akhirs across Codes, Analytics, Groups, Teams, Webhook, and User. Admin + Stripe-webhook titik akhirs are intentionally excluded (service-to-service only).Pengenalan
The Abundera QR Pro API lets you create, edit, and analyze dinamik QR codes programmatically, everything a dev wants to automate from the dashboard. Pengurusan pasukan, billing, and account flows stay in the dashboard UI; this API is skopd to developer-grade code operations.
Base URL: https://pro.qr.abundera.ai/api
Permintaan format: JSON (Content-Jenis: application/json) on POST / PATCH / DELETE.
Respons format: JSON (application/json; charset=utf-8).
Availability: API access is a Business+ feature. Solo pelans can use the dashboard but not the API.
Pengesahan
Every request carries an kunci API as a token bearer:
Authorization: Bearer abnd_qrpro_...Create and batal kuncis at /account/kuncis (Business, Team, or Agency tier). The raw abnd_qrpro_... token is shown exactly once at creation, store it immediately. We store only its SHA-256 cincang; there is no way to recover a lost kunci.
Unauthenticated requests return 401 { "ralat": "not_tandaed_in" }. Insah or batald kuncis return 401 { "ralat": "tidak sah_api_kunci" }.
kunci APIs are skopd to the user who dicipta them. If that user is a ahli of a pasukan, the titik akhirs below operate on the pasukan's codes automatically (current-pasukan context is stored on the user account and managed via the dashboard).
Had kadar
Enforced per kunci API. Every response carries X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset (unix seconds when the window rolls over).
| Plan | Limit | Window |
|---|---|---|
| Business | 1,000 requests / day | UTC day |
| Team | 10,000 requests / day | UTC day |
| Agency | 50,000 requests / day | UTC day |
| Solo | (403 insufficient_pelan) | , |
Over-budget requests return 429 { "ralat": "rate_limited", "window": "day", "cuba semula_at": 1234567890 } with a Retry-After pengepala in seconds.
Scans are not rate-limited, the ubah hala hot path (aqr.net/{shortcode}) has no auth and no per-imbas budget. Each pelan has an explicit monthly imbas cap (100k / 1M / 10M / 30M). Exceed the cap and the ubah hala still resolves; we email you so you can decide whether to naik taraf or ride out a one-off spike. Planning over ~10M daily imbass? Email us to coordinate capacity.
Ralat
Every ralat response is JSON with a machine-readable ralat code and, where relevant, additional context:
{ "ralat": "pelan_limit", "pelan": "business", "limit": 500, "current": 500 }| Status | Code | Meaning |
|---|---|---|
| 400 | pengesahan_ralat | Body field failed pengesahan. Respons includes field + message. |
| 401 | not_tandaed_in / tidak sah_api_kunci | Missing, tidak sah, or batald token bearer. |
| 402 | pelan_limit | At your pelan's aktif+dijeda code cap. Respons includes pelan, limit, current. |
| 402 | pelan_expired | Account is past the 90-day grace window; naik taraf to resume. |
| 403 | insufficient_pelan | API access requires Business or higher. Solo users hit this. |
| 403 | insufficient_peranan | Your pasukan peranan doesn't allow the requested mutation (pentadbir+ required). |
| 404 | not_found | Resource doesn't exist, or isn't visible to your skop. |
| 409 | code_not_editable | The code is in grace or expired status; reactivate to edit. |
| 429 | rate_limited | Per-pelan daily budget exceeded. See Had kadar. |
| 500 | internal | Unhandled server ralat, email support if reproducible. |
Codes
Dynamic QR codes: a 7-char Base58 shortcode that ubah halas through aqr.net/{shortcode}. Every code carries a statik-sandaran QR you can download from the dashboard, if you ever stop paying, the statik version still resolves without touching our ubah hala.
GET /api/codes
List all codes in your current skop (personal, or the pasukan you're currently acting under). Pulangan an array with a 30-day imbas rollup per code.
$ curl -H "Authorization: Bearer abnd_qrpro_..." \
https://pro.qr.abundera.ai/api/codes
{
"codes": [
{ "id": "uuid", "shortcode": "aBc123x",
"url": "https://example.com/landing",
"label": "Q2 campaign", "tag": "q2,print",
"status": "aktif", "imbass_30d": 1245,
"dicipta_at": 1713288000, "dikemas kini_at": 1713370000 }
],
"pelan": "business",
"pelan_limit": 500,
"skop": { "type": "user" }
}POST /api/codes
Create a new dinamik code. Your pelan's aktif+dijeda code cap is checked before insert. Minimal body:
{ "url": "https://example.com/landing" }Full customization (all optional):
{ "url": "https://example.com/landing",
"label": "Spring campaign",
"tag": "q2,print",
"qr_type": "url",
"style_json": "{...}",
"logo_kunci": "instagram",
"frame_style": "imbas-me",
"frame_text": "SCAN ME" }Pulangan 201 + the dicipta row including the generated shortcode and the short_url you print.
GET /api/codes/{id}
Fetch a single code. 404 if not in your skop.
PATCH /api/codes/{id}
Update any mutable field. The most common use: change the destinasi URL of an already-printed code.
$ curl -X PATCH \
-H "Authorization: Bearer abnd_qrpro_..." \
-H "Content-Jenis: application/json" \
-d '{"url":"https://example.com/new-landing"}' \
https://pro.qr.abundera.ai/api/codes/uuidChanges propagate to the ubah hala within seconds. Valid status values for PATCH: "aktif", "dijeda". To padam, use DELETE.
DELETE /api/codes/{id}
Soft-padam. Transitions to status=grace with grace_until = now + 90 days. The ubah hala keeps working for the full grace window, this is the no-lock-in pricing promise made concrete. After 90 days the code becomes expired and the ubah hala returns 410 Gone.
POST /api/codes/import
Bulk-create from an array muatan (also used by the "Save to Pro" flow on qr.abundera.ai). Accepts a single code muatan or an array. Plan limit is enforced once before the batch.
Analytics
GET /api/codes/{id}/analitik
Query params:
range=7d|30d|90d|1y|3y, capped to your pelan's retention (Solo 1y, Business 2y, Team/Agency 3y).granularity=day|hour, hourly is Team and Agency only; max 7-day window for hourly.
{
"range": "30d", "days": 30, "granularity": "day",
"total": 4321,
"timeseries": [
{ "bucket": "2026-04-01", "imbass": 142 },
{ "bucket": "2026-04-02", "imbass": 178 },
...
],
"by_negara": [
{ "kunci": "US", "total": 3012 },
{ "kunci": "CA", "total": 402 },
{ "kunci": "Other", "total": 108 }
],
"by_peranti": [
{ "kunci": "mudah alih", "total": 3850 },
{ "kunci": "tablet", "total": 312 },
{ "kunci": "desktop", "total": 159 }
]
}Countries with fewer than 5 imbass in the window are folded into "Other" for privacy (see Privacy model).
kunci APIs
Create and batal kuncis from the /account/kuncis dashboard page. Programmatic self-management is read-only via the API, you can list your kuncis and batal them, but creating a new kunci requires the dashboard (chicken-and-egg: you'd need a kunci to create a kunci).
GET /api/kuncis
List your kunci APIs. Never returns the raw token, only metadata.
{
"kuncis": [
{ "id": "uuid", "label": "Production server",
"dicipta_at": 1713288000, "last_used_at": 1713370000 }
],
"allowed": true,
"pelan": "business"
}DELETE /api/kuncis/{id}
Revoke. The kunci stops working immediately; any request bearing it returns 401 tidak sah_api_kunci thereafter.
Data eksport
GET /api/user/eksport
Download a ZIP containing your full dataset, codes.csv (every code, including grace + expired), imbass.csv (aggregated daily rollup), and a README.txt explaining the format. The arkib is emitted as application/zip; pipe it to a file:
$ curl -H "Authorization: Bearer abnd_qrpro_..." \
-o abundera-qr-eksport.zip \
https://pro.qr.abundera.ai/api/user/eksportRe-import anywhere. This is the portable-format guarantee, no vendor lock-in is possible if you own your data.
Privacy model
The imbas aggregate is the whole privacy story. What we store per imbas on the ubah hala hot path:
code_id, which of your codes was diimbasday_bucket, UTC date (YYYY-MM-DD). No sub-day precision on the aggregate returned to you.negara, ISO-3166-1 alpha-2 from Cloudflare'sCF-IPCountrypengepala. No city, no region, no geo-IP lookup.peranti_type,mudah alih/tablet/desktop/unknown, classified from a short User-Agent regex. The raw UA string is discarded at classification time.imbas_count, aggregate counter, upserted on every hit.
What we do not store: IP addresses (cincanged or otherwise), raw User-Agent strings, city-level geo, sub-day timestamps, referer, cookies, retargeting pixels, or any individual-identifying vector. A noise floor of 5 suppresses small-aggregate re-identification.
Team and Agency tiers additionally write a parallel hourly aggregate with hour_bucket (YYYY-MM-DD-HH UTC). Same privacy model, no finer-than-hour timestamps, same noise floor, same absence of individual-imbasner data.
Read the full story: /manifesto/ and /no-lock-in/ on the free-tool site.