Developers

URL shortener API and QR code API: one key, JSON in, short link out

Create, edit and measure short links and dynamic QR codes from code. A plain REST API with workspace keys, available on the free plan, with examples in curl, JavaScript and Python, and a WordPress plugin if you'd rather not write any.

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).

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

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.

Frequently asked questions

Is the API free?

Yes. Workspace API keys are available on the free plan, with the same limits as the dashboard (50 short links a month on Free, unlimited from Creator). There's no separate API pricing.

What are the rate limits?

600 requests per minute per key on the dashboard API, counted in fixed one-minute windows. Over the limit you get HTTP 429 with a Retry-After header. Redirects themselves are not rate-limited.

Can I choose the short code?

Yes: pass short_code (1–64 characters, letters, digits, hyphen, underscore). If it's taken you get a 409. Omit it and crisp generates a 3-character code that grows only as the space fills.

Do API-created links expire?

Not because of your plan or key status. Links and QR codes keep redirecting even if the key is revoked or the subscription ends; it's section 4 of the Terms of Service.

Is there an SDK?

The REST API is small enough that most people use fetch or requests directly. There are SDKs for React Native and Flutter for the deep-link surface (early access), and a WordPress plugin that wraps the links and QR endpoints.

Is there an OpenAPI spec?

Yes, apps/api-crisp-to/openapi.yaml in the repository covers the full surface; the docs site has the human-readable version.

Get a key, make a link

Create a workspace key under API keys in the dashboard. The first call takes a minute; the links outlast the key.

Start free →