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
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
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- optionaltitle/descriptionoverrides for the page metadata.blocks- the ordered content rows. Each block has atypeoflink,header,text,social,embed,image, orform, pluslabel,url,link_id,icon,sort_order,is_active, and a type-specificconfigobject. Alinkblock may setlink_idto 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
GET /bio-pages/{bioPage}
Returns the page with its blocks ordered by sort_order.
Search
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
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
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 athour,day, orweekresolution.
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
- Bio Pages overview →
- Tokens & abilities →
- MCP server → - the same operations as agent tools