Everything in the crisp dashboard is a call to the same API the dashboard uses. Create a workspace key, send JSON, get a short link back.
Authentication
One header. Keys start with wk_live_ and are created under API keys in the dashboard; each key belongs to a workspace and can be revoked at any time without affecting the links it created.
Authorization: Bearer wk_live_…
Responses use a single envelope: { "data": … } on success, { "error": { "code", "message" } } on failure, with conventional HTTP status codes (400 validation, 401 bad key, 402 plan limit, 404, 409 short code taken, 429 rate limited).
Create a short link
curl -X POST https://api.crisp.to/v1/dashboard/links \
-H "Authorization: Bearer wk_live_…" \
-H "Content-Type: application/json" \
-d '{"type":"short","destination_url":"https://example.com/sale","short_code":"sale","tags":["summer"]}'
# 201 → {"data":{"link":{"id":"lnk_…","short_code":"sale",…},"url":"https://crisp.to/sale"}}
// JavaScript (Node 18+, Deno, Bun, browsers)
const res = await fetch("https://api.crisp.to/v1/dashboard/links", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.CRISP_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({ type: "short", destination_url: "https://example.com/sale" }),
});
const { data, error } = await res.json();
if (error) throw new Error(`${error.code}: ${error.message}`);
console.log(data.url); // https://crisp.to/x7K
# Python
import os, requests
r = requests.post(
"https://api.crisp.to/v1/dashboard/links",
headers={"Authorization": f"Bearer {os.environ['CRISP_KEY']}"},
json={"type": "short", "destination_url": "https://example.com/sale", "title": "Summer sale"},
timeout=10,
)
body = r.json()
print(body["data"]["url"] if r.ok else body["error"])
Optional fields: short_code, title, og_image (the preview image when the link is shared), tags (up to 10), expires_at (ISO 8601, for links that should stop on purpose; the default is never), and UTM parameters on the destination URL itself.
Edit, list, delete
Every link is dynamic. Change where it goes without changing the link:
curl -X PATCH https://api.crisp.to/v1/dashboard/links/lnk_… \
-H "Authorization: Bearer wk_live_…" -H "Content-Type: application/json" \
-d '{"destination_url":"https://example.com/sale-extended"}'
List with filters and search (GET /v1/dashboard/links?type=short&q=sale&tag=summer), export everything as CSV (GET /v1/dashboard/links/export), or delete (DELETE /v1/dashboard/links/{id}). Each link in a list carries its rolled-up clicks.
QR codes
A QR code is a short link plus rendering options, so every code is dynamic and its scans are the link's clicks:
curl -X POST https://api.crisp.to/v1/dashboard/qr \
-H "Authorization: Bearer wk_live_…" -H "Content-Type: application/json" \
-d '{"name":"Menu","destination_url":"https://cafe.example/menu","fg_color":"#1A1712","bg_color":"#FBF4E9"}'
# 201 → {"data":{"qr":{…},"url":"https://crisp.to/x7Kq"}}
Colours work on every plan; the style object (dot shapes, corner styles, a centre logo) is a Creator feature. PATCH /v1/dashboard/qr/{id} with a new destination_url re-points the code; printed copies keep working. Render the PNG or SVG from the returned URL in the dashboard, or with any QR library on your side; the data to encode is just the short URL.
Analytics
GET /v1/dashboard/links returns click totals per link; GET /v1/dashboard/geo returns clicks by country for the workspace over a range (7d, 30d, 90d, clamped to your plan). Page views and per-block clicks for pages come from GET /v1/dashboard/pages/{id}/stats. Every click records platform, country and referrer, so traffic from AI assistants shows up as its own referrer.
Pages
Pages (your small website) are also on the API: create with a handle and display name, PATCH the blocks, POST …/publish, download subscribers as CSV, and attach a custom domain on the Site plan. The block schema is documented on the docs site.
Limits and guarantees
- Rate limit: 600 requests per minute per key (fixed windows;
429+Retry-Afterbeyond that). Redirects are served from the edge and aren't limited. - Plan limits: Free is 50 short links a month; links created by publishing a page don't count. Hitting a limit returns
402with a message naming the plan that lifts it. - Durability: links and QR codes are never switched off because of plan or key status, even after a key is revoked or a subscription ends. Terms §4.
- Idempotency: creating with an existing
short_codereturns409; listing is safe to retry.
No code at all
The WordPress plugin wraps the links and QR endpoints: [crisp_url url="…"] and [crisp_qr url="…"] shortcodes, plus a one-click short link for every post.
The full reference, including the pages and deep-link surfaces, is on docs.crisp.to.