REST API
A JSON API over HTTPS for links, analytics, QR codes, domains and workspaces. The full machine-readable reference is the OpenAPI 3.1 spec at https://linkapp-staging.funnelkit.com/api/openapi.json.
Base URL
https://linkapp-staging.funnelkit.com/api
Authentication
Send a workspace API key as a Bearer token on every request. Keys start with fks_ and are created in the dashboard under Settings, API keys.
Authorization: Bearer fks_YOUR_KEY
A key acts on the workspace it was created in. A missing or invalid key returns 401.
Create a link
POST /links
curl -X POST https://linkapp-staging.funnelkit.com/api/links \
-H "Authorization: Bearer $FKS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/pricing",
"slug": "pricing",
"tags": ["web"],
"utm": { "source": "newsletter", "medium": "email" }
}'
Returns 201 with the link. Only url is required.
| Field | Type | Notes |
|---|---|---|
url | string | Destination, http or https. Scanned before the link is created. |
slug | string | 1 to 64 letters, digits, - or _. Random 7 characters if omitted. |
domain | string | A verified custom domain. Defaults to link-staging.funnelkit.com. |
title | string | Up to 200 characters. |
tags | string[] | Up to 20 tags. |
utm | object | source, medium, campaign, term, content. |
password | string | Pro and up. |
expiresAt, expiredUrl | string | ISO 8601 time and a fallback destination. Pro and up. |
geo | object | Country code to URL, for example {"DE":"https://example.de"}. Pro and up. |
iosUrl, androidUrl | string | Mobile deep links. Team and up. |
redirectType | number | 302 (default) or 301. |
To create many links at once, POST /links/bulk with {"links":[...]} (up to 50 per request; the CLI splits larger files automatically). The response lists the result for each item.
List links
GET /links
curl "https://linkapp-staging.funnelkit.com/api/links?limit=25&tag=web" \
-H "Authorization: Bearer $FKS_API_KEY"
Query parameters: q (search), tag, domain, archived, limit (1 to 100, default 25) and cursor. The response is {"links":[...],"nextCursor":"..."}; pass nextCursor back as cursor to get the next page. It is null on the last page. Archiving a link only hides it from lists: it keeps redirecting. To stop a link but keep its analytics, set expiresAt.
Get and update a link
GET /links/{id} returns one link. PATCH /links/{id} changes any field except domain. Edits are unlimited on every plan.
curl -X PATCH https://linkapp-staging.funnelkit.com/api/links/LINK_ID \
-H "Authorization: Bearer $FKS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com/pricing-2027"}'
Link stats
GET /links/{id}/stats
curl "https://linkapp-staging.funnelkit.com/api/links/LINK_ID/stats?from=2026-09-01T00:00:00Z&interval=day" \
-H "Authorization: Bearer $FKS_API_KEY"
Returns totals, a timeseries and a breakdown by country, device, OS, browser, referrer and trigger. from and to are ISO 8601 times (default: the last 30 days) and interval is hour or day. The window is limited to your plan's analytics retention. GET /stats returns the same for the whole workspace plus the top links.
Delete a link
DELETE /links/{id}
curl -X DELETE https://linkapp-staging.funnelkit.com/api/links/LINK_ID \
-H "Authorization: Bearer $FKS_API_KEY"
Returns {"ok":true}. The short URL stops redirecting within about a minute.
Other endpoints
| Endpoint | What it does |
|---|---|
GET /links/{id}/qr | SVG QR code. size, ecc, and on Pro and up fg/bg colours. |
GET /tags | Tags with link counts. |
GET /stats/export | Click data as CSV. Team and up. |
GET /domains, POST /domains | List or add custom domains. |
POST /domains/{id}/verify | Re-check DNS and the certificate. |
GET /workspace | The current workspace, plan and usage. |
Errors
Every error has the same shape:
{
"error": {
"code": "validation_failed",
"message": "Use 1-64 letters, digits, \"-\" or \"_\"",
"details": { "field": "slug" }
}
}
code is stable and safe to branch on; message is for people; details is optional.
| Status | Codes | Meaning |
|---|---|---|
401 | unauthorized | Missing or invalid API key. |
402 | plan_limit_reached, feature_not_in_plan | The new-link allowance is used up, or the feature needs a higher plan. Existing links keep working. |
403 | forbidden | Your role cannot do this, or the workspace is suspended. |
404 | not_found | No such link or domain in this workspace. |
409 | slug_taken, conflict | The slug is already used on that domain. |
422 | validation_failed, unsafe_url | The body is invalid, or the destination failed the safety scan. |
429 | rate_limited | Too many requests. Wait and retry. |
Rate limits
Requests per minute, per workspace: 60 on Free, 600 on Pro, 3,000 on Team and 10,000 on Scale. Over the limit you get 429; back off and retry.