> For the complete documentation index, see [llms.txt](https://help.multisig.ledger.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://help.multisig.ledger.com/guides/api-guides/1.-querying-safe-data.md).

# 1. Querying Safe Data

### Shared configuration

All tutorials use the same Transaction Service base URL pattern:

```http
https://app.multisig.ledger.com/api/safe-transaction-service/{chainId}
```

### What you'll learn

* Initialize the Safe API Kit against the Ledger Enterprise Multisig Transaction Service.
* Query Safe info, creation data, balances, and transaction history.
* List delegates and retrieve the next available nonce.
* Understand which endpoints the SDK wraps and which require direct `fetch.`

### Prerequisites

* **Node.js 18+** and a package manager (npm, pnpm, or yarn)
* **A Safe deployed on a supported chain,** you can use any existing Safe
* No private key or API key is needed for read-only queries.
* Install the SDK:

```bash
npm install @safe-global/api-kit @safe-global/types-kit
```

### Configuration

All examples in this tutorial use the Ledger Enterprise Multisig Transaction Service. This is the Ledger-hosted backend, transactions and Safes indexed here appear in the [Ledger Enterprise Multisig UI](https://app.multisig.ledger.com). It is **not** the public Safe Transaction Service.

{% tabs %}
{% tab title="TypeScript" %}

```typescript
import SafeApiKitModule from "@safe-global/api-kit";

// ESM interop-safe constructor resolution (required in some runtimes)
const SafeApiKit =
  typeof SafeApiKitModule === "function"
    ? SafeApiKitModule
    : (SafeApiKitModule as unknown as { default: typeof SafeApiKitModule }).default;

const CHAIN_ID = 11155111n; // Sepolia
const TX_SERVICE_URL = `https://app.multisig.ledger.com/api/safe-transaction-service/${CHAIN_ID}`;
const SAFE_ADDRESS = "0xYourSafeAddress";

const apiKit = new SafeApiKit({
  chainId: CHAIN_ID,
  txServiceUrl: TX_SERVICE_URL,
});
```

{% endtab %}

{% tab title="Python" %}

```python
import requests

CHAIN_ID = 11155111  # Sepolia
TX_SERVICE_URL = f"https://app.multisig.ledger.com/api/safe-transaction-service/{CHAIN_ID}"
SAFE_ADDRESS = "0xYourSafeAddress"

session = requests.Session()
session.headers.update({"accept": "application/json"})
```

{% endtab %}

{% tab title="curl" %}

```bash
export CHAIN_ID=11155111  # Sepolia
export TX_SERVICE_URL="https://app.multisig.ledger.com/api/safe-transaction-service/${CHAIN_ID}"
export SAFE_ADDRESS="0xYourSafeAddress"
```

{% endtab %}
{% endtabs %}

**Supported chains:** Ethereum (1), Optimism (10), BSC (56), Polygon (137), Base (8453), Arbitrum (42161), Sepolia (11155111).

Replace the `CHAIN_ID` with the numeric chain ID for your target network. The URL pattern is always:

```http
https://app.multisig.ledger.com/api/safe-transaction-service/{chainId}
```

### Steps

{% stepper %}
{% step %}

### Get Safe info

Retrieve the on-chain configuration of a Safe: owners, threshold, nonce, version, modules, and guard.

{% tabs %}
{% tab title="TypeScript" %}

```typescript
const safeInfo = await apiKit.getSafeInfo(SAFE_ADDRESS);

console.log("Address:", safeInfo.address);
console.log("Threshold:", safeInfo.threshold);
console.log("Owners:", safeInfo.owners);
console.log("Nonce:", safeInfo.nonce);
console.log("Version:", safeInfo.version);
console.log("Modules:", safeInfo.modules);
console.log("Fallback handler:", safeInfo.fallbackHandler);
console.log("Guard:", safeInfo.guard);
```

{% endtab %}

{% tab title="Python" %}

```python
resp = session.get(f"{TX_SERVICE_URL}/v1/safes/{SAFE_ADDRESS}/")
resp.raise_for_status()
safe_info = resp.json()

print("Address:", safe_info["address"])
print("Threshold:", safe_info["threshold"])
print("Owners:", safe_info["owners"])
print("Nonce:", safe_info["nonce"])
print("Version:", safe_info.get("version"))
print("Modules:", safe_info.get("modules", []))
print("Fallback handler:", safe_info.get("fallbackHandler"))
print("Guard:", safe_info.get("guard"))
```

{% endtab %}

{% tab title="curl" %}

```bash
curl -sS -X GET \
  "${TX_SERVICE_URL}/v1/safes/${SAFE_ADDRESS}/" \
  -H "accept: application/json"
```

{% endtab %}
{% endtabs %}

> **REST API:** `GET /v1/safes/{address}/`\
> Example: `GET https://app.multisig.ledger.com/api/safe-transaction-service/11155111/v1/safes/0xYourSafe/`
> {% endstep %}

{% step %}

### Get Safe creation info

Find out when and how a Safe was deployed, the creator address, factory, and deployment transaction hash.

{% tabs %}
{% tab title="TypeScript" %}

```typescript
const creation = await apiKit.getSafeCreationInfo(SAFE_ADDRESS);

console.log("Created:", creation.created);
console.log("Creator:", creation.creator);
console.log("Tx hash:", creation.transactionHash);
console.log("Factory:", creation.factoryAddress);
```

{% endtab %}

{% tab title="Python" %}

```python
resp = session.get(f"{TX_SERVICE_URL}/v1/safes/{SAFE_ADDRESS}/creation/")
resp.raise_for_status()
creation = resp.json()

print("Created:", creation["created"])
print("Creator:", creation["creator"])
print("Tx hash:", creation["transactionHash"])
print("Factory:", creation["factoryAddress"])
```

{% endtab %}

{% tab title="curl" %}

```bash
curl -sS -X GET \
  "${TX_SERVICE_URL}/v1/safes/${SAFE_ADDRESS}/creation/" \
  -H "accept: application/json"
```

{% endtab %}
{% endtabs %}

> **REST API:** `GET /v1/safes/{address}/creation/`
> {% endstep %}

{% step %}

### Get balances

Query native and token balances held by the Safe.

> **Important:** The API Kit does **not** expose a `getSafeBalances` method. Use a direct `fetch` call against the v2 balances endpoint.

{% tabs %}
{% tab title="TypeScript" %}

```typescript
const response = await fetch(`${TX_SERVICE_URL}/v2/safes/${SAFE_ADDRESS}/balances/`);
if (!response.ok) {
  throw new Error(`Failed to fetch balances: HTTP ${response.status}`);
}

const data = (await response.json()) as {
  count: number;
  results: Array<{
    tokenAddress: string | null;
    token: { symbol: string; decimals: number } | null;
    balance: string;
  }>;
};

function formatUnits(raw: string, decimals: number) {
  const value = BigInt(raw);
  const base = 10n ** BigInt(decimals);
  const whole = value / base;
  const fraction = (value % base).toString().padStart(decimals, "0").replace(/0+$/, "");
  return fraction ? `${whole}.${fraction}` : `${whole}`;
}

for (const entry of data.results) {
  const symbol = entry.token?.symbol ?? "ETH";
  const decimals = entry.token?.decimals ?? 18;
  const human = formatUnits(entry.balance, decimals);
  console.log(`${symbol}: ${human}`);
}
```

{% endtab %}

{% tab title="Python" %}

```python
resp = session.get(f"{TX_SERVICE_URL}/v2/safes/{SAFE_ADDRESS}/balances/")
resp.raise_for_status()
data = resp.json()

def format_units(raw: str, decimals: int) -> str:
    value = int(raw)
    base = 10 ** decimals
    whole = value // base
    frac = value % base
    if frac == 0:
        return str(whole)
    frac_str = str(frac).rjust(decimals, "0").rstrip("0")
    return f"{whole}.{frac_str}"

for entry in data["results"]:
    symbol = (entry.get("token") or {}).get("symbol") or "ETH"
    decimals = (entry.get("token") or {}).get("decimals") or 18
    print(f"{symbol}: {format_units(entry['balance'], int(decimals))}")
```

{% endtab %}

{% tab title="curl" %}

```bash
curl -sS -X GET \
  "${TX_SERVICE_URL}/v2/safes/${SAFE_ADDRESS}/balances/" \
  -H "accept: application/json"
```

{% endtab %}
{% endtabs %}

> **REST API:** `GET /v2/safes/{address}/balances/`
> {% endstep %}

{% step %}

### Get multisig transactions

List all multisig transactions (both executed and pending) for the Safe, ordered by nonce.

{% tabs %}
{% tab title="TypeScript" %}

```typescript
const txs = await apiKit.getMultisigTransactions(SAFE_ADDRESS);

console.log("Total:", txs.count);

for (const tx of txs.results.slice(0, 5)) {
  console.log("safeTxHash:", tx.safeTxHash);
  console.log("to:", tx.to);
  console.log("value:", tx.value);
  console.log("nonce:", tx.nonce);
  console.log("executed:", tx.isExecuted);
  console.log("successful:", tx.isSuccessful);
  console.log(`confirmations: ${tx.confirmations?.length ?? 0} / ${tx.confirmationsRequired}`);
}
```

{% endtab %}

{% tab title="Python" %}

```python
resp = session.get(f"{TX_SERVICE_URL}/v2/safes/{SAFE_ADDRESS}/multisig-transactions/?limit=5")
resp.raise_for_status()
txs = resp.json()

print("Total:", txs["count"])
for tx in txs["results"][:5]:
    print("safeTxHash:", tx["safeTxHash"])
    print("to:", tx["to"])
    print("value:", tx["value"])
    print("nonce:", tx["nonce"])
    print("executed:", tx["isExecuted"])
    print("successful:", tx.get("isSuccessful"))
    confirmations = (tx.get("confirmations") or [])
    print(f"confirmations: {len(confirmations)} / {tx['confirmationsRequired']}")
```

{% endtab %}

{% tab title="curl" %}

```bash
curl -sS -X GET \
  "${TX_SERVICE_URL}/v2/safes/${SAFE_ADDRESS}/multisig-transactions/?limit=5" \
  -H "accept: application/json"
```

{% endtab %}
{% endtabs %}

> **REST API:** `GET /v2/safes/{address}/multisig-transactions/`
> {% endstep %}

{% step %}

### Get pending transactions

Filter to only transactions that have been proposed but not yet executed.

{% tabs %}
{% tab title="TypeScript" %}

```typescript
const pending = await apiKit.getPendingTransactions(SAFE_ADDRESS);

console.log("Pending count:", pending.count);

for (const tx of pending.results) {
  console.log("safeTxHash:", tx.safeTxHash);
  console.log("nonce:", tx.nonce);
  console.log(`confirmations: ${tx.confirmations?.length ?? 0} / ${tx.confirmationsRequired}`);
}
```

{% endtab %}

{% tab title="Python" %}

```python
# REST equivalent: filter multisig txs to only not executed.
resp = session.get(
    f"{TX_SERVICE_URL}/v2/safes/{SAFE_ADDRESS}/multisig-transactions/?executed=false"
)
resp.raise_for_status()
pending = resp.json()

print("Pending count:", pending["count"])
for tx in pending["results"]:
    confirmations = (tx.get("confirmations") or [])
    print("safeTxHash:", tx["safeTxHash"])
    print("nonce:", tx["nonce"])
    print(f"confirmations: {len(confirmations)} / {tx['confirmationsRequired']}")
```

{% endtab %}

{% tab title="curl" %}

```bash
curl -sS -X GET \
  "${TX_SERVICE_URL}/v2/safes/${SAFE_ADDRESS}/multisig-transactions/?executed=false" \
  -H "accept: application/json"
```

{% endtab %}
{% endtabs %}
{% endstep %}

{% step %}

### Get all transactions (including incoming transfers)

Returns every transaction type: multisig, module, and incoming transfers (ETH and ERC-20 received by the Safe).

{% tabs %}
{% tab title="TypeScript" %}

```typescript
const all = await apiKit.getAllTransactions(SAFE_ADDRESS, { limit: 5 });

console.log("Total:", all.count);

for (const tx of all.results) {
  const record = tx as Record<string, unknown>;
  console.log("type:", record.txType);
  console.log("hash:", record.transactionHash ?? record.txHash);
}
```

{% endtab %}

{% tab title="Python" %}

```python
resp = session.get(f"{TX_SERVICE_URL}/v2/safes/{SAFE_ADDRESS}/all-transactions/?limit=5")
resp.raise_for_status()
all_txs = resp.json()

print("Total:", all_txs["count"])
for tx in all_txs["results"]:
    # Different types return different hash fields.
    tx_type = tx.get("txType")
    tx_hash = tx.get("transactionHash") or tx.get("txHash")
    print("type:", tx_type)
    print("hash:", tx_hash)
```

{% endtab %}

{% tab title="curl" %}

```bash
curl -sS -X GET \
  "${TX_SERVICE_URL}/v2/safes/${SAFE_ADDRESS}/all-transactions/?limit=5" \
  -H "accept: application/json"
```

{% endtab %}
{% endtabs %}

> **REST API:** `GET /v2/safes/{address}/all-transactions/`
> {% endstep %}

{% step %}

### Get delegates

List all delegates authorized to propose transactions on behalf of Safe owners.

{% tabs %}
{% tab title="TypeScript" %}

```typescript
const delegates = await apiKit.getSafeDelegates({
  safeAddress: SAFE_ADDRESS,
});

console.log("Delegate count:", delegates.count);

for (const d of delegates.results) {
  console.log("delegate:", d.delegate);
  console.log("delegator:", d.delegator);
  console.log("label:", d.label);
}
```

{% endtab %}

{% tab title="Python" %}

```python
resp = session.get(f"{TX_SERVICE_URL}/v2/delegates/?safe={SAFE_ADDRESS}")
resp.raise_for_status()
delegates = resp.json()

print("Delegate count:", delegates["count"])
for d in delegates["results"]:
    print("delegate:", d["delegate"])
    print("delegator:", d["delegator"])
    print("label:", d.get("label"))
```

{% endtab %}

{% tab title="curl" %}

```bash
curl -sS -X GET \
  "${TX_SERVICE_URL}/v2/delegates/?safe=${SAFE_ADDRESS}" \
  -H "accept: application/json"
```

{% endtab %}
{% endtabs %}

> **REST API:** `GET /v2/delegates/?safe={address}`
> {% endstep %}

{% step %}

### Get next nonce

Returns the next nonce to use when creating a new Safe transaction. This accounts for both executed and pending (queued) transactions.

{% tabs %}
{% tab title="TypeScript" %}

```typescript
const nextNonce = await apiKit.getNextNonce(SAFE_ADDRESS);

console.log("Next nonce:", nextNonce);
```

{% endtab %}

{% tab title="Python" %}

```python
resp = session.get(f"{TX_SERVICE_URL}/v1/safes/{SAFE_ADDRESS}/next-nonce/")
resp.raise_for_status()
next_nonce = resp.json()

print("Next nonce:", next_nonce)
```

{% endtab %}

{% tab title="curl" %}

```bash
curl -sS -X GET \
  "${TX_SERVICE_URL}/v1/safes/${SAFE_ADDRESS}/next-nonce/" \
  -H "accept: application/json"
```

{% endtab %}
{% endtabs %}
{% endstep %}
{% endstepper %}

### Key concepts

> **The Safe Transaction Service** is an off-chain backend that indexes on-chain Safe events and stores proposed (not-yet-executed) transactions. The Ledger Enterprise Multisig Transaction Service is Ledger's hosted instance of this service. When you query it, you get:
>
> * **On-chain state** (owners, threshold, nonce) synced from the blockchain
> * **Off-chain proposals** (pending transactions with their collected signatures)
> * **Full transaction history** including incoming transfers
>
> The API Kit is a TypeScript wrapper around the Transaction Service REST API. Most endpoints are covered, but some (like balances) require direct HTTP calls.

### Tips and pitfalls

* **`getSafeBalances` does not exist.** The API Kit (v2.5.7) does not wrap the balances endpoint. Use a direct `fetch()` call to `/v2/safes/{address}/balances/` as shown in step 3.
* **No API key required.** The Ledger Multisig Transaction Service does not enforce API keys for read operations. No rate-limiting headers are needed.
* **ESM import gotcha.** In some ESM runtimes, `import SafeApiKit from "@safe-global/api-kit"` throws `TypeError: SafeApiKit is not a constructor`. Use the interop-safe constructor resolution shown in Configuration.
* **Indexing lag.** After an on-chain transaction executes, the Transaction Service may take 10–60 seconds to index the new state. If you query immediately after execution and see stale data, wait and retry.
* **Paginated responses.** Methods like `getMultisigTransactions` return paginated results. Use the `limit` and `offset` parameters (or `next`/`previous` URLs in the response) to page through large result sets.

### Next steps

* Tutorial index
* **Transaction Lifecycle:** Create, sign, propose, and execute a Safe transaction
* **Verified runnable source:** `playground/src/phase1-read.ts`
* Troubleshooting
* [Safes API Reference](https://ledger-4.gitbook.io/ledger-multisig/reference/safes)
* [Transactions API Reference](https://ledger-4.gitbook.io/ledger-multisig/reference/transactions)
* [Ledger Multisig Overview](https://ledger-4.gitbook.io/ledger-multisig)
