linkpop

Use the Linkpop API to create short links, manage bio link pages, and view click analytics. Covers auth, URL shortening, bio links, profile updates, and analytics at linkpop.space.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "linkpop" with this command: npx skills add manojk0303/linkpop-skills/manojk0303-linkpop-skills-linkpop

Linkpop Skill

Use this skill when the user wants to shorten URLs, manage a bio link page, or view click analytics using Linkpop (linkpop.space).


Base URL

https://linkpop.space

Authentication

All protected endpoints require a Bearer token in the Authorization header:

Authorization: Bearer YOUR_TOKEN

You get the token from the signup or login response. Store it immediately — you'll need it for every subsequent request.


IMPORTANT: Response Field Guide

Several endpoints return duplicate fields with different names for compatibility. Here is what to use:

EndpointField confusionWhat to use
Signup / Logintoken and api_token are identicalUse token
Create/Update bio linkResponse has both link and bioLink (identical objects)Use either
List bio linksResponse has both links and bioLinks (identical arrays)Use either
Create short linkUse short_url from the response, not url.short_codeshort_url is the full ready-to-share URL
Analytics overviewData is nested under insights keyAccess response.insights.totalClicks etc.

Endpoint Reference

1. Create Account

POST /api/auth/signup

Request body:

{
  "email": "user@example.com",
  "password": "atleast8chars",
  "username": "myusername"
}

Username rules: 3–30 chars, letters/numbers/hyphens/underscores only. Cannot be reserved words like admin, api, dashboard, s, analytics, linktree, bitly.

Response (HTTP 201):

{
  "success": true,
  "user": {
    "id": "uuid",
    "email": "user@example.com",
    "username": "myusername",
    "display_name": null,
    "bio": null,
    "avatar_url": null,
    "theme": "light",
    "created_at": "...",
    "updated_at": "..."
  },
  "token": "your-session-token",
  "api_token": "your-session-token",
  "profile_url": "https://myusername.linkpop.space"
}

token and api_token are identical — use token. profile_url is the user's public bio page.


2. Login

POST /api/auth/login

Request body:

{
  "email": "user@example.com",
  "password": "yourpassword"
}

Response (HTTP 200):

{
  "success": true,
  "user": { "id": "...", "username": "myusername", "email": "..." },
  "token": "your-session-token",
  "api_token": "your-session-token",
  "profile_url": "https://myusername.linkpop.space"
}

3. Get Current User

GET /api/auth/me
Requires auth.

Response:

{
  "user": {
    "id": "uuid",
    "email": "...",
    "username": "myusername",
    "display_name": "My Name",
    "bio": "My bio",
    "avatar_url": null,
    "custom_domain": null,
    "root_domain_mode": "bio",
    "root_domain_redirect_url": null,
    "use_domain_for_shortlinks": true
  }
}

4. Create Short Link

POST /api/urls
Requires auth.

Request body:

{
  "originalUrl": "https://example.com/very/long/url",
  "customCode": "my-link",
  "title": "My Link Title"
}
  • originalUrl — required, must include https:// or http://
  • customCode — optional, 3–100 chars, letters/numbers/hyphens/underscores. Short codes are unique per user (not globally), so two different users can have the same code.
  • title — optional, max 255 chars

Response (HTTP 201):

{
  "success": true,
  "url": {
    "id": "uuid",
    "short_code": "my-link",
    "original_url": "https://example.com/very/long/url",
    "title": "My Link Title",
    "clicks": 0,
    "is_active": true,
    "custom_code": true,
    "user_id": "uuid",
    "created_at": "...",
    "updated_at": "..."
  },
  "short_url": "https://myusername.linkpop.space/my-link"
}

Always use short_url — it is the complete, ready-to-share URL. It respects custom domain settings automatically. Do not construct URLs manually from url.short_code.

If customCode is already taken by this user, the API returns a suggestedCode alternative in the error response:

{ "error": "You already have a link with this code. Try: my-link123", "suggestedCode": "my-link123" }

5. List Short Links

GET /api/urls
Requires auth.

Response:

{
  "urls": [
    {
      "id": "uuid",
      "short_code": "my-link",
      "original_url": "https://example.com",
      "title": "My Link Title",
      "clicks": 42,
      "is_active": true,
      "created_at": "..."
    }
  ]
}

6. Update Short Link

PATCH /api/urls/{link_id}
Requires auth.

Request body (all fields optional):

{
  "originalUrl": "https://new-url.com",
  "title": "New Title",
  "shortCode": "new-code"
}

Note: all fields are camelCase (originalUrl, shortCode), not snake_case.

Response:

{
  "success": true,
  "url": { "id": "...", "short_code": "new-code", "original_url": "...", "title": "...", "clicks": 42 },
  "short_url": "https://myusername.linkpop.space/new-code"
}

7. Delete Short Link

DELETE /api/urls/{link_id}
Requires auth.

Response:

{ "success": true, "message": "URL deleted successfully", "deleted": true }

8. Create Bio Link

POST /api/bio-links
Requires auth.

Request body:

{
  "title": "My Instagram",
  "url": "https://instagram.com/myprofile",
  "block_type": "link",
  "is_visible": true
}

block_type options:

  • "link" — standard clickable link (default, recommended)
  • "social" — social media link with auto-detected platform icon (detected from URL hostname)
  • "page" — full markdown page (requires block_data.content and block_data.slug)
  • "accordion" — expandable content (requires block_data.content)
  • "copy-text" — click-to-copy (requires block_data.text)
  • "divider" — visual separator (optional block_data.showTitle)

Response (HTTP 201):

{
  "success": true,
  "link": {
    "id": "uuid",
    "title": "My Instagram",
    "url": "https://instagram.com/myprofile",
    "block_type": "link",
    "is_visible": true,
    "position": 0,
    "user_id": "uuid",
    "created_at": "..."
  },
  "bioLink": { "...same object as link..." }
}

link and bioLink are always identical — use either one.


9. List Bio Links

GET /api/bio-links
Requires auth.

Response:

{
  "success": true,
  "links": [ { "id": "...", "title": "...", "url": "...", "block_type": "link", "is_visible": true, "position": 0 } ],
  "bioLinks": [ "...same array as links..." ],
  "count": 1
}

links and bioLinks are always identical — use either one. count is the total number of bio links.


10. Update Bio Link

PATCH /api/bio-links/{link_id}
Requires auth.

Request body (all optional):

{
  "title": "Updated Title",
  "url": "https://newurl.com",
  "icon": null,
  "isVisible": false,
  "block_data": {}
}

Important: visibility field here is isVisible (camelCase), NOT is_visible. This is different from the create endpoint.

Response:

{
  "success": true,
  "link": { "id": "...", "title": "Updated Title", "url": "...", "is_visible": false },
  "bioLink": { "...same as link..." }
}

11. Delete Bio Link

DELETE /api/bio-links/{link_id}
Requires auth.

Response:

{ "success": true, "message": "Bio link deleted successfully", "deleted": true }

12. Reorder Bio Links

POST /api/bio-links/reorder
Requires auth.

The array order determines display order on the bio page (index 0 = top).

Request body:

{
  "linkIds": ["uuid1", "uuid2", "uuid3"]
}

Response:

{ "success": true, "message": "Bio links reordered successfully", "reordered": true, "count": 3 }

13. Update Profile

PATCH /api/profile
Requires auth.

All fields optional:

{
  "display_name": "My Display Name",
  "bio": "Bio text up to 500 chars",
  "avatar_url": "https://example.com/avatar.png",
  "profile_image_url": "https://example.com/image.png",
  "theme": "dark",
  "background_type": "gradient",
  "background_value": "#ff0000",
  "font_family": "Inter",
  "custom_domain": "mysite.com",
  "use_domain_for_shortlinks": true,
  "root_domain_mode": "bio",
  "root_domain_redirect_url": "https://mysite.com/landing"
}
  • theme: "default" | "dark" | "light"
  • background_type: "solid" | "gradient" | "image"
  • root_domain_mode: "bio" (show profile at root) | "redirect" (redirect root to another URL)
  • If root_domain_mode is "redirect", root_domain_redirect_url must be set and valid
  • Setting custom_domain to "" removes the domain and resets all domain settings
  • Changing custom_domain resets domain_verified to false — you must re-verify

Response:

{ "success": true, "message": "Profile updated successfully", "updated": true }

14. Get Analytics Overview

GET /api/insights
Requires auth. Optional query params: ?startDate=2025-01-01&endDate=2025-01-31

Response — note data is nested under the insights key:

{
  "insights": {
    "totalClicks": 1234,
    "urlClicks": 800,
    "bioLinkClicks": 434,
    "clicksToday": 50,
    "clicksThisWeek": 300,
    "clicksThisMonth": 1000,
    "topUrls": [
      { "id": "uuid", "title": "My Link", "short_code": "my-link", "url": "https://example.com", "clicks": 100 }
    ],
    "topBioLinks": [
      { "id": "uuid", "title": "Instagram", "url": "https://instagram.com/...", "clicks": 50 }
    ],
    "recentClicks": [
      { "id": "uuid", "type": "url", "title": "My Link", "clicked_at": "...", "country": "US", "city": "New York" }
    ],
    "clicksByDay": [
      { "date": "2025-01-29", "clicks": 50, "urlClicks": 30, "bioLinkClicks": 20 }
    ]
  }
}

Access data as response.insights.totalClicks, not response.totalClicks.


15. Get Short Link Analytics

GET /api/insights/shortlinks/{shortlink_id}
Requires auth. Optional query params: ?startDate=...&endDate=...

The {shortlink_id} is the UUID from url.id, not the short code string.

Response:

{
  "link": {
    "id": "uuid",
    "short_code": "my-link",
    "destination_url": "https://example.com",
    "title": "My Link",
    "total_clicks": 500,
    "created_at": "..."
  },
  "clicksByDay": [ { "date": "2025-01-29", "clicks": 50 } ],
  "topCountries": [ { "country": "United States", "clicks": 200 } ],
  "topCities": [ { "city": "New York", "country": "United States", "clicks": 50 } ],
  "topBrowsers": [ { "browser": "Chrome", "version": "120.0", "clicks": 300 } ],
  "topOS": [ { "os": "Windows", "version": "11", "clicks": 250 } ],
  "topReferrers": [ { "platform": "twitter", "referrer": "https://t.co/...", "clicks": 100 } ],
  "deviceTypes": [ { "deviceType": "mobile", "clicks": 300 }, { "deviceType": "desktop", "clicks": 200 } ],
  "summary": {
    "totalClicks": 500,
    "dateRange": { "start": "...", "end": "..." }
  }
}

16. Get Bio Page Analytics

GET /api/insights/pages
Requires auth. Optional query params: ?startDate=...&endDate=...

Returns profile view stats and per-link CTR:

{
  "overview": { "profileViews": 1000, "linkClicks": 200, "ctr": 20.0 },
  "viewsByDay": [ { "date": "2025-01-29", "views": 50 } ],
  "topLinks": [ { "id": "uuid", "title": "Instagram", "url": "...", "clicks": 80, "ctr": 8.0 } ],
  "topCountries": [ { "country": "IN", "views": 400 } ],
  "deviceTypes": [ { "deviceType": "mobile", "views": 600 } ]
}

17. Get Subscription Info

GET /api/subscription
Requires auth.

Response:

{
  "tier": "free",
  "expiresAt": null,
  "limits": {
    "maxLinks": -1,
    "maxUrls": -1,
    "analyticsRetentionDays": 365,
    "customDomain": true,
    "customJS": true,
    "advancedBlocks": true,
    "removeWatermark": true
  }
}

-1 means unlimited. Currently all users (free and pro) get unlimited everything with 365-day analytics retention.


Error Responses

All errors return JSON with an error field:

{ "error": "Human-readable error message" }
HTTP StatusMeaning
400Bad request — invalid input or validation failure
401Unauthorized — missing or invalid token
403Forbidden — feature requires upgrade
404Not found
429Rate limited — check X-RateLimit-Reset header
500Server error
503Database temporarily unavailable — retry

Rate limit headers on every response:

  • X-RateLimit-Limit — max requests per minute
  • X-RateLimit-Remaining — requests left in this window
  • X-RateLimit-Reset — ISO timestamp when the window resets

Rate limits: signup = 20/min, most endpoints = 100/min per user.


Complete Workflow Example

// 1. Sign up
const signup = await fetch('https://linkpop.space/api/auth/signup', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ email: 'me@example.com', password: 'password123', username: 'mybot' })
})
const { token, profile_url } = await signup.json()
// profile_url = "https://mybot.linkpop.space"

const headers = { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }

// 2. Create a short link
const urlRes = await fetch('https://linkpop.space/api/urls', {
  method: 'POST',
  headers,
  body: JSON.stringify({ originalUrl: 'https://example.com/article', customCode: 'article', title: 'My Article' })
})
const { short_url } = await urlRes.json()
// short_url = "https://mybot.linkpop.space/article" — use this directly

// 3. Add a bio link
await fetch('https://linkpop.space/api/bio-links', {
  method: 'POST',
  headers,
  body: JSON.stringify({ title: 'My Article', url: short_url, block_type: 'link' })
})

// 4. Get analytics — remember to access .insights
const analyticsRes = await fetch('https://linkpop.space/api/insights', { headers })
const { insights } = await analyticsRes.json()
console.log(insights.totalClicks, insights.clicksToday)

// 5. Hide a bio link (note: isVisible camelCase, not is_visible)
await fetch(`https://linkpop.space/api/bio-links/${linkId}`, {
  method: 'PATCH',
  headers,
  body: JSON.stringify({ isVisible: false })
})

Gotchas & Common Mistakes

  • Short codes are per-user, not global. Two different users can both have a short code "blog". When routing, the subdomain (username.linkpop.space) determines which user's link is resolved.
  • Always use short_url from create/update responses. Never manually build URLs from short_code.
  • Analytics are under response.insights.*, not at the root of the response.
  • Bio link update uses isVisible (camelCase) — different from the create field is_visible.
  • Short link update uses camelCase: originalUrl, shortCode — not snake_case.
  • Setting customCode to an already-used code returns a suggestedCode in the error — use it.
  • Rate limit 429: wait until X-RateLimit-Reset timestamp before retrying.
  • URLs must include protocol: https://example.com ✓ vs example.com

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

Todoist CLI Skill

Manage tasks and projects in Todoist using the Official Todoist CLI tool (https://github.com/Doist/todoist-cli). Use when user asks about tasks, to-dos, remi...

Registry SourceRecently Updated
Coding

Claw Insights Install

Install and run Claw Insights, a read-only observability dashboard that monitors your OpenClaw agent with zero intrusion — no code changes, no cloud dependen...

Registry SourceRecently Updated
Coding

Wip Release

One-command release pipeline. Bumps version, updates changelog + SKILL.md, publishes to npm + GitHub.

Registry SourceRecently Updated
Coding

You.com Web Search & Research API

Integrate You.com APIs (Research, Search, Contents) into any language using direct HTTP calls — no SDK required. - MANDATORY TRIGGERS: YDC API, You.com API i...

Registry SourceRecently Updated