---
title: "Search wallet profiles"
method: GET
path: "/v0/profiles"
tags: ["Profiles"]
---

# Search wallet profiles

`GET /v0/profiles`

## Request

- Base URL: `https://api.formo.so`
- URL: `https://api.formo.so/v0/profiles`
- Auth: HTTP bearer

## Query parameters

- `address` string
- `expand` string
- `timestamp` string, date-time
- `order_by` 'net_worth_usd' | 'tx_count' | 'first_onchain' | 'last_onchain' | 'updated_at' | 'first_seen' | 'last_seen' | 'num_sessions' | 'revenue' | 'volume' | 'points'
- `order_dir` 'asc' | 'desc'
- `page` integer
- `size` integer

## Request body

- ProfileFilter: A profile-search filter group. `filters` contains canonical `{field, op, value}` leaves and `logic` combines them at this level.
  - `logic` 'and' | 'or'
  - `filters` FilterCondition[], required
    - `field` string, required: Stable profile filter field. User/profile/social fields use user.* or users.*. Resource metrics use chains.balance, apps.balance, tokens.balance, or labels.value; resource identifiers belong in named qualifier properties and must not be embedded in the field path.
    - `op` 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'in' | 'nin' | 'contains' | 'startsWith' | 'endsWith' | 'notEmpty' | 'isEmpty', required: Comparison operator. Only the canonical terse tokens (`eq`, `neq`, `gt`, `gte`, `lt`, `lte`, `in`, `nin`, `contains`, `startsWith`, `endsWith`, `notEmpty`, `isEmpty`) are accepted; the retired long-form spellings (`equals`, `notEquals`, `greater`, `greaterOrEqual`, `less`, `lessOrEqual`, `notIn`, `includes`) are rejected with a 400 naming the token. **Substring operators:** `contains`, `startsWith` and `endsWith` are supported on routable string user attributes (`users.device`, `users.os`, `users.referrer`, `users.utm_*`, `users.click_id`, and the `first_*`/`last_*` attribution variants), where they match **case-sensitively**. Social fields (e.g. `users.twitter`, `users.email`) additionally support `contains`, which matches **case-insensitively** there; `startsWith`/`endsWith` are rejected on social fields. `contains` is also supported on `labels.value` and matches case-insensitively; `startsWith` and `endsWith` remain unsupported on labels. All three substring operators are rejected on numeric, chain/app/token, and lifecycle fields. `notEmpty` ("is not empty" / "is set") is a string existence check for user string attributes (device/os/utm_*/referrer/…) and social fields; `isEmpty` ("is empty" / "is not set") is its complement, supported for user string attributes only (not social fields). Both ignore `value`. `users.lifecycle` supports only `eq` (a single stage) and `in` (a list of stages).
    - `value` union: Filter value (string, number, boolean, or non-empty array). Required for every operator except the existence checks `notEmpty` and `isEmpty`, which ignore it. Resource balance fields (`chains.balance`, `apps.balance`, and `tokens.balance`) require a JSON number; empty strings and numeric strings are rejected. `labels.value` also rejects an empty string operand.
      - string
      - number
      - boolean
      - union[]
        - union
          - string
          - number
    - `scope` 'any' | 'protocol': Required for tokens.balance: any includes wallet and protocol balances; protocol requires app_id.
    - `chain_id` string: Optional chain qualifier. Omit to compare across all chains.
    - `app_id` string: Required for apps.balance and for tokens.balance with scope=protocol.
    - `token_address` string: Required token address for tokens.balance.
    - `tag_id` string: Required label tag for labels.value.

## Response `200`

Paginated wallet profile search results.

- object: Pagination cursor returned alongside `data` on every paginated list endpoint. Use these to walk pages: `has_more` is true while `page * size < total`. Combine with the matching `Page` and `Size` query parameters to request the next page.
  - `page` integer, required: 1-indexed page number echoed from the request.
  - `size` integer, required: Page size echoed from the request.
  - `total` integer, required: Total row count across all pages.
  - `has_more` boolean, required: True when more pages remain (`page * size < total`).
  - `data` Profile[], required
    - `address` string
    - `net_worth_usd` number
    - `tx_count` integer
    - `first_onchain` string: First onchain activity date
    - `last_onchain` string: Last onchain activity date
    - `updated_at` string, date-time
    - `ens` string, nullable
    - `farcaster` string, nullable
    - `lens` string, nullable
    - `basenames` string, nullable
    - `linea` string, nullable
    - `avatar` string, nullable
    - `display_name` string, nullable
    - `description` string, nullable
    - `discord` string, nullable
    - `telegram` string, nullable
    - `twitter` string, nullable
    - `github` string, nullable
    - `linkedin` string, nullable
    - `email` string, nullable
    - `instagram` string, nullable
    - `facebook` string, nullable
    - `website` string, nullable
    - `reddit` string, nullable
    - `youtube` string, nullable
    - `tiktok` string, nullable
    - `first_seen` string, nullable: First seen in project
    - `last_seen` string, nullable: Last seen in project
    - `lifecycle` 'New' | 'Returning' | 'Power user' | 'At Risk' | 'Churned' | 'Resurrected', nullable
    - `num_sessions` integer, nullable
    - `revenue` number, nullable
    - `volume` number, nullable
    - `points` number, nullable
    - `device` string, nullable
    - `browser` string, nullable
    - `os` string, nullable
    - `location` string, nullable
    - `first_utm_source` string, nullable
    - `first_utm_medium` string, nullable
    - `first_utm_campaign` string, nullable
    - `first_referrer` string, nullable
    - `last_utm_source` string, nullable
    - `last_utm_medium` string, nullable
    - `last_utm_campaign` string, nullable
    - `last_referrer` string, nullable
    - `first_referrer_url` string, nullable: First referrer full URL
    - `last_referrer_url` string, nullable: Last referrer full URL
    - `first_ref` string, nullable: First referral code
    - `last_ref` string, nullable: Last referral code
    - `first_utm_content` string, nullable: First UTM content
    - `last_utm_content` string, nullable: Last UTM content
    - `first_utm_term` string, nullable: First UTM term
    - `last_utm_term` string, nullable: Last UTM term
    - `first_paid_source` string, nullable: First-touch acquiring ad network (google, meta, microsoft, tiktok, twitter, linkedin, reddit, or paid_utm for pricing-medium UTMs with no click ID)
    - `last_paid_source` string, nullable: Last-touch acquiring ad network (same value set as first_paid_source)
    - `first_click_id` string, nullable: First-touch raw ad-platform click ID (gclid, fbclid, msclkid, ttclid, twclid, li_fat_id, or rdt_cid)
    - `last_click_id` string, nullable: Last-touch raw ad-platform click ID
    - `last_type` string, nullable: Last event type
    - `last_event` string, nullable: Last event name
    - `last_properties` string, nullable: Last event properties (JSON string)
    - `activity_dates` string[], nullable: Array of activity dates (YYYY-MM-DD format)
    - `chains` WalletChain[]: Requires expand=chains
      - `chain_id` string
      - `net_worth_usd` number
      - `tx_count` integer
      - `first_onchain` string
      - `last_onchain` string
    - `apps` WalletApp[]: Requires expand=apps
      - `chain_id` string
      - `id` string
      - `name` string
      - `img` string, nullable
      - `url` string, nullable
      - `balance_usd` number
    - `tokens` WalletToken[]: Requires expand=tokens
      - `chain_id` string
      - `token_address` string
      - `app_id` string
      - `name` string
      - `symbol` string
      - `img` string, nullable
      - `decimals` integer
      - `price` number
      - `balance` string
      - `balance_usd` number
    - `labels` WalletLabel[]: Requires expand=labels
      - `id` string: e.g. coinbase.verified_account
      - `value` string
      - `chain_id` string
      - `source` string

## Other responses

- `400`: The request was rejected. `code` is either `INVALID_VALIDATION_REQUEST` (Zod schema mismatch; `details` carries a `{ fieldPath: message }` map) or `BAD_REQUEST` (semantic validation failure outside Zod, e.g. mismatched IDs, business-rule violations). Branch on `code`, not status, to tell the two apart.
- `401`: Missing or invalid API key.
- `403`: The API key is valid but lacks the required scope for this endpoint.
- `429`: Per-workspace rate limit exceeded. Inspect the `RateLimit-*` response headers and back off.
- `500`: An unexpected error occurred on the server. The error has been logged; retry with exponential backoff.

---

[API](https://skmtc.dev/formo/apis/formo-public-api.md) · [All operations](https://skmtc.dev/formo/apis/formo-public-api/llms.txt) · [OpenAPI document](https://skmtc.dev/formo/apis/formo-public-api/revisions/67ada57df63c?raw)
