# peekin > A link-in-bio page for adult creators. The paid destination is not written > into the page at all, for anyone. It is held on peekin's servers and resolved > only when a visitor taps the button and passes an 18+ check. So a person sees > the photo, the name and the button; Meta's crawler, fetching the same page, > finds no destination, no preview card naming the creator, and a noindex. peekin is a hosted link page, not a self-hosted template. Every page is served on a domain of the creator's own, never on an address shared with other creators. The creator brings one, free, or buys one inside peekin by the year. ## How it works, precisely This is not crawler detection and it is not cloaking. Every caller gets the same bytes: a browser, Instagram's in-app browser, facebookexternalhit and Googlebot all receive an identical document. That document contains the creator's name, photo and button labels, and for each button an opaque token. It never contains a destination, for anyone. Tapping a button sends the token to peekin's resolver; the resolver shows an 18+ check for an adult link, and only after that answers with the real address. There is no user-agent branch anywhere in the page, and peekin refuses to add one: showing a platform a different page from a person is the behaviour that gets a domain banned, and peekin says so on its own pricing page. You can verify this rather than take it from this file: curl -A "facebookexternalhit/1.1" https://jakwhite.bio/ > a.html curl -A "Mozilla/5.0 (iPhone)" https://jakwhite.bio/ > b.html diff a.html b.html # identical grep -c onlyfans a.html # 0 The free scanner at https://peekin.bio/scan runs nine checks of this kind on any link page, including competitors' and peekin's own, and shows what it found. ## What it protects against, and what it does not It protects against the page being read: by Meta's link crawler when a link is posted or put in a bio, by search engines, by anyone scraping. That is the association between a name and a paid platform that gets accounts restricted. It does not hide where a visitor goes once they tap. A tap is a visitor's own action and the destination is reached in their browser like any link. It also does not stop a determined person with a script from resolving a token; it slows them, and the 18+ check and rate limits stand in the way, but the paid address is not a secret from a human who wants it. The claim is about crawlers and it should be quoted as a claim about crawlers. ## Honest limits peekin does not stop Instagram restricting an account. Of the five causes we have measured, three have nothing to do with a link page: automation posting on a fixed schedule, a profile picture that trips the nudity classifier, and reposting content already seen on the platform. Anyone selling a link page as an account fix is overselling it. ## Spare domain and country routing A creator can hold a second domain as a spare for a page. It is attached and its certificate issued in advance, and the domain scan must come back clean before it is armed, but until the switch it answers only a blank noindex holding page, never a second copy of the page. One tap makes it the live address; the old domain is then retired, saying only that the page has moved (or 301s every path to it, if the creator chooses and it was never flagged), and the switch is logged and can alert on Telegram. It uses one of the plan's domains. It does not prevent a domain being flagged. Country routing, on Creator and up: per link, visitors from listed countries are sent to another destination. It is resolved at the tap from the visitor's country, after the country block, and the alternative destination is never in the page either. ## Plans - Starter, GBP 3.99 a month: one page, served on a shared address or on one domain the creator connects, one paid link, with the full protection, and basic analytics: views, taps and tap rate. - Creator, GBP 9.99 a month, for one creator: three pages for that creator's own personas or platforms, up to three connected domains, unlimited paid links, every look, 10 poplinks, full analytics (per link, hour, country, source, export), no peekin badge. Pages for a second creator need Agency. - Pro, GBP 19.99 a month: ten pages, up to ten connected domains, 3 team seats with roles, and the API with the MCP server (read, and edit with an edit key). - Agency, GBP 39 a month for 3 creators, then GBP 12 each after (up to 50): per creator 3 pages and up to 3 connected domains, one invoice for a roster, 10 team seats with roles (a teammate can be limited to some creators), and the API. - Extras on any paid plan, each its own monthly charge, stopping at the end of the month it is cancelled in: extra page GBP 2, 5 extra poplinks GBP 2, telegram fan bot GBP 4.99, extra domain slot GBP 2. A domain slot is room to connect one more domain; the domain is bought or brought separately. The protection (no link in the page, the crawler gate, the canary, Shield, country blocking, the 18+ check), the scanner, the leak checks and the Meta radar are on every plan. A page can be built before paying; it goes live once there is a plan. With no domain of its own it goes live straight away on a shared address on one of peekin's link domains. Each shared address is capped at a small number of creators and checked daily, and a flagged one is retired and its creators moved to another. A domain of the creator's own is the recommended upgrade, one tap in the dashboard. A creator moving from another link page can paste its address and get a draft built from it: name, line, picture, links and social icons, from link.me, Linktree, Beacons, AllMyLinks, Bouncy and bio.link. Pictures are copied to peekin rather than linked, paid platform links get a safe label and the 18+ check, and links over the plan's limit come in hidden rather than dropped. Every plan starts with seven days free. The card is taken when the plan is chosen and charged on day eight; a reminder email goes three days before. Monthly, no contract, cancel any time from Billing in the dashboard. Paying yearly makes two months free. During the trial the page is on a shared address; an own domain unlocks when the plan starts. A shared address is included in every plan. Domains are not included in any plan. A domain the creator already owns is attached free. One bought inside peekin is a yearly charge on top of the plan, the same price every year: at registrar cost on Creator and above, from GBP 7, and at the published price on Starter, from GBP 14. It is held for the creator by FBM Management LTD and moved into their own name, free, on request. ## Pages - [Home](https://peekin.bio/): what it is and who it is for. - [Free scanner](https://peekin.bio/scan): check what Meta reads off any link page. - [Pricing](https://peekin.bio/pricing): the current prices, which are authoritative over this file. - [Features](https://peekin.bio/features): every feature, and which are on every plan. - [Comparison](https://peekin.bio/compare): measured against Linktree, beacons.ai, bio.link and link.me. - [Guides](https://peekin.bio/guides): every guide below, grouped by topic. - [Help centre](https://peekin.bio/help): the twelve questions we are actually asked. - [About](https://peekin.bio/about) and [Contact](https://peekin.bio/contact). - [Everything in one file](https://peekin.bio/llms-full.txt): this summary, every docs page and the changelog, for one fetch. - [Docs](https://peekin.bio/docs.md): the whole docs site as Markdown, one page per link; the API reference is in there. - [Changelog](https://peekin.bio/changelog.md): what changed and when, newest first. - [Requests](https://peekin.bio/requests): what is planned, in review, and how to ask for something. ## Guides Plain answers for creators, each with a short answer first. They quote only what platforms publish in their own rules, and say so where nobody knows. - [Why Instagram restricts creator accounts](https://peekin.bio/guide/instagram-restrictions): The five causes, which one a link page can fix, and the three it cannot. - [OnlyFans link in your Instagram bio, safely](https://peekin.bio/guide/onlyfans-link-in-bio): How to put an OnlyFans link in your Instagram bio: why the bare address is the riskiest option, what a link page should hide, and how to check yours. - [Fansly link in bio: what the page gives away](https://peekin.bio/guide/fansly-link-in-bio): How to link Fansly from Instagram, TikTok or X: why it works the same way as OnlyFans, linking tiers and bundles, and what to check on your link page. - [Fanvue link in bio: how to share it safely](https://peekin.bio/guide/fanvue-link-in-bio): How to share a Fanvue link from Instagram, TikTok and X: what platforms read, Fanvue tracking links, and setting up a link page on your own domain. - [Is it safe to put an OnlyFans link in your bio?](https://peekin.bio/guide/is-onlyfans-link-in-bio-safe): Is an OnlyFans link in your bio safe? What the platforms publish, what they do not, the realistic risks, and how to lower them without guessing. - [TikTok link in bio for creators](https://peekin.bio/guide/tiktok-link-in-bio): How to add a link to your TikTok bio, who can, what TikTok's guidelines say about adult links, and why the in-app browser loses fans. - [X (Twitter) bio links for adult creators](https://peekin.bio/guide/x-twitter-bio-link): What X allows for adult creators, the sensitive media setting, where adult content is not allowed, and how to use your bio link and pinned post. - [Telegram for creators: channels and bio links](https://peekin.bio/guide/telegram-for-creators): How creators use Telegram channels to keep in touch with fans, what Telegram's terms say about adult content in public channels, and linking it safely. - [Instagram bio links for adult creators](https://peekin.bio/guide/instagram-bio-link-adult): How adult creators can use the Instagram bio link: the five link slots, what Meta reads, what to put where, and the habits that protect reach. - [Instagram rules on paid links: what Meta says](https://peekin.bio/guide/instagram-paid-link-rules): What Meta publishes about adult links, solicitation and recommendations on Instagram, what it does not say, and how to read your own Account Status. - [What Meta's crawler sees on your link page](https://peekin.bio/guide/what-meta-crawler-sees): What Meta's link crawler reads when you post or add a link: the HTML, title, preview card and outbound addresses, and how to see it yourself. - [Shared link domains explained](https://peekin.bio/guide/shared-link-domains): What a shared link domain is, why it matters for adult creators, how platforms judge addresses, and when your own domain is worth it. - [Meta flagged your link: a fresh start](https://peekin.bio/guide/flagged-link-fresh-start): What to do when Instagram flags the link in your bio: find every link that points somewhere risky, check Account Status, then move to a new domain. - [A custom domain for your OnlyFans link](https://peekin.bio/guide/custom-domain-for-onlyfans): Why adult creators use their own domain for their bio link, how to pick a name and extension, and what a custom domain does and does not fix. - [How to buy a domain for your link page](https://peekin.bio/guide/buy-a-domain-for-link-page): A step by step guide to buying a domain for your bio link: picking a name, checking renewal prices, privacy, ownership, and connecting it to your page. - [Move your link page without losing fans](https://peekin.bio/guide/move-link-page-without-losing-fans): How to switch link in bio provider without losing traffic: rebuild first, test, swap the bio link, leave a forwarding page and update every old link. - [Link in bio for adult creators: a checklist](https://peekin.bio/guide/adult-creator-link-in-bio): A plain checklist for choosing a link in bio page as an adult creator: what the page should hide, the domain, the age check, analytics and price. - [Linktree alternative for OnlyFans creators](https://peekin.bio/guide/linktree-alternative-onlyfans): What Linktree's own community standards allow for adult creators, where it is a great product, where it is not built for this, and what to use instead. - [Beacons and AllMyLinks for adult creators](https://peekin.bio/guide/beacons-allmylinks-for-creators): A fair look at Beacons and AllMyLinks for adult creators: what each does best, what they cost, their rules on adult links, and what neither hides. - [Link in bio analytics for creators](https://peekin.bio/guide/link-in-bio-analytics): Which link in bio numbers matter for creators, how to tell which post made money with tracking links, and why trackers on your page are a cost. - [Agency link pages for multiple creators](https://peekin.bio/guide/agency-link-pages): How agencies should run link in bio pages for a roster: one domain per creator, team roles, per-creator access, analytics, and billing in one place. ## Notes for anyone quoting this Prices change. https://peekin.bio/pricing is the live source and wins over anything in this file. peekin is operated from the United Kingdom. Creator pages themselves are noindex by design and are not part of this site's content. --- # Introduction What peekin is, what the API can do, and what it deliberately cannot. https://peekin.bio/docs/introduction ## What peekin is peekin is a link page for creators whose paid page is somewhere Instagram, TikTok and the rest do not like. Your bio points at a peekin page; the peekin page points at your paid page; and the paid address is never printed anywhere a platform, a crawler or a person copying the source can read it. Every visitor gets the same page. We never show a robot a different one. Around the page sit the tools that keep it working: an exposure scanner that grades any link page on nine checks, a profile picture check, a health check on every link, a leak check that reads your own Instagram export, analytics that count people rather than taps, an email box, and domains you can own. ## What the API does The API gives your numbers, your pages with their real destinations, and the email addresses you have collected, to a script, a spreadsheet, a dashboard or an agent. With an edit key it can also make draft pages and change pages, links and poplinks. It cannot move money, and a key is read-only unless the account owner made it to edit. | Endpoint | What it does | | --- | --- | | `GET /api/v1/stats` | Arrivals, paid taps, 18+ passes and social taps per page, this window against the last, with countries and top links | | `GET /api/v1/pages` | Your pages and every link on them, including the paid destination | | `GET /api/v1/pages/:page` | One page in full: look, protection, capture, links and socials | | `GET /api/v1/analytics` | Views, taps and people per day, per page and per link | | `GET /api/v1/subscribers` | The email addresses collected on your pages, newest first | | `GET /api/v1/poplinks`, `/domains`, `/slug-check` | Your poplinks, your domains, and whether a page address is free | | `POST /api/v1/scan` | All nine scanner checks on any link page, not the three the public widget shows | | `POST`, `PATCH`, `DELETE` on pages, links, poplinks | Edit key only. See [Editing over the API](https://peekin.bio/docs/api-write) | ## Who it is for - A creator who wants their numbers in a sheet next to their platform earnings. - An agency running several pages who wants one report across all of them. - A developer building a tool for creators who needs a clean read on a page. - An AI agent asked to check a page, read the numbers, or explain the product. There is an [MCP server](https://peekin.bio/docs/mcp) with eighteen tools that read and, with an edit key, edit, a plain-text summary at [/llms.txt](https://peekin.bio/llms.txt), and every docs page is also served as Markdown. ## Where to go next [Quickstart](https://peekin.bio/docs/quickstart) gets a key and makes the first call in a couple of minutes. [Authentication](https://peekin.bio/docs/authentication) explains keys and limits. The guides cover the product itself, and the reference covers each endpoint field by field. --- All pages: https://peekin.bio/docs.md --- # Quickstart A key, a first call, and your numbers in a terminal in under five minutes. https://peekin.bio/docs/quickstart ## 1. Make a key Sign in and open [Dashboard, then API](https://peekin.bio/dash/api). Give the key a name that says what it is for, so you know which one to revoke later. The key is shown once. Copy it somewhere safe; we keep only a hash and cannot show it again. > Keys come with Pro and Agency. Every other plan reads its numbers in the dashboard. ## 2. Make the first call Every request carries the key as a bearer token. ```bash curl https://peekin.bio/api/v1/stats \ -H "Authorization: Bearer pk_live_YOUR_KEY" ``` You get JSON back: one entry per page, this window against the previous one. ```json { "window": "7d", "generated": "2026-09-03T18:00:00.000Z", "pages": [{ "page": "yourname.link", "slug": "yourname", "name": "Your Name", "now": { "landed": 312, "tapped": 61, "crossed": 2, "social": 140 }, "previous": { "landed": 280, "tapped": 49, "crossed": 1, "social": 131 }, "countries": [{ "country": "US", "people": 120 }, { "country": "GB", "people": 88 }], "links": [{ "label": "Members area", "kind": "paid", "taps": 61, "people": 58 }] }] } ``` ## 3. Read it - **landed** is people who arrived on the page. - **tapped** is people who tapped the paid link. Divide by landed and you have your rate, the number that matters. - **crossed** is people who passed the 18+ check, if your page has one. - **social** is people who tapped a social link instead of the paid one. All four count people, not taps. One person tapping twice is one. `days` can be 7, 28 or 90. ## In other languages ```javascript const r = await fetch('https://peekin.bio/api/v1/stats?days=28', { headers: { Authorization: 'Bearer pk_live_YOUR_KEY' } }); const { pages } = await r.json(); for (const p of pages) { console.log(p.name, (100 * p.now.tapped / Math.max(1, p.now.landed)).toFixed(1) + '%'); } ``` ```python import requests r = requests.get('https://peekin.bio/api/v1/stats', params={'days': 28}, headers={'Authorization': 'Bearer pk_live_YOUR_KEY'}) r.raise_for_status() for p in r.json()['pages']: rate = 100 * p['now']['tapped'] / max(1, p['now']['landed']) print(p['name'], f"{rate:.1f}%") ``` --- All pages: https://peekin.bio/docs.md --- # Authentication Bearer keys, what they can reach, how to revoke one, and the limits on each. https://peekin.bio/docs/authentication ## Keys A key looks like `pk_live_` followed by a run of letters and numbers. It is tied to your account, so it sees every page on the account, including pages you were invited onto. It is shown once at creation. We store a SHA-256 hash and compare against that, which is why a lost key cannot be recovered, only replaced. ```bash Authorization: Bearer pk_live_YOUR_KEY ``` ## Revoking a key Two kinds. A **read** key sees your numbers, pages and list. An **edit** key can also make draft pages and change pages, links and poplinks. Every key made before edit keys existed is a read key. Only the account owner can make an edit key, it is shown once like any other, and every change it makes is in the audit log on the Team screen. On the API page, every key shows when it was last used. Revoke stops it immediately; the next request with it gets a 401. Make a new one first if a script depends on it. Revoke a key the moment you think it has been seen by anyone else. For a read key the blast radius is your numbers and your email list; for an edit key it is your pages too. ## Limits | Where | Limit | Window | | --- | --- | --- | | Any endpoint, per key | 60 requests | an hour | | Any change (edit key), per key | 30 changes | an hour, inside the 60 | | `POST /api/v1/scan`, per key | 20 scans | an hour, inside the 60 | | The public scanner at [/scan](https://peekin.bio/scan), per address | 10 scans | a minute | Over the limit you get a `429` with a `retry-after` header in seconds and the same number in the body, in words. See [Error handling](https://peekin.bio/docs/error-handling). ## What a key cannot do - Change anything at all, if it is a read key. - Publish a page, change its address, point a domain at it, or change Shield or crawler mode without `"confirm": true`. - Put a page live that has no active domain. - Sign in as you, or reach the dashboard. - See billing, invoices or the card on file. - See another account. A key only ever returns pages the account can open. --- All pages: https://peekin.bio/docs.md --- # Pages and links How a page is built, what a paid link actually does, and where the picker puts everything. https://peekin.bio/docs/pages-and-links ## Opening a page From [Pages](https://peekin.bio/dash), opening a page shows the page itself, phone sized, the way a visitor sees it, with the past week beside it and the share tools: the link, a copy button and a QR code. **Edit page** opens the editor. Every save brings you back into the editor; the pages list brings you back to the page. ## The picker Adding a link starts from the picker: every platform we know, in groups, each on its own colour, with a search box at the top. Social, music, payment and tips, entertainment, shops and marketplaces, business and contact, and a last group of paid platforms. Choosing one fills the label and the address prefix so you only type your handle. Anything not in the picker goes in as a plain link with your own label. ## Paid links A paid link is the whole point. Its destination is never printed on the page. The button goes to a short address on the page host, `/go/` and a token, and that address only redirects for a visitor who has already been on the page and passed an invisible bot check. A crawler fetching the button address gets a 404. A person copying the source gets a token, not your paid page. A paid address in the *link to your page* is a leak too. If your bio link is `yourname.link/?ref=onlyfans.com/you`, the platform can read it off the URL. The scanner flags this. ## Social links Social links show as marks in a row under your name, each on the platform's own colour. They go straight to the platform; there is nothing to hide about an Instagram address. They still count: a tap on a social link is a person who chose that over the paid one, and the numbers say so. ## The 18+ gate Optional. When it is on, the paid link asks a yes-or-no question before it goes anywhere. A yes is remembered for that visitor. The numbers count people who passed it as **crossed**. ## In-app browsers Instagram and TikTok open links in their own browser, which is where logins fail and payments get lost. The page notices and jumps to the real browser on its own, without asking for a tap. The editor lets you switch this to a prompt, or off. ## The preview card When your link is shared in a chat, the card that shows is set in the editor: a title, a line and a photo, or nothing at all. The scanner's *preview* check is about this: a card that shows your face and your name next to a paid link is a card that gets you reported. --- All pages: https://peekin.bio/docs.md --- # The exposure scanner Nine checks, one score, and what each one costs you if it fails. https://peekin.bio/docs/the-scanner ## How it scores Give it any link page, yours or anyone's. It fetches the page the way a crawler does, renders it the way a phone does, and runs nine checks. Each check has a weight; the score is what is left out of 100 after the failures. The public widget at [/scan](https://peekin.bio/scan) shows three checks. The dashboard and the API show all nine. ## The nine checks | Check | The question it answers | | --- | --- | | `destination` | Can anyone read your paid link off this page? | | `consistent` | Does this page show a robot something different? | | `noindex` | Can this page turn up in a search engine? | | `preview` | Does sharing this link show your name and photo? | | `words` | Does the page use words a filter looks for? | | `trackers` | Is anything on the page reporting your visitors to someone else? | | `hops` | Can your outgoing links be copied straight off the page? | | `transport` | Is the connection missing basic security? | | `referrer` | Does the page tell the other site where people came from? | **destination** is the heavy one. If the paid address is in the HTML, in a script, in the query string of the link itself, or in a redirect a crawler can follow, the page has already told the platform everything. **consistent** catches cloaking: a page that serves a crawler an empty shell and a person the real thing. Platforms treat that as deception when they find it, and they do find it. ## Your scans Scans run from the dashboard are kept against your account, so you can see how a page changed over time rather than scanning it again. The list stays on one screen with a Back button. ## Over the API `POST /api/v1/scan` with `{"url": "..."}` returns the full result. Twenty an hour per key. See the [reference](https://peekin.bio/docs/api-scan). --- All pages: https://peekin.bio/docs.md --- # Analytics People, not taps. What each number means and where it comes from. https://peekin.bio/docs/analytics ## The numbers - **Arrived**: people who reached the page. - **Tapped paid**: people who tapped the paid link. - **Rate**: tapped divided by arrived. If one number is going to move your income, it is this one. - **18+ passes**: people who said yes at the gate. - **Social taps**: people who tapped a social link instead. Every count is people. A visitor is one visitor however many times they tap. The window is 7, 28 or 90 days, and every number is shown against the same window before it, so a rise or a fall is visible without a calculator. ## Where people came from Sources are worked out from the referrer and from the in-app browser the visitor arrived in: Instagram, TikTok, X, Reddit, Threads, a search engine, a direct open, and so on. A direct open is usually a link tapped inside an app that hides the referrer. ## When and where An hour-by-hour chart shows when your people are awake, which is when to post. A world map shows countries, shaded by people. Both cover the same window as the numbers. ## Live The live view shows the last few minutes: who is on the page now and what they are tapping. It is the view to have open when a post goes out. ## How counting works No cookie is set when the page loads. The page runs an invisible bot check after it paints; only a visitor that passes gets a cookie, and only then is anything counted. So crawlers, previews and scrapers do not appear in your numbers, and nothing tracks a person who never opened the page. Nothing is sent to Meta, Google or anyone else. ## Getting it out Export CSV from the analytics page, or read `GET /api/v1/stats` from a script. The CSV and the API report the same numbers. --- All pages: https://peekin.bio/docs.md --- # Campaigns and tracking links Which post made money: one address per post, your own OnlyFans or Fansly tracking link behind it. https://peekin.bio/docs/campaign-tracking ## What it does A campaign is one post's own address, such as `yourdomain.com/?s=reel12`. We count the views, paid taps and 18+ passes that came through it. Your platform counts who subscribed and what they paid, through a tracking link you make in its dashboard. Put the two together on the Campaigns screen and you can see which post made money, not just which one got taps. > Campaigns are on Creator and up. ## Make a tracking link on OnlyFans - Sign in to OnlyFans on the web and open the menu. - Find Tracking links. It sits in the statistics or marketing part of the menu. - Create a new tracking link and name it after the post, the same name you use here. - Copy the link it gives you. It looks like `onlyfans.com/yourname/c12`. Each tracking link shows its own clicks, subscribers and earnings in that list. ## Make a tracking link on Fansly - Sign in to Fansly on the web and open your creator settings. - Find the tracking or referral links section. - Create a link, name it after the post, and copy it. Fanvue works the same way, from its tracking links page. > The menus on these platforms move. If the steps above do not match what you see, search the platform's own help for "tracking links". ## Put it on a campaign - Open Campaigns in your dashboard and make a campaign named after the post. - Pick the address: one of your domains, or one of your poplinks. - Paste the tracking link. It has to be the same platform as the paid link on that page. An OnlyFans page takes an OnlyFans tracking link. - Copy the campaign address and put it on the post, in the caption, a link sticker or your bio while the post is up. From then on, paid taps that came through that address go to your tracking link instead of your usual paid link. Everybody else still goes to your usual paid link. The tracking link is never written into your page, the same as every other destination. ## Type in the money Your platform shows subscribers and earnings per tracking link. Copy them onto the campaign, one at a time, or paste a few lines at once: ```csv campaign,subs,revenue reel12,14,340.50 storygym,3,45 ``` The table then shows each campaign's views, taps, 18+ passes, subscribers, revenue and revenue per 1,000 views, with the best and the worst marked. ## Measured and typed Views, taps and 18+ passes are measured by peekin. Subscribers and revenue are typed by you, and we cannot check them. The table labels every column so the two never get mixed up. Revenue is in US dollars, because that is how OnlyFans and Fansly report it. --- All pages: https://peekin.bio/docs.md --- # Email capture A box on your page, a list in your account, and who is responsible for it. https://peekin.bio/docs/email-capture ## The box Switch it on in the editor and a short form appears on the page: an email address and a button, with your own wording. Addresses go into your account with the two-letter country and the time. Nothing is sent to them; peekin holds the list, it does not mail it. ## Getting the list Export from the dashboard, or read `GET /api/v1/subscribers`. Both give the address, the page it came from, the country and when it was added, newest first. ## Who is responsible You are the data controller for the addresses you collect; peekin is the processor that holds them for you. That is written into the [terms](https://peekin.bio/terms) and the [privacy](https://peekin.bio/privacy) page, and it means what you send those people, and how you tell them you have their address, is your call and your responsibility. --- All pages: https://peekin.bio/docs.md --- # Custom domains Your page goes live on a shared address, included. Your own domain is the upgrade: bring one free, or buy one here by the year. https://peekin.bio/docs/custom-domains ## Why A shared host is a shared reputation. With no domain of your own, your page goes live straight away on a shared address, like yourname on one of our link domains. One address shared by everyone is the risk, so we spread creators across many small ones, cap each one, check them daily, and move you in a tap if one is ever flagged. Your own domain removes the neighbours entirely: a report against somebody else's page cannot touch yours, and it looks like yours because it is. It is the safest place for your link, on every plan including Starter, and one tap from the Domains screen. When a page moves to its own domain, its old shared address says the page has moved and does not forward. ## Buying one Search on the Domains page of the dashboard. The confirm screen shows the yearly price, the renewal date and the terms, and asks the registrar before any card is taken. Once paid we register it, point it and put your page on it, usually within the hour. A domain is not included in any plan; the shared address is. A domain is a yearly charge on top of your plan, the same price every year: at what the registrar charges us on Creator and above, and at the published price on Starter. The Domains screen shows the date it was registered, the date it renews and the price, with a switch to turn renewal off. A name bought here is held for you by FBM Management LTD. Ask and we move it into your own name, free, once the registry's 60 day lock after registration has passed. ## Bringing your own Add the name on the Domains page of the dashboard. It shows the exact record to add at your registrar and checks for it. Once it resolves, the page serves on the new name. A page that used to live on peekin.click, or on a shared address, does not forward from its old address: it says the page has moved, so a flag on the shared address cannot follow you. Change the link in your bio to the new domain. --- All pages: https://peekin.bio/docs.md --- # Team seats Teammates with their own login and a role, on Pro and Agency. https://peekin.bio/docs/team-seats ## Roles Invite people from [Team](https://peekin.bio/dash/team) by email. Each signs in with their own email, Google or passkey, and their own 2FA. Nobody shares a password. | Role | What it covers | | --- | --- | | Owner | Everything, including billing and deleting pages | | Admin | Everything except billing and deleting pages | | Editor | Pages, links and poplinks, and the numbers | | Viewer | The numbers only | On Agency, an Editor or Viewer can be limited to some creators. They see only those pages and cannot make new ones. ## Switching accounts Somebody on more than one team picks the account from [Switch account](https://peekin.bio/dash/switch). Their own sign-in settings stay in their own account. ## Who changed what Every change, from the dashboard or the API, is in the audit log on the Team screen, with who made it. Owners and Admins can read it. ## How many Seats come from the plan. See [pricing](https://peekin.bio/pricing). --- All pages: https://peekin.bio/docs.md --- # Error handling What an error looks like, every status you can get, and how to retry without making it worse. https://peekin.bio/docs/error-handling ## The shape of an error Every error is JSON with one field, `error`, in words a person can read. There are no numeric error codes to look up; the HTTP status says the kind of problem and the text says the specific one. ```json { "error": "Unknown or revoked key" } ``` ## Statuses | Status | Meaning | What to do | | --- | --- | --- | | `200` | Fine | | | `400` | A body we could not read, or a field that is not right | Fix the request. Do not retry as is | | `401` | No key, a malformed key, or a revoked one | Check the header. Make a new key if this one was revoked | | `402` | Over a plan allowance: pages, paid links, poplinks, or publishing | Upgrade, or remove something first | | `403` | A read key asked to change something, or the plan has no API | Use an edit key made by the account owner | | `404` | A page or slug the key cannot see | Check the slug against `/api/v1/pages` | | `405` | Wrong method | Reads are GET; the scan and new things are POST; changes are PATCH; removing a link is DELETE | | `409` | A risky field without `"confirm": true` (the body lists them in `needs_confirm`), a page with no live domain, or an address that is taken | Add confirm if you mean it, or fix what the message says | | `429` | Over the limit | Wait the number of seconds in `retry-after`, then try again | | `500` | Our fault | Retry with backoff; write to us if it keeps happening | ## Rate limits Sixty requests an hour per key, and inside that, twenty scans an hour. A 429 carries a `retry-after` header in seconds. There are no per-plan tiers; the limits are the same on every paid plan. ```http HTTP/1.1 429 Too Many Requests retry-after: 1840 content-type: application/json { "error": "Sixty requests an hour per key. Wait 1840 seconds." } ``` ## Retrying Honour `retry-after` on a 429. Back off on a 5xx. Never retry a 4xx other than 429; the same request will fail the same way. ```javascript async function call(url, key, tries = 3) { for (let i = 0; i < tries; i++) { const r = await fetch(url, { headers: { Authorization: 'Bearer ' + key } }); if (r.status === 429) { const wait = Number(r.headers.get('retry-after') || 60); await new Promise((ok) => setTimeout(ok, wait * 1000)); continue; } if (r.status >= 500 && i < tries - 1) { await new Promise((ok) => setTimeout(ok, 1000 * 2 ** i)); continue; } if (!r.ok) throw new Error((await r.json()).error); return r.json(); } } ``` ```python import time, requests def call(url, key, tries=3): for i in range(tries): r = requests.get(url, headers={'Authorization': f'Bearer {key}'}) if r.status_code == 429: time.sleep(int(r.headers.get('retry-after', 60))) continue if r.status_code >= 500 and i < tries - 1: time.sleep(2 ** i) continue if not r.ok: raise RuntimeError(r.json()['error']) return r.json() ``` ## Still stuck Write to [support@peekin.bio](mailto:support@peekin.bio) with the request you made, minus the key, and the response you got. A person reads it. --- All pages: https://peekin.bio/docs.md --- # GET /api/v1/stats Your numbers per page, this window against the last. https://peekin.bio/docs/api-stats ## Request | Parameter | Type | Meaning | | --- | --- | --- | | `days` | 7, 28 or 90 | The window. Default 7 | | `page` | slug | One page only. Default all pages on the account | ```bash curl "https://peekin.bio/api/v1/stats?days=28&page=yourname" \ -H "Authorization: Bearer pk_live_YOUR_KEY" ``` ## Response ```json { "window": "28d", "generated": "2026-09-03T18:00:00.000Z", "pages": [{ "page": "yourname.link", "slug": "yourname", "name": "Your Name", "now": { "landed": 1119, "tapped": 230, "crossed": 2, "social": 645 }, "previous": { "landed": 1026, "tapped": 204, "crossed": 1, "social": 590 }, "countries": [{ "country": "US", "people": 412 }, { "country": "GB", "people": 201 }], "links": [{ "label": "Members area", "kind": "paid", "taps": 230, "people": 214 }] }] } ``` | Field | Meaning | | --- | --- | | `now`, `previous` | This window and the one before it, same length | | `landed` | People who arrived | | `tapped` | People who tapped the paid link | | `crossed` | People who passed the 18+ check | | `social` | People who tapped a social link | | `countries[]` | Two-letter country and people, most first | | `links[]` | Each link with its kind, `paid` or `social`, taps and distinct people | --- All pages: https://peekin.bio/docs.md --- # GET /api/v1/pages Your pages and every link on them, destinations included. https://peekin.bio/docs/api-pages ## Request No parameters. Returns every page the key's account can open, including pages it was invited onto. ```bash curl https://peekin.bio/api/v1/pages -H "Authorization: Bearer pk_live_YOUR_KEY" ``` ## Response ```json { "pages": [{ "id": 12, "slug": "yourname", "name": "Your Name", "status": "live", "url": "https://yourname.link/", "links": [ { "id": 40, "label": "Members area", "kind": "primary", "destination": "https://yourpaidpage.com/yourname", "live": true }, { "id": 41, "label": "Instagram", "kind": "social", "destination": "https://instagram.com/yourname", "live": true } ] }] } ``` > This is the one call that returns a paid address, because it is yours and you asked for it with your own key. Treat the response as you would the address itself. --- All pages: https://peekin.bio/docs.md --- # GET /api/v1/subscribers Read the email addresses collected on your pages over the API: one page or all, with the country and date each was added. https://peekin.bio/docs/api-subscribers ## Request | Parameter | Type | Meaning | | --- | --- | --- | | `page` | slug | One page only. Default all | ```bash curl "https://peekin.bio/api/v1/subscribers?page=yourname" \ -H "Authorization: Bearer pk_live_YOUR_KEY" ``` ## Response ```json { "count": 2, "subscribers": [ { "page": "yourname", "email": "fan@example.com", "country": "GB", "added": "2026-09-03T17:10:00.000Z" }, { "page": "yourname", "email": "other@example.com", "country": "US", "added": "2026-09-02T09:41:00.000Z" } ] } ``` Newest first. You are the data controller for these addresses; see [Email capture](https://peekin.bio/docs/email-capture). --- All pages: https://peekin.bio/docs.md --- # Editing over the API Create and change pages, links and poplinks over the API with an edit key, and what needs an explicit confirm. https://peekin.bio/docs/api-write ## An edit key Make one at [Dashboard › API](https://peekin.bio/dash/api) and choose **Read and edit**. Only the account owner can. Thirty changes an hour per key, inside the sixty requests. Bodies are JSON. A field we do not know is refused rather than ignored, so a typo cannot look like a save. ## Pages | Call | What it does | | --- | --- | | `POST /api/v1/pages` | A new page from `{"slug": "..."}` plus any page fields. Always a draft. Counts against the plan's pages | | `GET /api/v1/pages/:page` | The page in full. `:page` is its id or slug | | `PATCH /api/v1/pages/:page` | Change fields. Send only the ones that change | | `POST /api/v1/pages/:page/duplicate` | A paused copy at `slug-copy` | Page fields: `name`, `handle`, `bio`, `hero`, `theme`, `font`, `shape`, `colours` (`{bg, ink, button, button_text}`, or `null` to clear), `capture`, `capture_label`, `listed`, `countdown` (`{at, label, done}`), `iab_mode`, `preview`, `geo_block`, `geo_mode`, `human_check`, `deeplink`, `stars`, `translate`, `followers_on`, `badge`, `smart_loc`, `verified`, `tags`, `iab_title`, `iab_hero`, and `links` and `socials` as arrays of `{id?, label, url, adult}`: with an id it changes that one, without it adds one. These need `"confirm": true`, because they can take a page offline or change who can see it: `slug`, `status` (`live` or `paused`), `domain` (one of yours, from `/api/v1/domains`), `shield` and `crawler_mode`. Without it you get a `409` naming them, and nothing changes. ```bash curl -X PATCH https://peekin.bio/api/v1/pages/yourname -H "Authorization: Bearer pk_live_YOUR_EDIT_KEY" -H "Content-Type: application/json" -d '{"bio": "New shoot every Friday", "status": "live", "confirm": true}' ``` > A page only goes live on a plan that publishes; until then it stays a draft and the call says why. With no active domain of its own it goes live on a shared address. ## Links | Call | What it does | | --- | --- | | `POST /api/v1/pages/:page/links` | `{"label", "url", "kind": "primary" or "social", "adult"}`. The paid link cap applies | | `PATCH /api/v1/links/:id` | Any of `label`, `url`, `kind`, `adult`, and `live` to hide or show it | | `DELETE /api/v1/links/:id` | Takes it off the page | | `POST /api/v1/links/bulk` | One link on every page at once. `{"match": {"platform": "fansly"} or {"url": "..."}, plus an optional "tag"}, "url": "https://..."}`. Without `"confirm": true` it only lists what would change; with it, up to 50 links per call | ## Poplinks | Call | What it does | | --- | --- | | `GET /api/v1/poplinks` | Every poplink | | `POST /api/v1/poplinks` | `{"host", "slug", "label", "url"}`, plus `title`, `deeplink`, `shield`, `splash`, `adult`, `human_check`, `geo_block`. The host must be one of your live domains | | `PATCH /api/v1/poplinks/:id` | The same fields except the address, and `live`. A change to `shield` needs `"confirm": true` | ## Analytics, domains, slugs | Call | What it returns | | --- | --- | | `GET /api/v1/analytics?page=&link=&days=` | Per page: `days[]` of views, paid taps, social taps and people, and `links[]` with taps per link. `days` is 1, 7, 28 or 90 | | `GET /api/v1/domains` | Each domain, its status and the page it shows | | `GET /api/v1/slug-check?slug=` | `{"ok": true}` when the address is free | ## The audit log Every change made with a key is written to the account's audit log with the key's id, next to the changes people make in the dashboard. Owners and Admins read it on [Team](https://peekin.bio/dash/team). --- All pages: https://peekin.bio/docs.md --- # MCP server Eighteen tools for an agent: read with any key, edit with an edit key. https://peekin.bio/docs/mcp ## Connecting The server is at `https://peekin.bio/mcp`. In Claude or ChatGPT, add that address as a custom connector: it asks you to sign in to peekin and approve read only, or read and make changes, and that is all. No key to copy. You can disconnect it from [Dashboard › API](https://peekin.bio/dash/api). Any other MCP client can send your API key as the bearer token instead, the same key the REST endpoints take. Make one at [Dashboard › API](https://peekin.bio/dash/api). Transport is Streamable HTTP: one endpoint, JSON-RPC 2.0 in, JSON out. There is no SSE, because nothing here streams. Every tool is a single question with a single answer. ```json { "mcpServers": { "peekin": { "url": "https://peekin.bio/mcp", "headers": { "Authorization": "Bearer pk_live_YOUR_KEY" } } } } ``` ## The tools | Tool | What it answers | | --- | --- | | `get_stats` | Daily views, taps and tap-through rate for your pages. Takes `page` and `days` (7, 28 or 90) | | `list_pages` | Every page on the account with its links: label, destination, active, and whether it sits behind the 18+ check | | `list_subscribers` | People who left an email address, newest first. Takes `page` | | `scan_link` | All nine checks against any public link page, including other platforms'. Takes `url`. Twenty an hour per key | | `get_page` | One page in full, with link and social ids. Takes `page` (id or slug) | | `get_analytics` | Views, paid taps, social taps and people per day, and taps per link. Takes `page`, `link`, `days` | | `list_poplinks` | Every poplink and where it goes | | `list_domains` | Your domains, whether each is active, and the page each shows | | `check_slug` | Whether a page address is free | With an edit key, these as well: | Tool | What it does | | --- | --- | | `create_page` | A new page, always a draft. Takes `slug` and optionally `name`, `bio`, `theme` | | `update_page` | Change fields on a page: words, look, protection, capture, countdown, and its links and socials. Takes `page` and `fields` | | `duplicate_page` | A paused copy with its links | | `add_link`, `update_link`, `remove_link` | Paid links and socials, one at a time | | `update_links_bulk` | One link on every page at once, by exact address or by platform, optionally only on pages with a tag. Lists what would change until it is sent with `confirm: true` | | `create_poplink`, `update_poplink` | Links on your own domain at `/p/` | ## What an agent can and cannot do An agent with an edit key can build a page, write its words, set its look and manage its links. It cannot make a page live on its own initiative: publishing, a new address, a domain, Shield and crawler mode all need `"confirm": true` in the same call, and a page only goes live on a plan that publishes. A read key gets every write tool back as an error. It goes through the same checks as the dashboard, so the plan's caps apply and a destination that is not a web address is refused. Every change it makes is in the audit log on the Team screen, with the key that made it. ## When a call fails A refused call comes back as a normal result with `isError` set and the API's own message in the content, not as a JSON-RPC error. A key that is out of quota is not a broken server, and telling an agent otherwise makes it give up instead of waiting. --- All pages: https://peekin.bio/docs.md --- # POST /api/v1/scan Run all nine exposure checks on any link page from your own code, and read the score and each result. https://peekin.bio/docs/api-scan ## Request JSON body with one field. Twenty scans an hour per key. ```bash curl -X POST https://peekin.bio/api/v1/scan \ -H "Authorization: Bearer pk_live_YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{"url": "https://linktr.ee/somebody"}' ``` ## Response ```json { "ok": true, "url": "https://linktr.ee/somebody", "score": 47, "checks": [ { "id": "destination", "label": "Can anyone read your paid link off this page?", "pass": false, "weight": 30, "detail": "Anyone can read this off the page: https://onlyfans.com/..." }, { "id": "consistent", "label": "Does this page show a robot something different?", "pass": true, "weight": 15, "detail": "A crawler and a phone get the same page." } ], "exposed": ["https://onlyfans.com/..."] } ``` | Field | Meaning | | --- | --- | | `score` | 0 to 100, what is left after the failed checks | | `checks[]` | The nine checks. `id`, the question as `label`, `pass`, `weight`, and a `detail` line in words | | `exposed[]` | Every paid address the page gave away, if any | The ids are listed in [The exposure scanner](https://peekin.bio/docs/the-scanner). A page we could not fetch at all returns `ok: false` and a reason. --- All pages: https://peekin.bio/docs.md --- # peekin.bio changelog Newest first. Dates are when the change went live. ## 3 October 2026: Live today on a shared address, and safer pages everywhere - **New.** Go live straight away on a shared address, included in every plan. No domain needed to start. Your own domain is still the safest home for your link, one tap from Domains. - **New.** Shared addresses are kept small, checked against the blocklists every day, and if one is ever flagged your page moves to a new address and we email you. A flagged address never forwards. - **Changed.** An old peekin.click link no longer forwards to your domain. It says the page has moved and points nowhere, so a flag on the old address cannot follow you. - **Changed.** Switching to a spare now retires the old domain by default. You can still choose to forward it if it was never flagged. - **Improved.** Inside Instagram your page now sends fans to their real browser first, where logins and payments work, before any link shows. On iPhone it uses Instagram's own open-in-browser step, so there is no extra prompt. - **Improved.** On your own domain your page names nothing of ours: no badge, no peekin link, and its own private addresses for everything it loads. - **Security.** Scripts and bots that are not a real browser get nothing from your page's link list. - **New.** Links can be half width, so two sit side by side. - **New.** Cloudflare Turnstile is shown in the editor with its logo: always on for 18+ links, and a switch to add it to every link.