> 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)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://help.multisig.ledger.com/guides/api-guides/1.-querying-safe-data.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
