> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cabalspy.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Profile

> GET /v1/profile: one CabalSpy ID user by X handle, csid_id (the sub of the ID token) or a verified wallet address.

One user's profile: the X account, a verified Telegram account, the KOL badge and every verified wallet with the chains it is verified on. Find the user by their handle, by their `csid_id` (the `sub` of their ID token) or by one of their verified wallets.

Users who signed in with Telegram or Google and have not connected X have `x` and `twitter` set to null, no bio and a handle like `tg.<telegram username>` or `id.<10 hex characters>`. See [Users without X](/cabalspy-id/openid-connect#users-without-x).

<Tip>
  Pass exactly one of username, csid\_id or address. The user must be one of your users, within your plan's user limit, unless you use scope=global on the Enterprise plan.
</Tip>

## Query Parameters

<ParamField query="username" type="string">
  The user's handle, with or without the @: their X handle, or `tg.<name>` / `id.<hex>` for users without X.
</ParamField>

<ParamField query="csid_id" type="string">
  The user's id, the `sub` claim of their ID token.
</ParamField>

<ParamField query="address" type="string">
  A wallet verified by the user, on any chain.
</ParamField>

<ParamField query="scope" default="instance" type="string">
  `global` searches every CabalSpy ID user. Enterprise plan only.
</ParamField>

<ParamField query="instance" type="string">
  Your Instance ID. Only needed when your account has several instances.
</ParamField>

<ParamField query="api_key" type="string" required>
  Your API key. Alternatively pass it as an Authorization Bearer header.
</ParamField>

## Usage Example

<CodeGroup>
  ```bash cURL theme={null}
  curl "https://api-id.cabalspy.xyz/v1/profile?username=MazeCCC" \
    -H "Authorization: Bearer YOUR_KEY"
  ```

  ```python Python theme={null}
  import requests

  r = requests.get("https://api-id.cabalspy.xyz/v1/profile",
                   params={"csid_id": "0b7c4a52-3f1e-4f43-9d0a-2b5e7c1d9a11"},
                   headers={"Authorization": "Bearer YOUR_KEY"})
  p = r.json()["data"]
  print(p["username"], "KOL" if p["kol"]["badge"] else "", [w["address"] for w in p["wallets"]])
  ```

  ```javascript JavaScript theme={null}
  const p = new URLSearchParams({ address: "7j7AA3HZR2zEjwAQEKPFh2qucLY4fqZpB9iodf39w8xW" });
  const res = await fetch(`https://api-id.cabalspy.xyz/v1/profile?${p}`, {
    headers: { Authorization: "Bearer YOUR_KEY" },
  });
  const { data } = await res.json();
  console.log(data.username, data.kol.badge, data.wallets);
  ```
</CodeGroup>

## Response

<ResponseField name="success" type="boolean">
  True on success.
</ResponseField>

<ResponseField name="data" type="object">
  <Expandable title="data">
    <ResponseField name="csid_id" type="string">
      The user's id, the `sub` of their ID token.
    </ResponseField>

    <ResponseField name="username" type="string">
      X handle, or `tg.<telegram username>` / `id.<10 hex characters>` for users without X.
    </ResponseField>

    <ResponseField name="name" type="string">
      Display name from X, Telegram or Google.
    </ResponseField>

    <ResponseField name="image_url" type="string">
      Avatar URL from X or Google, can be null.
    </ResponseField>

    <ResponseField name="bio" type="string">
      X bio, null without X.
    </ResponseField>

    <ResponseField name="twitter" type="string">
      X profile link, null without X.
    </ResponseField>

    <ResponseField name="telegram" type="string">
      Link to the user's verified Telegram account (`https://t.me/<username>`), or an empty string.
    </ResponseField>

    <ResponseField name="x" type="object">
      X account, null when the user has no X account connected.

      <Expandable title="x">
        <ResponseField name="id" type="string">
          Numeric X user id.
        </ResponseField>

        <ResponseField name="followers" type="integer">
          Followers.
        </ResponseField>

        <ResponseField name="following" type="integer">
          Accounts followed.
        </ResponseField>

        <ResponseField name="verified" type="boolean">
          X verification.
        </ResponseField>

        <ResponseField name="verified_type" type="string">
          X verification type.
        </ResponseField>
      </Expandable>
    </ResponseField>

    <ResponseField name="kol" type="object">
      <Expandable title="kol">
        <ResponseField name="badge" type="boolean">
          Whether the user holds the reviewed KOL badge. Requires X.
        </ResponseField>

        <ResponseField name="since" type="string">
          When the badge was granted, or null.
        </ResponseField>
      </Expandable>
    </ResponseField>

    <ResponseField name="profile_url" type="string">
      Public CabalSpy ID profile.
    </ResponseField>

    <ResponseField name="wallets" type="array">
      <Expandable title="wallet">
        <ResponseField name="address" type="string">
          Wallet address.
        </ResponseField>

        <ResponseField name="family" type="string">
          Address family, for example solana or evm.
        </ResponseField>

        <ResponseField name="chains" type="array">
          Chains the wallet is verified on.
        </ResponseField>

        <ResponseField name="verified_at" type="string">
          When the wallet was verified.
        </ResponseField>
      </Expandable>
    </ResponseField>

    <ResponseField name="joined_at" type="string">
      When the CabalSpy ID was created.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseExample>
  ```json Response theme={null}
  {
    "success": true,
    "data": {
      "csid_id": "0b7c4a52-3f1e-4f43-9d0a-2b5e7c1d9a11",
      "username": "MazeCCC",
      "name": "Maze",
      "image_url": "https://pbs.twimg.com/profile_images/.../photo.jpg",
      "bio": "onchain since 2020",
      "twitter": "https://x.com/MazeCCC",
      "telegram": "https://t.me/maze_calls",
      "x": {
        "id": "1440000000000000000",
        "followers": 48210,
        "following": 812,
        "verified": true,
        "verified_type": "blue"
      },
      "kol": { "badge": true, "since": "2026-10-10T12:00:00Z" },
      "profile_url": "https://id.cabalspy.xyz/u/MazeCCC",
      "wallets": [
        {
          "address": "7j7AA3HZR2zEjwAQEKPFh2qucLY4fqZpB9iodf39w8xW",
          "family": "solana",
          "chains": ["solana"],
          "verified_at": "2026-10-09T14:21:07Z"
        },
        {
          "address": "0x4b1e9c0d2a7f3e5b8c6d1a9f0e2b7c4d5a6e8f01",
          "family": "evm",
          "chains": ["base", "bnb"],
          "verified_at": "2026-10-11T08:40:12Z"
        }
      ],
      "joined_at": "2026-10-09T14:02:51Z"
    },
    "meta": {
      "request_id": "req_5162c7cf7f87",
      "cached": false,
      "cache_age_seconds": 0,
      "version": "v1",
      "timestamp": "2026-10-13T08:04:11Z"
    }
  }
  ```

  ```json Without X theme={null}
  {
    "success": true,
    "data": {
      "csid_id": "5e2d8c41-7a9b-4c3e-b1f0-6d5e4c3b2a19",
      "username": "tg.degen_dan",
      "name": "Dan",
      "image_url": null,
      "bio": null,
      "twitter": null,
      "telegram": "https://t.me/degen_dan",
      "x": null,
      "kol": { "badge": false, "since": null },
      "profile_url": "https://id.cabalspy.xyz/u/tg.degen_dan",
      "wallets": [],
      "joined_at": "2026-10-15T09:02:51Z"
    },
    "meta": { "request_id": "req_5162c7cf7f87", "cached": false, "cache_age_seconds": 0, "version": "v1", "timestamp": "2026-10-15T10:04:11Z" }
  }
  ```
</ResponseExample>

## Errors

| Status | Code | Meaning |
| - | - | - |
| 400 | missing\_parameter | Not exactly one of username, csid\_id, address. |
| 400 | invalid\_parameter | csid\_id is not a valid id, or username too long. |
| 401 | missing\_api\_key | API key missing. |
| 403 | invalid\_api\_key | API key invalid or not enabled for CabalSpy ID. |
| 403 | plan\_user\_limit | csid\_id is an anonymous id (anon\_...) of a user beyond your plan's limit. |
| 403 | scope\_not\_allowed | scope=global needs the Enterprise plan. |
| 404 | profile\_not\_found | No such user in your instance, or beyond your plan's user limit. |
| 429 | rate\_limit\_exceeded | Rate limit exceeded. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.