All docs
5 min read Last updated:

Bio Pages

"Link in bio" landing pages. Bearer-auth, team-scoped, two ability tracks: bio:read / bio:write for management, plus the separate bio:analytics:read for the stats endpoints.

A Bio Page is a single public profile listing many blocks - it is not a form, survey, funnel, or link.

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

Method Path Ability
GET /bio-pages bio:read
POST /bio-pages bio:write
GET /bio-pages/search?q=… bio:read
GET /bio-pages/{bioPage} bio:read
PATCH /bio-pages/{bioPage} bio:write
DELETE /bio-pages/{bioPage} bio:write
GET /bio-pages/{bioPage}/stats/counters bio:analytics:read
GET /bio-pages/{bioPage}/stats/views bio:analytics:read

The {bioPage} path parameter accepts the page's ULID.

List pages

http
GET /bio-pages?per_page=25&search=acme

Returns the current team's Bio Pages, paginated and newest-first. The optional search filter matches the handle, display name, or headline. Each row includes a blocks_count.

Create a page

http
POST /bio-pages
Content-Type: application/json

{
  "handle": "acme",
  "display_name": "Acme Co.",
  "headline": "Everything Acme, one link.",
  "bio": "Product updates, support, and the latest drop.",
  "theme": { "palette": "midnight", "accent": "#4f46e5", "button_style": "pill" },
  "seo": { "title": "Acme", "description": "All of Acme in one place." },
  "blocks": [
    { "type": "header", "label": "Start here", "sort_order": 0 },
    { "type": "link", "label": "Latest drop", "url": "https://acme.example/new", "sort_order": 1 },
    { "type": "link", "label": "Tracked promo", "link_id": "01J…", "sort_order": 2 },
    { "type": "social", "icon": "instagram", "url": "https://instagram.com/acme", "sort_order": 3 }
  ]
}
  • handle - optional. When omitted (or when the requested handle is taken or reserved), a readable handle is generated. Allowed characters: letters, numbers, hyphens, underscores; must start with a letter or number.
  • display_name, headline, bio, avatar_path - optional profile fields.
  • theme - the theme tokens (see the themes doc).
  • seo - optional title / description overrides for the page metadata.
  • blocks - the ordered content rows. Each block has a type of link, header, text, social, embed, image, or form, plus label, url, link_id, icon, sort_order, is_active, and a type-specific config object. A link block may set link_id to reuse an existing short link's routing and click history.

New pages are created as drafts. Response 201: BioPageResource with its blocks.

Show a page

http
GET /bio-pages/{bioPage}

Returns the page with its blocks ordered by sort_order.

Search

http
GET /bio-pages/search?q=acme

Free-text search across handle, display name, and headline. Returns up to 50 lightweight matches (id, handle, display_name, headline).

Update a page

http
PATCH /bio-pages/{bioPage}
Content-Type: application/json

{
  "headline": "New season, same link.",
  "is_published": true,
  "blocks": [
    { "id": "01J…", "type": "link", "label": "Shop", "url": "https://acme.example/shop", "sort_order": 0 }
  ]
}

All fields optional. Set is_published to true to publish (the first publish stamps published_at) or false to unpublish. When blocks is present it replaces the page's block set - include every block you want to keep, each with its sort_order; include a block's id to update it in place, omit the id to create a new one.

Response 200: the updated BioPageResource.

Delete a page

http
DELETE /bio-pages/{bioPage}

Disables the handle immediately so the public page stops serving, then queues the page and its blocks for deletion. Response 200: { "deleted": true }.

Analytics

The two /stats/* endpoints require bio:analytics:read (separate from bio:read so you can issue read-only tokens that do not see traffic).

  • counters - views, distinct visitors, total clicks, per-block clicks, plus last-7-days and all-time view totals.
  • views - time-series of views and visitors at hour, day, or week resolution.

Both accept from, to, and tz query parameters. Windows are clamped to your plan's analytics retention. Views and clicks are recorded asynchronously, so analytics has an eventual-consistency lag of a few seconds.

Errors

Status When
402 Plan limit reached (creating beyond the Free-plan page limit) or the team's plan no longer includes Bio Pages
403 Token missing the required ability
404 Page does not belong to the current team
422 Validation (e.g. an invalid or already-used handle)

See also