Link-in-bio page

One public page per brand for the link in your profiles, with its views and the clicks on each link.

View as MarkdownOpenAPIBase URL https://postwire.io

Each brand can have a page at postwire.io/@<slug>, free on every plan: the brand's name, the avatar of a connected account, a short bio, up to 10 links and the connected profiles as icons. The address is 3-30 lowercase letters, digits and hyphens, unique, and not a reserved word (GET /api/bio?check=<slug> says whether one is free). Links are http or https only; each is published as a tracked postwire.io/r/ link, so GET /api/bio returns the clicks on each link and the page's views, all time and the last 30 days, counting people only (link previews and bots are left out). The page carries no script, escapes everything and has a "Report this page" link. "Made with PostWire" sits at the bottom and can be switched off (hide_badge) on paid plans. The page is cached for a minute, so a change shows within about a minute.

The free tools that need no account are at postwire.io/tools.

Read a brand's link-in-bio page, its views and each link's clicks

GET/api/bio

The brand's page at postwire.io/@<slug> (free on every plan): its settings, the public URL, views (people only: link previews and bots are left out) and clicks per link, all time and the last 30 days. With ?check=<slug>, says whether that address is free and allowed instead. Before the server's bio-page table exists the answer is available: false, code bio_unavailable.

Needs an API key: Authorization: Bearer pw_live_….

Parameters

FieldTypeDescription
brand_idquery, string (uuid)The brand; default: the account's first brand
checkquery, stringAn address to check: { slug, ok, code?, error? }

Responses

StatusMeans
200{ available, brand_id, page: { slug, title, bio, links: [{ label, url }], show_socials, hide_badge, updated_at } | null, url, stats: { views, views_30d, links: [{ label, url, clicks, clicks_30d }] } | null, socials: [{ platform, handle }], avatar_url, can_hide_badge, limits }
404code brand_not_found
curl
curl "https://postwire.io/api/bio?brand_id=b3f1c2d4-8a9e-4f6b-a1c2-3d4e5f6a7b8c" \
  -H "Authorization: Bearer $POSTWIRE_API_KEY"
Node
const res = await fetch("https://postwire.io/api/bio?brand_id=b3f1c2d4-8a9e-4f6b-a1c2-3d4e5f6a7b8c", {
  method: "GET",
  headers: { Authorization: `Bearer ${process.env.POSTWIRE_API_KEY}` },
});
console.log(res.status, await res.json());
Python
import os, requests

r = requests.get(
    "https://postwire.io/api/bio?brand_id=b3f1c2d4-8a9e-4f6b-a1c2-3d4e5f6a7b8c",
    headers={"Authorization": f"Bearer {os.environ['POSTWIRE_API_KEY']}"},
)
print(r.status_code, r.json())

Create or update a brand's link-in-bio page

PUT/api/bio

Free on every plan. The address is 3-30 lowercase letters, digits and hyphens, unique, and not a reserved word. Up to 10 links, http or https only; each is published as a tracked postwire.io/r/ link so its clicks are counted. "Made with PostWire" can be switched off on paid plans only (on Free the request is saved with it on, and the answer says so in note). Changes show within about a minute.

Needs an API key: Authorization: Bearer pw_live_….

Body (JSON)

FieldTypeDescription
brand_idstring (uuid)
slug requiredstringpostwire.io/@<slug> Up to 30 characters.
titlestringThe name shown; the brand's name when empty Up to 60 characters.
biostringUp to 280 characters.
linksarray of objectUp to 10 items.
links[].label requiredstringUp to 60 characters.
links[].url requiredstring (uri)Up to 1000 characters.
show_socialsbooleanThe brand's connected profiles as icons Default true.
hide_badgebooleanPaid plans only Default false.

Responses

StatusMeans
200{ ok: true, note?, ...the same body as GET }
400code slug_required, slug_length, slug_chars, slug_reserved, bad_url, label_required or too_many_links, with field (e.g. links.2.url) and every problem in errors[]
409code slug_taken
503code bio_unavailable: bio pages are not switched on on this server yet
curl
curl -X PUT "https://postwire.io/api/bio" \
  -H "Authorization: Bearer $POSTWIRE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "brand_id": "b3f1c2d4-8a9e-4f6b-a1c2-3d4e5f6a7b8c",
  "slug": "luna-bakery",
  "title": "Luna Bakery",
  "bio": "Sourdough and custom cakes in Lima. Order by 6 pm for next-day pickup.",
  "links": [
    {
      "label": "Order a cake",
      "url": "https://lunabakery.example/cakes"
    },
    {
      "label": "This week'\''s menu",
      "url": "https://lunabakery.example/menu"
    }
  ]
}'
Node
const res = await fetch("https://postwire.io/api/bio", {
  method: "PUT",
  headers: { Authorization: `Bearer ${process.env.POSTWIRE_API_KEY}`, "Content-Type": "application/json" },
  body: JSON.stringify({
    "brand_id": "b3f1c2d4-8a9e-4f6b-a1c2-3d4e5f6a7b8c",
    "slug": "luna-bakery",
    "title": "Luna Bakery",
    "bio": "Sourdough and custom cakes in Lima. Order by 6 pm for next-day pickup.",
    "links": [
      {
        "label": "Order a cake",
        "url": "https://lunabakery.example/cakes"
      },
      {
        "label": "This week's menu",
        "url": "https://lunabakery.example/menu"
      }
    ]
  }),
});
console.log(res.status, await res.json());
Python
import os, requests

r = requests.put(
    "https://postwire.io/api/bio",
    headers={"Authorization": f"Bearer {os.environ['POSTWIRE_API_KEY']}"},
    json={
        "brand_id": "b3f1c2d4-8a9e-4f6b-a1c2-3d4e5f6a7b8c",
        "slug": "luna-bakery",
        "title": "Luna Bakery",
        "bio": "Sourdough and custom cakes in Lima. Order by 6 pm for next-day pickup.",
        "links": [
            {
                "label": "Order a cake",
                "url": "https://lunabakery.example/cakes",
            },
            {
                "label": "This week's menu",
                "url": "https://lunabakery.example/menu",
            },
        ],
    },
)
print(r.status_code, r.json())

Remove a brand's link-in-bio page

DELETE/api/bio

The address becomes free for anyone; the page's views and clicks are deleted with it.

Needs an API key: Authorization: Bearer pw_live_….

Parameters

FieldTypeDescription
brand_idquery, string (uuid)

Responses

StatusMeans
200{ ok: true, removed: <slug> }
404code bio_not_found or brand_not_found
curl
curl -X DELETE "https://postwire.io/api/bio?brand_id=b3f1c2d4-8a9e-4f6b-a1c2-3d4e5f6a7b8c" \
  -H "Authorization: Bearer $POSTWIRE_API_KEY"
Node
const res = await fetch("https://postwire.io/api/bio?brand_id=b3f1c2d4-8a9e-4f6b-a1c2-3d4e5f6a7b8c", {
  method: "DELETE",
  headers: { Authorization: `Bearer ${process.env.POSTWIRE_API_KEY}` },
});
console.log(res.status, await res.json());
Python
import os, requests

r = requests.delete(
    "https://postwire.io/api/bio?brand_id=b3f1c2d4-8a9e-4f6b-a1c2-3d4e5f6a7b8c",
    headers={"Authorization": f"Bearer {os.environ['POSTWIRE_API_KEY']}"},
)
print(r.status_code, r.json())