Get wallet identity profile

One request, one JSON: verified on-chain names (ENS, Basenames, Unstoppable Domains, SPACE ID), native balance / nonce / contract flag on every mounted EVM network plus Tron, activity summary, known-address labels, and — on request — deployer detection with the list of created tokens.

GEThttps://triport.io/v1/wallet/0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045/identity?chains=eth,base,tron
Network
Cross-chain — Ethereum, Base, Polygon, BNB Smart Chain, Robinhood Chain, Tron
Scope
— (tier-gated, see below)
Auth
x-token: $TRIPORT_API_KEY (or Authorization: Bearer, or ?api-key= query)
Rate limit
Business — 30 rps (wallet_identity, burst ×2); Enterprise — 150 rps

Terms on this page: JSON-RPC.

For transaction history, period volumes, counterparties, bridge/exchange flows, token approvals and shared funding candidates, use the separate Wallet Research endpoint. It reports scanned coverage per network and complements this lightweight profile.

{address} is an EVM address (0x + 40 hex characters). The profile is assembled in parallel from every EVM network mounted on the serving PoP; the Tron address is derived from the same key and queried over Tron JSON-RPC.

Names are read straight from the chain (Universal Resolver for ENS, the Basenames registry on Base, Unstoppable Domains ProxyReader on Ethereum and Polygon, the SPACE ID registry on BNB Smart Chain). A name is returned only when its forward record resolves back to the queried address (verified: true), so a reverse record pointing at somebody else's name is never reported as that name. Names that live off-chain (CCIP-read, e.g. *.cb.id) are listed in unresolved with reason: offchain_lookup.

The response is 200 even when part of the profile is missing: a network that is not mounted on this PoP or did not answer is reported per chain in chains.<net>.error, and a name source that failed is listed in unresolved. Only when every requested network fails does the endpoint return 503 all_chains_unavailable.

The deployer group is off by default because it scans the address's CREATE range: Triport derives every contract address the wallet could have created (keccak(rlp([address, nonce])) for nonce = 0…N-1), checks which of them hold code, and probes each for ERC-20 metadata (symbol, name, decimals, totalSupply). Request it with fields=deployer; deployer_max_nonce caps the scan (default 2000, ceiling 5000, truncated: true when the wallet's nonce is higher).

This surface is separate from the Solana wallet REST at /v1/wallet/{base58}/…, which keeps its own contract and is not tier-gated.

Parameters

addressstringrequired
Path segment. EVM address, 0x + 40 hex characters, any letter case. The response echoes it lower-cased.
chainsstringoptional
Comma-separated subset of eth,base,polygon,bsc,robinhood,tron. Default: all six. Unknown id → 400 unknown_chain; a known network not mounted on this PoP → chains.<net>.error: "not_mounted". Selects networks for balances, activity and deployer only — names and socials do not depend on it.
name_sourcesstringoptional
Comma-separated subset of ens,basenames,spaceid,unstoppable,farcaster,lens. Default: all. Unknown id → 400 invalid_name_sources; an external source named here but not enabled on this PoP → unresolved[] with not_available.
fieldsstringoptional
Comma-separated projection of names,socials,balances,activity,labels,deployer. Default names,balances,activity,labels. socials is off by default and implies names — see Socials.
deployer_max_nonceintegeroptional
Nonce range scanned for created contracts. Default 2000, ceiling 5000.
fresh0/1optional
Bypass the profile cache (names 15 min, balances 60 s). Costs a second wallet_identity token.

Response

Response fields

FieldTypeDescription
addressstringQueried address, lower-cased.
address_tronstringTron base58 form of the same key.
names[]arrayVerified names. source ∈ ens, basenames, unstoppable, spaceid, farcaster, lens; chain is where the record was read (optimism for Farcaster, lens for Lens Chain); primary marks the address's reverse record; verified means the forward record resolves back to the address (for Farcaster / Lens — the address is the account's custody address / owner); normalized means the name is already ENSIP-15 normalized. With fields=socials each name also carries records — raw text records, key → value.
socials[]arrayOnly with fields=socials: social accounts and profile links, see Socials.
labels[]arrayTriport labels of known addresses (source: "triport", entity, category ∈ cex, bridge, solver, dex, mixer, fund, other). Only verified labels are returned.
chains.<net>objectPer-network state: balance_wei (or balance_sun on Tron), balance in coin units, nonce (null on Tron), is_contract, latency_ms. Present with error instead when the network did not answer.
deployerobjectOnly with fields=deployer: is_deployer, scanned_nonces, truncated, contracts[] (chain, address, nonce, is_erc20, symbol, name, decimals, total_supply), tokens_created, errors.
activityobjectany_tx, chains_active[] (nonce > 0), max_nonce.
unresolved[]arrayName sources that could not answer: chain, source, reason ∈ offchain_lookup, upstream_error, not_mounted, timeout, budget, rpc_error, not_available, records_error. Always present (possibly empty).
metaobjectcached, generated_at, pop (eu/us), cost — tokens charged per tier category.

Socials (fields=socials)

Request fields=socials (off by default; it implies names). The response then carries socials[] — normalized social accounts and profile links — and records on every verified name — the raw text records exactly as stored:

curl "https://triport.io/v1/wallet/0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045/identity?fields=socials&chains=robinhood" \
  -H "Authorization: Bearer $TRIPORT_API_KEY"
{
  "address": "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
  "names": [
    {
      "source": "ens",
      "name": "vitalik.eth",
      "chain": "eth",
      "primary": true,
      "verified": true,
      "normalized": true,
      "records": {
        "com.twitter": "VitalikButerin",
        "com.github": "vbuterin",
        "url": "https://vitalik.eth.limo",
        "email": "owner@example.com"
      }
    }
  ],
  "socials": [
    {"platform": "twitter", "handle": "VitalikButerin", "url": "https://x.com/VitalikButerin", "source": "ens", "name": "vitalik.eth", "chain": "eth", "key": "com.twitter", "verified_owner": false},
    {"platform": "github", "handle": "vbuterin", "url": "https://github.com/vbuterin", "source": "ens", "name": "vitalik.eth", "chain": "eth", "key": "com.github", "verified_owner": false},
    {"platform": "website", "handle": "", "url": "https://vitalik.eth.limo", "source": "ens", "name": "vitalik.eth", "chain": "eth", "key": "url", "verified_owner": false}
  ],
  "unresolved": [],
  "meta": {"cached": false, "generated_at": "2026-09-22T10:00:00Z", "pop": "eu", "cost": {"wallet_identity": 1, "eth_read_rpc": 2}}
}

Each socials[] entry names the platform, the handle (without @; empty for website / avatar / header), a canonical url built by Triport, and where it came from: source, name (the name or account), chain and the original key. The same platform found under two names gives two entries, in the order of names.

PlatformENS / Basenames / SPACE ID key (ENSIP-5)Unstoppable Domains keyFarcasterLens attribute
twittercom.twitter (vnd.twitter if com.twitter is empty)social.twitter.usernameprofile field, or X account confirmed by the Farcaster clientx, twitter
githubcom.github—profile field, or GitHub account confirmed by the Farcaster clientgithub
telegramorg.telegramsocial.telegram.username——
discordcom.discordsocial.discord.username——
redditcom.redditsocial.reddit.username——
youtube—social.youtube.username——
websiteurl, url2, url3—profile URLwebsite, url
avataravatarsocial.picture.valueprofile picturepicture
headerheader—banner—

Returned only in records, never in socials: email, description, xyz.farcaster (a Farcaster name claimed in a name record — the Farcaster account itself comes from the farcaster source), Unstoppable whois.email.value and ipfs.html.value, Lens name and bio. Values are untrusted input written by the name or account owner: socials[] holds only values that passed per-platform validation (handle pattern, 256-character cap, https:// links; avatars also ipfs:// and eip155:), while records returns them as stored — escape them before rendering.

verified_owner / verified_by. The name itself is forward-verified (names[].verified: true; records of unverified names never become socials), but a text record is only what the name owner claims. Entries from ENS, Basenames, SPACE ID and Unstoppable records, and Farcaster / Lens profile strings that nobody confirmed, are verified_owner: false. verified_owner: true appears only on an X or GitHub account that the Farcaster client confirmed for the account, and always together with verified_by: "farcaster_client"; the Farcaster protocol itself does not verify these strings.

Robinhood Chain and Tron: socials through the address's name. Neither network has a name service of its own, and names do not depend on chains. With chains=robinhood (or tron) and fields=socials the response still reads the address's ENS, Basenames, SPACE ID and Unstoppable names on their own networks and returns their socials; Farcaster and Lens are looked up by the same EVM address.

Farcaster and Lens. Farcaster accounts are found by the account's custody address only (the Farcaster ID registry on Optimism, profile from a public Farcaster Hub): an address that is merely a verified (connected) address of an account, and not its custody address, returns no Farcaster account. Lens returns an account the address owns (source: lens, chain: lens). The account name goes to names[], its profile links to socials[], the raw profile fields to records. These are external services: when one answers partially or not at all the response is still 200 with what was found, and the source is listed in unresolved[] with upstream_error, timeout or budget. An external source is enabled per PoP; named in name_sources but not enabled, it is reported as not_available and not charged.

Cost. Reading records adds one <net>_read_rpc (SPACE ID — bsc_read_rpc_heavy) per name service where a name was found, charged to that service's own network. Each external source adds one social_read per address on a cache miss. Everything is itemized in meta.cost; records are cached with the names (15 minutes). If records could not be read, the name stays in names without records and unresolved[] gets reason: records_error.

Errors

CodeMeaningWhen it happens
400invalid_address / unknown_chain / invalid_fields / invalid_name_sources / invalid_deployer_max_nonceMalformed address, unknown network id, unknown field name, unknown name source, or a negative nonce cap. Body: {"error": "<code>", "message": "…", "code": 400}.
401unauthorized / trial_expired / subscription_expiredMissing or invalid credentials, or the trial/subscription has lapsed.
403tier_insufficientThe API key's tier is below business. Response carries current_tier, required_tier, and the X-Required-Tier header.
429rate_limitedwallet_identity sustained RPS exceeded (or the second token for fresh=1 is not available). Response carries Retry-After.
503all_chains_unavailableEvery requested network failed or none is mounted on this PoP.

All error bodies use the shared envelope (error, message, request_id). See errors.md for the full envelope and per-code fields.

Examples

JavaScript (fetch)

const address = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045";
const res = await fetch(
  `https://triport.io/v1/wallet/${address}/identity?fields=names,balances,deployer`,
  { headers: { Authorization: `Bearer ${process.env.TRIPORT_API_KEY}` } }
);


if (!res.ok) {
  const err = await res.json();
  throw new Error(`${res.status} ${err.error}: ${err.message}`);
}


const profile = await res.json();
const primary = profile.names.find((n) => n.primary && n.verified);
console.log(primary?.name ?? "(no verified name)", profile.deployer?.tokens_created ?? 0);

Python (requests)

import os
import requests


address = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
res = requests.get(
    f"https://triport.io/v1/wallet/{address}/identity",
    params={"chains": "eth,base,bsc", "fields": "names,balances,labels"},
    headers={"Authorization": f"Bearer {os.environ['TRIPORT_API_KEY']}"},
    timeout=30,
)
res.raise_for_status()
profile = res.json()
for chain, state in profile["chains"].items():
    print(chain, state.get("balance"), state.get("error", ""))

Filtering a list of addresses (shell)

while read -r addr; do
  curl -s "https://triport.io/v1/wallet/$addr/identity?fields=names,balances" \
    -H "Authorization: Bearer $TRIPORT_API_KEY" \
  | jq -r 'select(.names | length > 0) | "\(.address) \(.names[0].name) \(.chains.eth.balance // "-")"'
done < addresses.txt

Notes

  • Cost accounting: each request charges one wallet_identity token plus, per queried network, one <net>_read_rpc token per internal upstream call (BSC eth_call is charged as bsc_read_rpc_heavy). The exact breakdown is returned in meta.cost, so batch runs can be sized against the key's per-network budgets.
  • Caching: names are cached for 15 minutes, balances and nonces for 60 seconds per PoP; meta.cached: true means no upstream call was made. fresh=1 bypasses the cache at double the wallet_identity cost.
  • Tron: the account model has no nonce, so chains.tron.nonce is null and the deployer scan skips Tron; activity comes from the account's last operation time.
  • Off-chain names: CCIP-read resolvers (ENSIP-10) are not followed in this version; such names appear in unresolved with reason: offchain_lookup rather than being silently dropped.
  • Related: the Solana wallet surface is documented under Solana REST; the tier matrix is in Rate limits and tiers.

Wallet overview preview

The app offers a limited overview before upgrading: counts of used networks, verified names and address labels. Names, entity labels, balances, transaction counts by network and deployment details require Business or Enterprise. These details are omitted from preview API responses; the locked panel displays placeholders. Preview requests retain address, concurrency and rate limits.