Lookup User

Look up a user by username, uid, id, or Discord id — optionally with full profile data.

Resolve a haunt.gg user by their username, numeric uid, internal id, or linked discord account id. By default the response is a lean basic payload; opt into additional data (full profile, badges, links, projects, and more) with per-section include flags so you only ever transfer what you need.

GET/api/lookup/user

Users can switch off public API access for their profile in their account security settings. Lookups for those users answer 404 with {"error":"This profile is private."} no matter which type you resolve them by — treat it as "exists, but not available here" rather than retrying with another identifier.

Authorizations

X-API-Keyheaderstringrequired
API key from your dashboard with the lookup:user permission (granted by staff). Treat it like a password.

Query Parameters

typequerystringrequired
How to interpret value. One of username, uid, id, or discord. Username matching is case-insensitive and falls back to aliases; discord resolves the user via their linked Discord account.
valuequerystringrequired
The lookup value: a username, a numeric uid, an internal user id, or a Discord account id (snowflake) — matching the chosen type. 1–64 characters.
showAliasProfilequeryboolean
Resolution modifier (not an include flag). When value resolves through an alias that points to a custom profile, scope all profile-derived data — profile, badges, links, widgets, audios, backgrounds, projects, gallery, sections, and the user's avatarUrl — to that alias profile instead of the user's active one. Only views, aliases, feedback and comments stay user-wide. No-op for direct username/uid/id/discord matches and for aliases without a custom profile. Pass true. Defaults to false.
profilequeryboolean
Include the active profile: display name, description, theme, fonts, resolved asset URLs, and the 22-key settings object. Pass the literal string true. Defaults to false.
badgesqueryboolean
Include the user's badges — both enabled and disabled, each carrying an enabled flag plus its per-profile display overlay (color, recolor, selected tier, order). Filter on enabled client-side if you only want the visible ones. Catalog badges carry ready-to-embed artwork as imageUrl (plus one per entry in tiers); custom badges carry their upload under image or, when the user picked an icon instead, server-resolved markup under iconSvg. The profile-wide badge styling (monochrome, glow, border, scroll, …) lives separately under profile.settings.badgesSettings (request profile=true). Pass true. Defaults to false.
widgetsqueryboolean
Include the active profile's widgets — id, platform, type, identifier and order only. Pass true. Defaults to false.
audiosqueryboolean
Include the active profile's audio tracks. Pass true. Defaults to false.
backgroundsqueryboolean
Include the active profile's backgrounds. Pass true. Defaults to false.
linksqueryboolean
Include the active profile's links. A custom link's glyph is either an uploaded icon or server-resolved Iconify markup in iconSvg — never both. Pass true. Defaults to false.
projectsqueryboolean
Include the active profile's projects. Pass true. Defaults to false.
viewsqueryboolean
Include the user's total view count. It is user-wide, not per profile, so showAliasProfile does not change it. Pass true. Defaults to false.
aliasesqueryboolean
Include the user's aliases. Pass true. Defaults to false.
galleryqueryboolean
Include every gallery image across the profile's Gallery sections (caption, width half/full/quad, and resolved asset URL), in section-then-item order. Pass true. Defaults to false.
sectionsqueryboolean
Include the profile's section layout in render order. Nine types exist: About Me, Projects, Custom Text, Gallery, Audio Player, Games, Anime, Movies and Series, and Music. About-Me sections carry the bio content, skills and widgetIds; Projects sections carry projectIds — resolve those id references against the widgets/projects includes. The four catalog sections carry their imported items inline. Pass true. Defaults to false.
feedbackqueryboolean
Include the user's profile feedback tally (likes and dislikes counts). Votes from banned users are excluded. Pass true. Defaults to false.
commentsqueryboolean
Include the profile's top-level comments as comments.items (newest first, pinned bubble up; capped at 100, replies not expanded — use replyCount), alongside comments.count. Both exclude replies and comments written by banned users, and count is the full total, so it exceeds items.length once a profile passes 100 comments. Pass true. Defaults to false.

Only the sections you opt into are queried and serialized, so the default payload stays small. Include flags are read as exact strings — pass true (e.g. profile=true); any other value is treated as false. The same exact-string rule applies to showAliasProfile.

Discord lookups — resolve a Discord user straight to their haunt.gg account with their snowflake id: ?type=discord&value=1363890885443452989. Returns 404 when no haunt.gg user has that Discord account linked.

Alias profiles — when a user runs multiple aliases with their own custom profiles, add showAliasProfile=true to a username/alias lookup to scope the returned profile, badges, links, widgets, audios, backgrounds, projects, gallery, sections and avatarUrl to the alias's profile rather than the active one — e.g. ?type=username&value=john-music&profile=true&showAliasProfile=true.

Example Request

Request
curl -X GET "https://haunt.gg/api/lookup/user?type=username&value=john&profile=true" \
  -H "X-API-Key: YOUR_API_KEY"

Asset URLs

Every uploaded asset — avatars, banners, cursors, backgrounds, audio and covers, link and project icons, gallery images, fonts, sounds and custom badge art — resolves to https://r2.haunt.gg/…. The only exception is catalog badge artwork (badges[].imageUrl and badges[].tiers[].imageUrl), which is served from https://assets.haunt.gg/badges/haunt/….

One more shape to expect: when a profile is set to mirror its Discord avatar, user.avatarUrl and profile.avatar point at https://haunt.gg/api/discord/avatar/<userId> instead of an uploaded file.

Response

Response
User found. The full payload with every include flag enabled (profile, badges, widgets, audios, backgrounds, links, projects, views, aliases, gallery, sections, feedback, comments all set to true). Without any flags only `error` and the basic `user` object are returned; each flag adds its key, so request just the ones you need to keep the payload small. `profile.settings` is elided here — the next tab shows it in full.
{
  "error": null,
  "user": {
    "id": "clz9k2x0a0000s601f8h3d7q2",
    "uid": 1337,
    "username": "john",
    "name": "John",
    "avatarUrl": "https://r2.haunt.gg/avatar/abc123.png",
    "discordId": "1363890885443452989",
    "lastfmUsername": "john",
    "premium": true,
    "verified": false,
    "imagehost": false,
    "inactive": false,
    "createdAt": "2026-01-15T10:30:00.000Z"
  },
  "profile": {
    "profileId": "clz9k3p1b0001s601a2b4c5d6",
    "name": "john",
    "displayName": "John",
    "description": "just vibing",
    "location": "Berlin",
    "theme": "default",
    "font": "inter",
    "fontUsername": "inter",
    "soundEffect": "none",
    "soundEffectButton": "none",
    "createdAt": "2026-01-15T10:30:00.000Z",
    "updatedAt": "2026-05-09T12:34:56.000Z",
    "avatar": {
      "url": "https://r2.haunt.gg/avatar/abc123.png",
      "mime": "image/png",
      "width": 512,
      "height": 512,
      "duration": null
    },
    "banner": null,
    "cursor": null,
    "cursorPointer": null,
    "fontAsset": null,
    "fontUsernameAsset": null,
    "soundEffectAsset": null,
    "soundEffectButtonAsset": null,
    "metadataFavicon": null,
    "metadataBanner": null,
    "settings": { "…": "22 keys — see the profile.settings tab" }
  },
  "badges": [
    {
      "id": "clz9kb1a0001s601donator00",
      "type": "STANDARD",
      "badgeId": "donator",
      "enabled": true,
      "selectedTier": 2,
      "unlockedTiers": [
        1,
        2
      ],
      "color": null,
      "recolor": false,
      "name": null,
      "order": 0,
      "image": null,
      "imageUrl": "https://assets.haunt.gg/badges/haunt/donator_fortune.png",
      "iconSvg": null,
      "tiers": [
        {
          "tier": 1,
          "threshold": 10,
          "title": "Donator",
          "description": "Donate at least 10 EUR to haunt.gg.",
          "color": "#13a15a",
          "unlocked": true,
          "selected": false,
          "imageUrl": "https://assets.haunt.gg/badges/haunt/donator.png"
        },
        {
          "tier": 2,
          "threshold": 50,
          "title": "Fortune",
          "description": "Donate at least 50 EUR to haunt.gg.",
          "color": "#aa3b3b",
          "unlocked": true,
          "selected": true,
          "imageUrl": "https://assets.haunt.gg/badges/haunt/donator_fortune.png"
        }
      ]
    },
    {
      "id": "clz9kc2b0002s601custom000",
      "type": "CUSTOM",
      "badgeId": null,
      "enabled": false,
      "selectedTier": null,
      "unlockedTiers": [],
      "color": "#ff66cc",
      "recolor": true,
      "name": "Early Bird",
      "order": 1,
      "image": {
        "url": "https://r2.haunt.gg/badge/earlybird.png",
        "mime": "image/png",
        "width": 64,
        "height": 64,
        "duration": null
      },
      "imageUrl": null,
      "iconSvg": null,
      "tiers": []
    }
  ],
  "widgets": [
    {
      "id": "clz9kw1c0001s601widget001",
      "platform": "spotify",
      "type": "track",
      "identifier": "3n3Ppam7vgaVa1iaRUc9Lp",
      "order": 0
    }
  ],
  "audios": [
    {
      "id": "clz9ka1d0001s601audio0001",
      "title": "Midnight",
      "artist": "John",
      "externalUrl": "https://open.spotify.com/track/3n3Ppam7vgaVa1iaRUc9Lp",
      "order": 0,
      "audio": {
        "url": "https://r2.haunt.gg/audio/midnight.mp3",
        "mime": "audio/mpeg",
        "width": null,
        "height": null,
        "duration": 213
      },
      "cover": {
        "url": "https://r2.haunt.gg/audio/cover/midnight.png",
        "mime": "image/png",
        "width": 640,
        "height": 640,
        "duration": null
      }
    }
  ],
  "backgrounds": [
    {
      "id": "clz9kbg1e0001s601bg000001",
      "title": "Nebula",
      "order": 0,
      "asset": {
        "url": "https://r2.haunt.gg/background/nebula.jpg",
        "mime": "image/jpeg",
        "width": 1920,
        "height": 1080,
        "duration": null
      }
    }
  ],
  "links": [
    {
      "id": "clz9kl1a0001s601link00001",
      "title": "GitHub",
      "url": "https://github.com/john",
      "platformId": "github",
      "platformType": "social",
      "isText": false,
      "color": null,
      "recolor": false,
      "order": 0,
      "icon": null,
      "iconSvg": null
    },
    {
      "id": "clz9kl2b0002s601link00002",
      "title": "My Portfolio",
      "url": "https://john.dev",
      "platformId": null,
      "platformType": null,
      "isText": false,
      "color": "#7c3aed",
      "recolor": true,
      "order": 1,
      "icon": {
        "url": "https://r2.haunt.gg/link/portfolio.png",
        "mime": "image/png",
        "width": 128,
        "height": 128,
        "duration": null
      },
      "iconSvg": null
    }
  ],
  "projects": [
    {
      "id": "clz9kj1e0004s601c7d8e9f0",
      "title": "haunt.gg",
      "description": "A bio-link platform.",
      "tags": [
        "nextjs",
        "typescript"
      ],
      "url": "https://haunt.gg",
      "order": 0,
      "icon": {
        "url": "https://r2.haunt.gg/project/haunt.png",
        "mime": "image/png",
        "width": 256,
        "height": 256,
        "duration": null
      }
    }
  ],
  "views": 18234,
  "aliases": [
    {
      "name": "john-music",
      "redirect": false,
      "profileId": "clz9k3p1b0001s601a2b4c5d6"
    },
    {
      "name": "j",
      "redirect": true,
      "profileId": null
    }
  ],
  "gallery": [
    {
      "id": "clz9kg1a0000s601m1n2o3p4",
      "caption": "Sunset",
      "width": "half",
      "asset": {
        "url": "https://r2.haunt.gg/gallery/abc.png",
        "mime": "image/png",
        "width": 1200,
        "height": 800,
        "duration": null
      }
    }
  ],
  "sections": [
    {
      "id": "clz9ka1b0001s601q5r6s7t8",
      "type": "ABOUT_ME",
      "order": 0,
      "title": "About Me",
      "description": null,
      "alignment": "left",
      "enabled": true,
      "content": "hey, I build things.",
      "widgetIds": [
        "clz9kw1c0001s601widget001"
      ],
      "skills": {
        "frontend": [
          "react",
          "typescript"
        ],
        "backend": [
          "node"
        ],
        "database": [],
        "core": [],
        "game": [],
        "tools": [],
        "cloud": [],
        "mobile": [],
        "design": [],
        "music": [],
        "custom": [
          {
            "label": "Shipping fast",
            "iconName": "solar:rocket-bold",
            "iconSvg": "<svg …>"
          }
        ]
      },
      "skillsEnabled": true,
      "skillsAlignment": "left"
    },
    {
      "id": "clz9kp1d0003s601y3z4a5b6",
      "type": "PROJECTS",
      "order": 1,
      "title": null,
      "description": null,
      "alignment": "center",
      "projectIds": [
        "clz9kj1e0004s601c7d8e9f0"
      ]
    },
    {
      "id": "clz9kt1h0007s601o9p0q1r2",
      "type": "CUSTOM_TEXT",
      "order": 2,
      "title": "Notes",
      "description": null,
      "alignment": "left",
      "enabled": true,
      "content": "Anything I feel like writing."
    },
    {
      "id": "clz9kq1f0005s601g1h2i3j4",
      "type": "GALLERY",
      "order": 3,
      "title": "Gallery",
      "description": null,
      "alignment": "left",
      "items": [
        {
          "id": "clz9kg1a0000s601m1n2o3p4",
          "caption": "Sunset",
          "width": "half",
          "asset": {
            "url": "https://r2.haunt.gg/gallery/abc.png",
            "mime": "image/png",
            "width": 1200,
            "height": 800,
            "duration": null
          }
        }
      ]
    },
    {
      "id": "clz9kr1g0006s601k5l6m7n8",
      "type": "AUDIO_PLAYER",
      "order": 4,
      "title": null,
      "description": null,
      "alignment": "left"
    },
    {
      "id": "clz9ku1i0008s601s3t4u5v6",
      "type": "GAMES",
      "order": 5,
      "title": "Games",
      "description": null,
      "alignment": "left",
      "games": [
        {
          "rawgId": 3498,
          "name": "Grand Theft Auto V",
          "image": "https://media.rawg.io/media/games/456/456dea5e1c7e3cd07060c14e96612001.jpg",
          "slug": "grand-theft-auto-v",
          "released": "2013-09-17",
          "rating": 4.47,
          "metacritic": 92
        }
      ]
    },
    {
      "id": "clz9kv1j0009s601w7x8y9z0",
      "type": "ANIME",
      "order": 6,
      "title": "Anime",
      "description": null,
      "alignment": "left",
      "anime": [
        {
          "anilistId": 21,
          "title": "ONE PIECE",
          "image": "https://s4.anilist.co/file/anilistcdn/media/anime/cover/medium/bx21.jpg",
          "format": "TV",
          "year": 1999,
          "score": 88
        }
      ]
    },
    {
      "id": "clz9kx1k0010s601a1b2c3d4",
      "type": "MEDIA",
      "order": 7,
      "title": "Movies and Series",
      "description": null,
      "alignment": "left",
      "media": [
        {
          "tmdbId": 1396,
          "mediaType": "tv",
          "title": "Breaking Bad",
          "image": "https://image.tmdb.org/t/p/w342/ggFHVNu6YYI5L9pCfOacjizRGt.jpg",
          "year": 2008,
          "rating": 8.9
        }
      ]
    },
    {
      "id": "clz9ky1l0011s601e5f6g7h8",
      "type": "MUSIC",
      "order": 8,
      "title": "Music",
      "description": null,
      "alignment": "left",
      "music": [
        {
          "kind": "track",
          "spotifyId": "3n3Ppam7vgaVa1iaRUc9Lp",
          "title": "Mr. Brightside",
          "artist": "The Killers",
          "image": "https://i.scdn.co/image/ab67616d0000b273ccdddd46119a4ff53eaf1f5d",
          "album": "Hot Fuss"
        },
        {
          "kind": "album",
          "spotifyId": "6TJmQnO44YE5BtTxH8pop1",
          "title": "Hot Fuss",
          "artist": "The Killers",
          "image": "https://i.scdn.co/image/ab67616d0000b273ccdddd46119a4ff53eaf1f5d"
        },
        {
          "kind": "artist",
          "spotifyId": "0C0XlULifJtAgn6ZNCW2eu",
          "title": "The Killers",
          "image": "https://i.scdn.co/image/ab6761610000e5eb2c1c1c1c1c1c1c1c1c1c1c1c"
        }
      ]
    }
  ],
  "feedback": {
    "likes": 42,
    "dislikes": 3
  },
  "comments": {
    "count": 137,
    "items": [
      {
        "id": "clz9k7c2d0002s601e9f0g1h2",
        "content": "great page!",
        "likeCount": 5,
        "replyCount": 2,
        "pinned": true,
        "createdAt": "2026-05-09T12:34:56.000Z",
        "author": {
          "id": "clz9k8d3e0003s601i2j3k4l5",
          "uid": 99,
          "username": "jane",
          "name": "Jane",
          "avatarUrl": "https://r2.haunt.gg/avatar/jane.png"
        }
      }
    ]
  }
}

Rate limits

Two buckets apply at once:

BucketLimit
Per API key30 requests/minute, or 60 if the key's owner has premium.
Per IP address60 requests/minute across all of /api/*.

A free-tier key is therefore effectively capped at 30 requests per minute.

Things worth knowing

  • The lookup payload is deliberately leaner than what the public profile page renders. Widgets come without their display options and images, audio without synced lyrics, backgrounds without their device targeting.
  • sections[].widgetIds is a flat list of ids — the per-section widget widths are not exposed here.
  • A section whose stored configuration no longer validates is silently dropped from sections, and disabled sections are never returned at all.
  • Any /api/* request that reaches the origin without Cloudflare's cf-connecting-ip header is refused with 403 and {"error":"errors.forbidden"} before the route runs. This only affects callers that bypass Cloudflare.