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.
| Cube | Metadata.Labels |
| Endpoints | https://streaming.bitquery.io/graphql |
| Auth | OAuth token as Authorization: Bearer <token> |
| Required | An Address filter on every query |
| Batch size | Up to 100 addresses per query |
| Streaming | Query-only, no subscription. Poll RecordedAt for newly added 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. Use the result to enrich analytics, screen flows for exchange or gambling exposure, or strip exchange and contract addresses out of "real user" metrics.
Rules that matter
- The
Addressfilter is mandatory. Every query must pinAddresswithisorin. 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". - Up to 100 addresses per query. Split larger lists into batches of 100.
- 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. - Labels can be corrected or removed, and old versions are not kept. An address returns one row per chain, per label, per recording pass, so the same label can recur with different
RecordedAtvalues. Fold that into a current view withlimitBy, shown below. When we find a wrong label we fix or delete it in place, so re-run a full lookup to pick up corrections.
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-22T07:53:03Z"
}
]
}
}
How to get only the current labels
Because the same label can come back once per recording pass, 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, such as 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, with 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
}
}
}
This window catches newly added labels only. Labels can be corrected or removed when we find
errors, and earlier versions are not kept. A label we fix can keep its old RecordedAt, so it
never enters the window, and a label we remove simply disappears. To catch corrections, re-run a
full lookup on the addresses you track from time to time and compare it with what you stored.
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:
| Filter | Operators | Notes |
|---|---|---|
Address | is, in only | Mandatory, max 100 in in. No negation, so you cannot exclude addresses server-side. |
Chain | is, in, not, notIn, like, includes, startsWith, … | Exact chain slugs, listed in the table below. |
Label: {Type: …} | full string set | e.g. {is: "cex-hot-wallet"} |
Label: {Value: …} | full string set | e.g. {startsWith: "binance"} |
RecordedAt | since, till, after, before, is, plus _relative variants | Standard DateTime filters. |
any | list of sub-filters | OR-combinator across conditions. |
Response fields
| Field | Type | Meaning |
|---|---|---|
Address | String | The queried address, exactly as stored (EVM addresses lowercase). |
Chain | String | Chain this label applies to. One address can map to many chains. |
Label.Type | String | Label category, listed below. |
Label.Value | String | Entity slug within the category, e.g. binance-hot-1, bybit-hot, wavax, banned-by-usdt. Numbered suffixes distinguish instances of the same entity. |
RecordedAt | DateTime | When the row was added to Bitquery's label table. Most Bitcoin labels carry 28 May 2026, the date of the first bulk load, so it does not tell you when an address was first identified. A corrected label can keep its old RecordedAt. |
Supported chains
Chain values are plain slugs and the set grows as coverage expands. Verified live:
| Ecosystem | Chain values |
|---|---|
| EVM | ethereum, bsc, polygon, arbitrum, base, avalanche-c, fantom, ethpow |
| Non-EVM | bitcoin, 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.Type | Meaning | Example Value |
|---|---|---|
cex-hot-wallet | Exchange-operated hot wallet | binance-hot-1, bybit-hot |
cex-cold-wallet | Exchange cold storage | binance-cold |
cex-deposit-address | Per-user deposit address swept to an exchange | coinex-deposit |
token-contract | A token's contract or mint address | usdt, wavax, wbnb |
token-clone | Contract imitating a well-known token | clone-wmatic-2 |
contract | General smart-contract tag | uniswap, bridge, stablecoin |
gambling | Gambling platform wallet | stake-com-hot |
issuer-blocked-usdt | Frozen or blacklisted by Tether | banned-by-usdt |
issuer-blocked-usdc | Frozen or blacklisted by Circle | banned-by-usdc |
Limits and common errors
- Missing
Addressfilter. The query fails with"Labels query requires a Address filter in the where clause". AChainorLabelfilter alone does not satisfy it. - Wrong casing. A checksummed EVM address silently returns zero rows. Lowercase before querying.
- No negation on
Address.not/notInaren't part of theAddressfilter. Exclusion belongs client-side, or inChain/Labelfilters, which do support it. - Subscriptions.
subscription { Metadata { … } }is rejected; the cube exists only underquery. - Empty result is not an error. Unlabeled addresses, and an empty
in: [], return an empty list. limit/limitByandorderBybehave as on every other cube.
Pick the right query
| You need | Use |
|---|---|
| Who is behind one address | Address: {is: …} |
| Enrich a list of ≤100 addresses | Address: {in: …} with limitBy: {by: [Address, Chain, Label_Type], count: 1} |
| Exchange-wallet screening | Label: {Type: {in: ["cex-hot-wallet", "cex-cold-wallet", "cex-deposit-address"]}} |
| Fake-token checks | Label: {Type: {is: "token-clone"}} |
| Sanctions and issuer-freeze screening | Label: {Type: {startsWith: "issuer-blocked"}} |
| Newly added labels | Poll with RecordedAt: {since: …}, plus a periodic full lookup for corrections |
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 across EVM chains, Solana, Bitcoin and Tron. Entities include exchange hot and cold wallets, per-user deposit addresses, token contracts and clones, gambling platforms, and addresses frozen by stablecoin issuers.
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 recorded in passes, and the same label can come back with a new RecordedAt value on a later pass. 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. Labels can also be corrected or removed when we find errors, and earlier versions are not kept.
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 newly added labels, poll the addresses you track with a RecordedAt since filter and keep the high-water mark on your side. That filter does not flag corrections. A label we fix can keep its old RecordedAt, and a label we remove simply disappears, so re-run a full lookup on those addresses from time to time.
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.
Related
- DEXTrades cube: trade activity whose maker and taker addresses you can label
- Transfers cube: transfer counterparties to enrich
- Balances & Holders cubes: what the addresses you identified actually hold
- Filtering, Sorting, Limits: the query mechanics used above
Ready to run this in production?
Get an API key and run these queries in minutes, or talk to us about plans and enterprise delivery.