API
Read your codes, campaigns, placements and scan totals from your own software. It is read-only on purpose: nothing has asked to make a code from a script, and a token that can only read is a much smaller thing to lose.
Getting a token
An Admin or Owner makes one in the workspace. It is shown once — qurl keeps only a hash of it, so it cannot be shown again — and it belongs to the workspace rather than to a person, so it keeps working when someone leaves. Revoking one stops it at once.
Send it as a bearer token. Everything is under https://app.qurl.co/v1, and every endpoint is a GET.
curl -H "Authorization: Bearer qurl_live_…" \ https://app.qurl.co/v1/codes
What you can read
GET /v1/codes
Every code in the workspace: its short link, name, kind, status, current destination and campaign.
GET /v1/codes/{short link}
One code, with its rules in order, their conditions, whether each is paused, any expiry date, and whether a password is set. The password itself is never returned.
GET /v1/campaigns
Campaigns with their start, end, key dates and goal. Key dates are what open-day rules read.
GET /v1/placements
Where printed codes are, with their coordinates and the date each went up.
GET /v1/scans/daily
Scans a day at a time, with people counted once a day as everywhere else. Takes from, to and code; up to 400 days at a time, and the last 30 days by default.
Small groups stay small
A count of fewer than ten comes back as the word "few" rather than a number, exactly as it does on a report. The API is not a way around that: it is the same rule, in the same place, for the same reason — a figure small enough to point at one family is not a figure qurl hands out.
{"day": "2026-10-04", "scans": "few", "people": "few"}
{"day": "2026-10-05", "scans": 318, "people": 240}
What it cannot tell you
There is no endpoint that returns an IP address, because qurl does not keep one. The same is true of anything that would identify a person who scanned. What a scan holds is in the privacy page, and the API shows no more than a report does.
Webhooks
For the other direction, a workspace can send its events to a URL you choose: a batch of scans, a destination changed, a code stopped. Each delivery carries a X-Qurl-Signature header — an HMAC of the body with your webhook's secret — so you can tell it came from qurl. Zapier and Make both accept one, which is how a qurl workspace reaches either.
A failing endpoint is retried with a widening gap and given up on after eight attempts, and every attempt is in a log an Admin can read.
A webhook can also be set up as one of two presets instead of a raw post:
- Google Analytics 4. Each scan becomes a qurl_scan event on the Measurement Protocol, carrying the code, campaign, country, rough area and kind of phone. Add your measurement id and an API secret from the data stream. GA4 asks for a client id and qurl has nothing to put there — it keeps no cookie and no IP, so it cannot tell one phone from another — so each scan gets a fresh one. Event counts are right; GA4’s “users” figure will equal scans and means nothing. That is qurl’s privacy promise showing through, not a fault.
- Microsoft Teams. A card in a channel when a code is stopped or a destination changes — the things somebody has to act on. Scans are deliberately not sent: a card per scan is noise, and noise gets muted. Use a Power Automate Workflows webhook; the older Office 365 connector stopped working on 30 April 2026.
Presets authenticate by their own address, so they carry no signature header. A raw webhook still does.
Limits
- 600 requests an hour for each token. Ask for a range of days rather than polling a day at a time.
- Ten tokens and ten webhooks for each workspace.
- Server to server: the API sends no cross-origin headers, so call it from your own backend rather than from a browser.