Skip to main content

Address Labels API — Identify Crypto Wallets

The Address Labels API tells you who is behind a blockchain address: which exchange owns a hot wallet, whether an address is a per-user deposit address, whether a contract is a token (or a clone imitating one), and whether an address has been frozen by a stablecoin issuer. One GraphQL query — Metadata { Labels } — answers this across EVM chains, Solana, Bitcoin, and Tron.

CubeMetadata.Labels
Endpointshttps://streaming.bitquery.io/graphql and https://streaming.bitquery.io/eap
AuthOAuth token as Authorization: Bearer <token>
RequiredAn Address filter on every query
Batch sizeUp to 100 addresses per query
StreamingQuery-only — no subscription. Poll RecordedAt for new labels

Most people use it as the second step of a two-query pattern: pull addresses from an activity API such as DEXTrades or Transfers, then resolve those addresses to entities here — to enrich analytics, screen flows for exchange or gambling exposure, or strip exchange and contract addresses out of "real user" metrics.

Rules that matter

  1. The Address filter is mandatory. Every query must pin Address with is or in. There is no way to list all addresses carrying a label. Without it you get: "Labels query requires a Address filter in the where clause".
  2. Up to 100 addresses per query. Split larger lists into batches of 100.
  3. Matching is exact and case-sensitive. Pass EVM addresses in lowercase — a checksummed 0xAbC… returns zero rows. Bitcoin, Solana, and Tron addresses are case-sensitive by nature, so pass them exactly as they appear on-chain.
  4. Labels are append-only records. An address returns one row per chain, per label, per recording pass, so the same label recurs with different RecordedAt values. Fold that into a current view with limitByshown below.

How to look up labels for one address

{
Metadata {
Labels(
where: {Address: {in: ["0x18e296053cbdf986196903e889b7dca7a73882f6"]}}
) {
Address
Chain
Label {
Type
Value
}
RecordedAt
}
}
}

The response identifies the address as a Bybit hot wallet on every chain where it is labeled (abridged — the full response also repeats labels recorded on earlier passes):

{
"Metadata": {
"Labels": [
{
"Address": "0x18e296053cbdf986196903e889b7dca7a73882f6",
"Chain": "ethereum",
"Label": { "Type": "cex-hot-wallet", "Value": "bybit-hot-1" },
"RecordedAt": "2026-07-31T13:11:13Z"
},
{
"Address": "0x18e296053cbdf986196903e889b7dca7a73882f6",
"Chain": "bsc",
"Label": { "Type": "cex-hot-wallet", "Value": "bybit-hot" },
"RecordedAt": "2026-07-31T13:11:26Z"
}
]
}
}

How to get only the current labels

Because records accumulate, most applications want the latest record per address, chain, and label type. limitBy plus a RecordedAt sort does exactly that, and this is the shape you should reach for by default:

{
Metadata {
Labels(
where: {Address: {is: "0x18e296053cbdf986196903e889b7dca7a73882f6"}}
limitBy: {by: [Address, Chain, Label_Type], count: 1}
orderBy: {descending: RecordedAt}
) {
Address
Chain
Label {
Type
Value
}
RecordedAt
}
}
}

This returns one clean row per chain instead of the wallet's full recording history. Note the nested field is addressed as Label_Type in limitBy and orderBy — the groupable and sortable names are Address, Chain, Label_Type, Label_Value, and RecordedAt.

Keep Label_Type in the limitBy key unless you deliberately want one row per address: an address can legitimately carry several different labels, since a token contract is often tagged both token-contract and contract.

How to label up to 100 addresses at once

Pass the list as a variable. This is the shape to use when enriching the output of another query — top traders, transfer counterparties, or token holders:

query ($addresses: [String!]) {
Metadata {
Labels(
where: {Address: {in: $addresses}}
limitBy: {by: [Address, Chain, Label_Type], count: 1}
orderBy: {descending: RecordedAt}
) {
Address
Chain
Label {
Type
Value
}
RecordedAt
}
}
}
{
"addresses": [
"0x18e296053cbdf986196903e889b7dca7a73882f6",
"0x28c6c06298d514db089934071355e5743bf21d60"
]
}

Addresses with no labels are simply absent from the response — no error and no placeholder row. Anything missing is "unlabeled so far", which in wallet analytics usually means an ordinary user wallet.

How to check if an address is an exchange wallet

Combine a batch with a Label.Type filter to keep only exchange-owned addresses:

{
Metadata {
Labels(
where: {
Address: {in: [
"0x28c6c06298d514db089934071355e5743bf21d60",
"0x18e296053cbdf986196903e889b7dca7a73882f6",
"0x7a250d5630b4cf539739df2c5dacb4c659f2488d"
]}
Label: {Type: {in: ["cex-hot-wallet", "cex-cold-wallet", "cex-deposit-address"]}}
Chain: {is: "ethereum"}
}
limitBy: {by: [Address, Label_Type], count: 1}
orderBy: {descending: RecordedAt}
) {
Address
Label {
Type
Value
}
RecordedAt
}
}
}

Only the Binance and Bybit hot wallets come back. The Uniswap router drops out because its labels (contract: uniswap) don't match the requested types — which is exactly how you separate exchange addresses from protocol contracts in a mixed list.

How to screen for issuer-blocked (frozen) addresses

Stablecoin issuers freeze addresses on their own contracts. issuer-blocked-usdt and issuer-blocked-usdc capture those, so a startsWith filter screens for both at once:

{
Metadata {
Labels(
where: {
Address: {in: [
"0x098b716b8aaf21512996dc57eb0615e2383e2f96",
"0x28c6c06298d514db089934071355e5743bf21d60"
]}
Label: {Type: {startsWith: "issuer-blocked"}}
}
limitBy: {by: [Address, Label_Type], count: 1}
orderBy: {descending: RecordedAt}
) {
Address
Chain
Label {
Type
Value
}
}
}
}

How to label Bitcoin, Tron, and Solana addresses

Address formats mix freely in one batch — each row's Chain tells you where the label applies:

{
Metadata {
Labels(
where: {Address: {in: [
"34xp4vRoCGJym3xR7yCVPFHoCNxv4Twseo",
"TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
"9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM"
]}}
limitBy: {by: [Address, Chain, Label_Type], count: 1}
orderBy: {descending: RecordedAt}
) {
Address
Chain
Label {
Type
Value
}
}
}
}

This resolves a Binance Bitcoin cold wallet (Chain: "bitcoin"), the USDT contract on Tron (Chain: "tron"), and a Binance cold wallet on Solana (Chain: "solana").

How to watch for newly added labels

The cube has no subscription, so poll the addresses you track with a RecordedAt window and keep the high-water mark on your side:

{
Metadata {
Labels(
where: {
Address: {is: "0x18e296053cbdf986196903e889b7dca7a73882f6"}
RecordedAt: {since: "2026-07-15T00:00:00Z"}
}
) {
Chain
Label {
Type
Value
}
RecordedAt
}
}
}

How to aggregate labels

The cube supports the standard metrics count and uniq, and the dimensions you select become the grouping key. This counts label records and distinct labeled addresses per chain:

{
Metadata {
Labels(
where: {Address: {in: [
"0x18e296053cbdf986196903e889b7dca7a73882f6",
"0x28c6c06298d514db089934071355e5743bf21d60",
"0xdac17f958d2ee523a2206206994597c13d831ec7"
]}}
) {
Chain
count
uniq(of: Address)
}
}
}

Filters

Full filter support applies on top of the mandatory Address:

FilterOperatorsNotes
Addressis, in onlyMandatory, max 100 in in. No negation — you cannot exclude addresses server-side.
Chainis, in, not, notIn, like, includes, startsWith, …Exact chain slugs — see the table below.
Label: {Type: …}full string sete.g. {is: "cex-hot-wallet"}
Label: {Value: …}full string sete.g. {startsWith: "binance"}
RecordedAtsince, till, after, before, is, plus _relative variantsStandard DateTime filters.
anylist of sub-filtersOR-combinator across conditions.

Response fields

FieldTypeMeaning
AddressStringThe queried address, exactly as stored (EVM addresses lowercase).
ChainStringChain this label applies to — one address maps to many chains.
Label.TypeStringLabel category — see below.
Label.ValueStringEntity slug within the category, e.g. binance-hot-1, bybit-hot, wavax, banned-by-usdt. Numbered suffixes distinguish instances of the same entity.
RecordedAtDateTimeWhen the labeling pipeline wrote this record. Re-confirmation appends a new record rather than updating the old one.

Supported chains

Chain values are plain slugs and the set grows as coverage expands. Verified live:

EcosystemChain values
EVMethereum, bsc, polygon, arbitrum, base, avalanche-c, fantom, ethpow
Non-EVMbitcoin, tron, solana

If you're unsure what a chain is called, query a known address from it without a Chain filter and read the slug off the response.

Label types

Label.Type is an open taxonomy — new categories appear as the pipeline learns new entity classes, so handle unknown types gracefully. The common ones:

Label.TypeMeaningExample Value
cex-hot-walletExchange-operated hot walletbinance-hot-1, bybit-hot
cex-cold-walletExchange cold storagebinance-cold
cex-deposit-addressPer-user deposit address swept to an exchangecoinex-deposit
token-contractA token's contract or mint addressusdt, wavax, wbnb
token-cloneContract imitating a well-known tokenclone-wmatic-2
contractGeneral smart-contract taguniswap, bridge, stablecoin
gamblingGambling operator walletstake-com-hot
issuer-blocked-usdtFrozen or blacklisted by Tetherbanned-by-usdt
issuer-blocked-usdcFrozen or blacklisted by Circlebanned-by-usdc

Limits and common errors

  • Missing Address filter — fails with "Labels query requires a Address filter in the where clause". A Chain or Label filter alone does not satisfy it.
  • Wrong casing — a checksummed EVM address silently returns zero rows. Lowercase before querying.
  • No negation on Addressnot/notIn aren't part of the Address filter. Exclusion belongs client-side, or in Chain/Label filters, which do support it.
  • Subscriptionssubscription { Metadata { … } } is rejected; the cube exists only under query.
  • Empty result is not an error — unlabeled addresses, and an empty in: [], return an empty list.
  • limit / limitBy and orderBy behave as on every other cube.

Pick the right query

You needUse
Who is behind one addressAddress: {is: …}
Enrich a list of ≤100 addressesAddress: {in: …} with limitBy: {by: [Address, Chain, Label_Type], count: 1}
Exchange-wallet screeningLabel: {Type: {in: ["cex-hot-wallet", "cex-cold-wallet", "cex-deposit-address"]}}
Fake-token checksLabel: {Type: {is: "token-clone"}}
Sanctions and issuer-freeze screeningLabel: {Type: {startsWith: "issuer-blocked"}}
Newly labeled addressesPoll with RecordedAt: {since: …}

Frequently Asked Questions

What is the Bitquery Address Labels API?

It is the Metadata.Labels cube in Bitquery's GraphQL API. It maps a blockchain address to the real-world entity behind it — exchange hot and cold wallets, per-user deposit addresses, token contracts and clones, gambling operators, and addresses frozen by stablecoin issuers — across EVM chains, Solana, Bitcoin and Tron.

Is the Address filter required?

Yes. Every Labels query must pin the Address field with is or in. Without it the API returns the error 'Labels query requires a Address filter in the where clause'. A Chain or Label filter alone does not satisfy the requirement.

How many addresses can I look up in one query?

Up to 100 addresses per query in the Address in list. To enrich a larger set, split it into batches of 100 and issue one query per batch.

Can I list every address that has a given label?

No. Because the Address filter is mandatory, the API answers 'what labels does this address have' rather than 'which addresses have this label'. You supply the candidate addresses, then filter them by label type or value.

Why does my address return no labels?

Either the address genuinely has no label yet, or the casing is wrong. Matching is exact and case-sensitive, so EVM addresses must be lowercase — a checksummed address returns zero rows. Unlabeled addresses are simply absent from the response rather than returning an error.

Why does one address return multiple rows with the same label?

Labels are append-only records. Each time the pipeline re-confirms a label it appends a new row with a fresh RecordedAt timestamp, and an address can also be labeled on several chains. Use limitBy with the key Address, Chain, Label_Type and count 1, ordered by descending RecordedAt, to get one current row per label.

Can I subscribe to label updates in real time?

No. The Metadata cube is query-only and does not exist under the GraphQL subscription root, so labels cannot be streamed. To pick up new labels, poll the addresses you track with a RecordedAt since filter and keep the high-water mark on your side.

Which blockchains have address labels?

Verified chains include Ethereum, BSC, Polygon, Arbitrum, Base, Avalanche C-Chain, Fantom and EthereumPoW on the EVM side, plus Bitcoin, Tron and Solana. The set grows as label coverage expands, and a single address can carry different labels on different chains.