# Resolve token metadata (curated list, then on-chain)

Endpoint reference for the 1delta API. Index: https://docs.1delta.io/llms.txt · every endpoint: https://docs.1delta.io/llms-full.txt

---

### GET /v1/data/token/metadata

- operationId: `resolve-token-metadata-curated-list-then-on-chain`
- docs: https://docs.1delta.io/1delta-api/resolve-token-metadata-curated-list-then-on-chain/
- markdown: https://docs.1delta.io/1delta-api/resolve-token-metadata-curated-list-then-on-chain.md
- tags: Token

Resolve token metadata (curated list, then on-chain)

Resolves arbitrary token addresses to `symbol` / `name` / `decimals`.

Unlike `/v1/data/token/list`, which serves only the **curated** list, this endpoint falls back to reading the ERC-20 contract directly — so a token nobody has curated can still be resolved, displayed and traded.

**Resolution order**
1. The chain's curated token list (`source: "list"`, `verified: true`)
2. An on-chain `name`/`symbol`/`decimals` read (`source: "onchain"`, `verified: false`)

**`assetGroup` is `null` for every on-chain result — deliberately.** `assetGroup` is the key the price layer joins on, and `name`/`symbol` read off an arbitrary contract are attacker-controlled strings. A token that names itself `USDC` / `USD Coin` would otherwise join to the real USDC price and render a worthless balance at $1. An uncurated token therefore carries **no price**, and `verified: false` says why. Render it without a USD value.

**An address with no `decimals()` is NOT returned.** It appears in `unresolved` instead. Decimals are never defaulted to 18 — every amount is scaled by that number, so a guessed scale is a wrong transaction. `unresolved` lets a caller distinguish "not a token on this chain" from "the lookup failed" (which is a 5xx).

Pre-standard tokens that return `bytes32` from `name()`/`symbol()` (MKR and friends) are decoded correctly.

**Limits:** at most 50 addresses per request; duplicates and casing are normalized.

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `chainId` | query | string | yes | Chain ID to resolve on See the `ChainId` schema for the full set of supported chains. |
| `assets` | query | string | yes | Comma-separated token addresses (max 50) |

**Response `200`** — Resolved token metadata, plus the addresses that are not ERC-20s

| Field | Type | Description |
| --- | --- | --- |
| `success` | true |  |
| `data` | object | Informational payload. `null` when the endpoint only builds calldata. |
| `actions` | null |  |

**Example response**

```json
{
  "success": true,
  "data": {}
}
```

**Response `400`** — Validation error

**Response `429`** — Rate limited. Unauthenticated callers share a per-IP budget; send an `x-api-key` header to lift it. Retry with exponential backoff.

**Response `500`** — Unexpected server error. Safe to retry with backoff.

**Response `502`** — An upstream data source or protocol origin failed (`error.code` is `ORIGIN_FAILED`). `error.details` carries the per-origin status. This is also what a missing or malformed required parameter currently returns, rather than a 400.
