All docs
4 min read

Links

Branded short links + QR codes. Bearer-auth, team-scoped, two ability tracks: links:read / links:write for management, plus the separate links:analytics:read for the stats endpoints.

Base URL: https://formspring.io/api/v1

Method Path Ability
GET /links links:read
POST /links links:write
GET /links/search?q=… links:read
GET /links/query?slug=… links:read
POST /links/upsert links:write
GET /links/{link} links:read
PATCH /links/{link} links:write
DELETE /links/{link} links:write
GET /links/{link}/qr?format=svg|png links:read
GET /links/{link}/stats/counters links:analytics:read
GET /links/{link}/stats/views links:analytics:read
GET /links/{link}/stats/heatmap links:analytics:read
GET /links/{link}/stats/metrics links:analytics:read
GET /links/{link}/stats/events links:analytics:read
GET /links/{link}/stats/locations links:analytics:read

The {link} path parameter accepts the link's ULID.

List links

http
GET /links?per_page=25

Returns the current team's links (excluding trashed), paginated.

Create a link

http
POST /links
Content-Type: application/json

{
  "target_url": "https://example.com/very/long/landing/path?utm_source=…",
  "slug": "spring-promo",
  "title": "Spring 2026 promo",
  "branded_domain_id": 4,
  "utm_params": {
    "utm_source": "newsletter",
    "utm_campaign": "spring-2026"
  }
}
  • target_url required.
  • slug - when omitted, the API picks an unambiguous slug from the configured alphabet (no 0/O/1/l/i).
  • branded_domain_id - bind to a verified branded domain. Without this the link serves on the team's shortener domain (e.g. frmsp.io).
  • utm_params - appended to the target URL on every redirect when redirect_with_query is enabled.

Response 201: LinkResource including the canonical short_url.

Idempotent upsert

http
POST /links/upsert
Content-Type: application/json

{
  "slug": "spring-promo",
  "target_url": "https://example.com/new-target"
}

Useful from CI / sync scripts: returns 201 when the slug is new, 200 when it already exists and the row was updated. The slug field is required.

Search

http
GET /links/search?q=spring

Free-text search across slug, title, and target URL. Returns up to 50 matches.

Query by exact slug

http
GET /links/query?slug=spring-promo

Strict lookup. Returns the resource or 404 - useful when you have a known slug from an external system and want to confirm it exists before redirecting.

Update a link

http
PATCH /links/{link}
Content-Type: application/json

{
  "target_url": "https://example.com/new-target",
  "disabled_at": null
}

All fields optional. Set disabled_at to a timestamp to disable the redirect without deleting the link; set it to null to re-enable. Disabling is also what DELETE does as a guardrail.

QR code

http
GET /links/{link}/qr
GET /links/{link}/qr?format=png

Renders the link's branded QR code as SVG (default) or PNG. Caches edge-side; rotation of the QR happens when the team updates branding or rotates the link.

Delete a link

http
DELETE /links/{link}

Soft-deletes + sets disabled_at. The short URL stops serving immediately even if the soft-delete scope is bypassed by a cached resolver. Recoverable from the admin trash.

Analytics

The six /stats/* endpoints all require links:analytics:read (separate from links:read so you can issue read-only tokens that don't see who clicked what).

  • counters - totals by horizon (today, 7d, 30d, all-time).
  • views - time-series of clicks at hourly/daily resolution.
  • heatmap - 7 × 24 matrix for day-of-week × hour-of-day visualization.
  • metrics - bucketed dimensions (browser, OS, device, referrer host).
  • events - recent raw click events (capped, useful for live tail).
  • locations - top countries / cities by click volume.

Click events are recorded asynchronously; analytics has an eventual-consistency lag of a few seconds during high-traffic periods.

Errors

Status When
403 Token missing required ability
404 Link doesn't belong to the current team, or has been hard-deleted
422 Validation (e.g. slug collision when upserting against a different team's link)

See also