> ## Documentation Index
> Fetch the complete documentation index at: https://docs.range.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Profile an address

> Combine exact balance strings, address attribution, and the fields returned by the v2 funding lookup.

Build a profile containing balances, label and entity fields, and a v2 funding record for one Solana address.

## Prerequisites

* An API key with the **Data API** scope, exported as `RANGE_API_KEY`. Use the [shared quickstart](https://docs.range.org/introduction/quickstart.md) for key setup and Bearer authentication.
* Python 3.10 or later and `httpx` for the complete script: `python -m pip install httpx`.
* Address `2kkSm4in18qXS1drfBvdpEULaiJ6k1ovXJd6T7aAzX2x` on `solana`. See [Cross-Chain Lookup](https://docs.range.org/data-api/guides/cross-chain-lookup.md) for tracing transfers.
* The Solana USDC mint `EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v`, also listed in [Test inputs](https://docs.range.org/resources/test-inputs.md). The balance request supplies this mint; the recorded response also contains other tokens.
* This sequence uses `/v1/address/balance`, `/v1/address`, and `/v2/address/fundedBy`. Check [coverage](https://docs.range.org/data-api/product-info/coverage.md) for each operation.

## 1. Read balances without losing precision

Call [GET /v1/address/balance](https://docs.range.org/data-api/reference/v1/get-address-balance.md).

```bash cURL theme={null}
curl --get "https://api.range.org/v1/address/balance" \
  --header "Authorization: Bearer $RANGE_API_KEY" \
  --data-urlencode "address=2kkSm4in18qXS1drfBvdpEULaiJ6k1ovXJd6T7aAzX2x" \
  --data-urlencode "network=solana" \
  --data-urlencode "network_type=solana" \
  --data-urlencode "token_contracts=EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
```

Recorded HTTP 200 on October 21, 2026, abbreviated:

```json theme={null}
{
  "lookup_status": "ok",
  "reason": null,
  "balances": {
    "native_tokens": [
      {
        "base_amount": "2996116010",
        "decimals": 9,
        "denom": "So11111111111111111111111111111111111111112",
        "symbol": "SOL",
        "usd_string": "318.0602617983764097754817",
        "amount_string": "2.99611601"
      }
    ],
    "tokens": [
      {
        "base_amount": "1286944646",
        "decimals": 6,
        "denom": "9BEcn9aPEmhSPbPQeFGjidRiEKki46fVQDyPpSQXPA2D",
        "symbol": "jlUSDC",
        "usd_string": "1370.9950786205436303354476",
        "amount_string": "1286.944646"
      }
    ]
  }
}
```

The recorded lookup is `ok`. Preserve `base_amount` and `amount_string` as strings when storing or displaying the profile. Preserve `usd_string` as a string when available; this valuation is optional and can be absent or null. Keep missing or null valuations distinct from zero, as described in [Quantities and USD values](https://docs.range.org/data-api/product-info/working-with-responses.md#quantities-and-usd-values). Keep `decimals` and `denom` with each balance. Continue with the same address and network.

## 2. Read label and entity attribution

Use [GET /v1/address](https://docs.range.org/data-api/reference/v1/get-address.md) for attribution without adding the risk score returned by the separate address-info operation.

```bash cURL theme={null}
curl --get "https://api.range.org/v1/address" \
  --header "Authorization: Bearer $RANGE_API_KEY" \
  --data-urlencode "address=2kkSm4in18qXS1drfBvdpEULaiJ6k1ovXJd6T7aAzX2x" \
  --data-urlencode "network=solana"
```

Recorded HTTP 200 on October 21, 2026:

```json theme={null}
{
  "address": "2kkSm4in18qXS1drfBvdpEULaiJ6k1ovXJd6T7aAzX2x",
  "ecosystem": "solana",
  "network": "solana",
  "name_tag": "",
  "category": "",
  "address_role": "",
  "entity": "",
  "entity_name": "",
  "attributes": {},
  "tags": [],
  "entity_icon_url": null,
  "malicious": false,
  "is_validator": false
}
```

This response identifies the requested address and network, while `name_tag`, `entity`, and `entity_name` are empty strings. Record those as missing attribution. An empty label does not mean the address lookup failed or establish who controls the address. Continue with the same input pair.

## 3. Read the v2 funding record

Call [GET /v2/address/fundedBy](https://docs.range.org/data-api/reference/v2/get-address-funded-by.md).

```bash cURL theme={null}
curl --get "https://api.range.org/v2/address/fundedBy" \
  --header "Authorization: Bearer $RANGE_API_KEY" \
  --data-urlencode "address=2kkSm4in18qXS1drfBvdpEULaiJ6k1ovXJd6T7aAzX2x" \
  --data-urlencode "network=solana"
```

Recorded HTTP 200 on October 21, 2026, abbreviated:

```json theme={null}
{
  "address": {
    "address": "0xbb00ed346b514c510a39a1237267bb11ed1c4f4a",
    "network": "base",
    "tags": []
  },
  "network": "base",
  "tx_hash": "0x50582e4ea5c1a839c82747960b56c04f9588572b15f9995bd495ad9b876671aa",
  "time": "2026-07-15T05:09:31.000Z",
  "amount": "10000",
  "token": "USDC"
}
```

Keep `address.address`, `address.network`, `network`, `tx_hash`, `time`, `amount`, and `token` as the returned funding record. Here, both returned network fields are `base`, even though the lookup input is `solana`. They are not echoes of the requested network. Preserve the amount string `"10000"`.

This profile records what v2 returns. It does not establish an original funder, and it makes no equivalence claim about the v1 funding operation.

### A lookup with no funding record

The shared CCTP program input produces a different outcome:

```bash cURL theme={null}
curl --get "https://api.range.org/v2/address/fundedBy" \
  --header "Authorization: Bearer $RANGE_API_KEY" \
  --data-urlencode "address=CCTPiPYPc6AsJuwueEnWgSgucamXDZwBd53dQ11YiKX3" \
  --data-urlencode "network=solana"
```

Recorded HTTP 404 on October 21, 2026:

```json theme={null}
{
  "statusCode": 404,
  "message": "No funder found for this address"
}
```

Store this as no funding record returned by this lookup. It does not prove that the address was never funded. The script below uses the successful input and stops on an HTTP error.

## Complete script

Save this as `profile_address.py`, then run `python profile_address.py` with `RANGE_API_KEY` set. It retains balance and funding strings without converting them to floating point.

```python theme={null}
import json
import os
import random
import time

import httpx


def get(client, path, params=None):
    for attempt in range(5):
        response = client.get(path, params=params)
        if response.status_code != 429 or attempt == 4:
            response.raise_for_status()
            return response.json()
        # No Retry-After header is sent, so back off exponentially with jitter.
        time.sleep(min(2 ** attempt, 30) + random.uniform(0, 1))


ADDRESS = "2kkSm4in18qXS1drfBvdpEULaiJ6k1ovXJd6T7aAzX2x"
NETWORK = "solana"
USDC_MINT = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"

with httpx.Client(
    base_url="https://api.range.org",
    headers={
        "Authorization": f"Bearer {os.environ['RANGE_API_KEY']}",
        "User-Agent": "curl/8.4.0",
        "Accept": "application/json",
    },
    timeout=60,
) as client:
    balance = get(client, "/v1/address/balance", {
        "address": ADDRESS, "network": NETWORK, "network_type": "solana",
        "token_contracts": USDC_MINT,
    })
    if balance["lookup_status"] != "ok":
        raise RuntimeError(json.dumps({
            "lookup_status": balance["lookup_status"], "reason": balance["reason"],
        }))
    lines = balance["balances"]["native_tokens"] + balance["balances"]["tokens"]
    exact_balances = [{
        "denom": line["denom"], "base_amount": line["base_amount"],
        "decimals": line["decimals"], "amount_string": line["amount_string"],
        "usd_string": line.get("usd_string"),
    } for line in lines]
    identity = get(client, "/v1/address", {"address": ADDRESS, "network": NETWORK})
    assert identity["address"] == ADDRESS and identity["network"] == NETWORK
    funding = get(client, "/v2/address/fundedBy", {
        "address": ADDRESS, "network": NETWORK,
    })
    print(json.dumps({
        "address": identity["address"], "network": identity["network"],
        "lookup_status": balance["lookup_status"], "balances": exact_balances,
        "label": identity.get("name_tag"), "entity": identity.get("entity"),
        "entity_name": identity.get("entity_name"),
        "funding_record": {
            "address": funding["address"]["address"],
            "address_network": funding["address"]["network"],
            "network": funding["network"], "tx_hash": funding["tx_hash"],
            "time": funding["time"], "amount": funding["amount"],
            "token": funding["token"],
        },
    }, indent=2))
```

The script succeeds when the balance lookup is `ok`, the attribution response identifies the requested address and network, and the v2 funding lookup returns a record. For this sample, the profile has empty attribution fields and a funding record on `base`; balances can change.

See [Working with responses](https://docs.range.org/data-api/product-info/working-with-responses.md) for response conventions, [coverage](https://docs.range.org/data-api/product-info/coverage.md) for operation and network availability, and [Errors and rate limits](https://docs.range.org/resources/errors-and-rate-limits.md) for failures and retry guidance.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.