> For the complete documentation index, see [llms.txt](https://docs.talus.network/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.talus.network/talus-docs-v2.1.0/guides/nexus-api/tutorial/02-read-the-network.md).

# 2️⃣ Read the Network

### Read the Network

{% hint style="info" %}
**Audience:** Someone with a provider-issued API key held in the server-side `NEXUS_API_KEY` environment.

**Goal:** Make the exact REST calls the Run Watcher will render — list executions, filter them, walk a page boundary, drill into one run — and read one error on purpose.
{% endhint %}

```mermaid
sequenceDiagram
  participant Watcher as dApp or Run Watcher
  participant Relay as Same-origin relay
  participant API as Nexus API
  participant Indexer as Indexed event log
  Watcher->>Relay: GET /executions with filters
  Relay->>API: Add server-side API key and forward filters
  API->>Indexer: Read page and apply scope/filter rules
  Indexer-->>API: items and next_token
  API-->>Relay: List envelope
  Relay-->>Watcher: List envelope
  loop Until next_token is null
    Watcher->>Relay: GET /executions?page_token=next_token
    Relay->>API: Forward page cursor with server-side key
    API-->>Relay: Next page
    Relay-->>Watcher: Next page
  end
  Watcher->>Relay: GET detail or payment-ledger
  Relay->>API: Forward resource request
  API-->>Relay: Detail or explicit error envelope
  Relay-->>Watcher: Detail or explicit error envelope
```

The sequence keeps the API boundary read-only: the watcher pages an indexed projection, then drills into the same execution's sub-resources. A `401`, `403`, `404`, `429`, or envelope-free `408`/`413` is a response to handle at the HTTP boundary, not a mutation of Nexus state.

### Keep the API key out of curl arguments

Use a mode-`0600` curl configuration file rather than expanding `NEXUS_API_KEY` into a header argument. Hidden input keeps the terminal from echoing the value; the cleanup trap removes the file and unsets the shell variable, while the key remains visible to same-user process inspection for the lifetime of the shell or child process:

```bash
umask 077
API_CURL_CONFIG="$(mktemp)"
chmod 600 "$API_CURL_CONFIG"
cleanup_api_key() {
  rm -f -- "$API_CURL_CONFIG"
  unset NEXUS_API_KEY API_CURL_CONFIG
}
trap cleanup_api_key EXIT
read -r -s NEXUS_API_KEY
printf '\n' >&2
printf 'header = "x-api-key: %s"\n' "$NEXUS_API_KEY" >"$API_CURL_CONFIG"
node --input-type=module - "$API_CURL_CONFIG" <<'NODE'
import { statSync } from "node:fs";
if ((statSync(process.argv[2]).mode & 0o777) !== 0o600) process.exit(1);
NODE
```

Do not type `export NEXUS_API_KEY='...'`, paste a real key into a command, or commit the configuration file; every request below passes only the configuration path to curl.

#### Authenticate a request

Every read endpoint accepts the `x-api-key` header; the configuration file supplies it without putting the secret in curl's argv:

```bash
curl --config "$API_CURL_CONFIG" -sS "$NEXUS_API_URL/executions"
```

#### The listing shape

Every list endpoint returns the same envelope — `items` plus pagination `metadata`:

```json
{
  "items": [
    {
      "object_id": "0x9f2c...",
      "dag_object_id": "0x41aa...",
      "status": "finished",
      "invoker": "0x7be1...",
      "created_at": "2026-08-19T14:03:22.101Z"
    }
  ],
  "metadata": { "total_items": 137, "next_token": 20 }
}
```

Pages default to 20 items and cap at 100. `next_token` is an opaque cursor: pass it back as `page_token` for the next page, and stop when it comes back `null`:

```bash
curl --config "$API_CURL_CONFIG" -sS "$NEXUS_API_URL/executions?page_size=100"
curl --config "$API_CURL_CONFIG" -sS "$NEXUS_API_URL/executions?page_size=100&page_token=100"
```

#### Filter to what you care about

List endpoints filter server-side; combine as many as you like. The executions family understands the run's whole context — its DAG, its invoker, its agent and skill, the task that scheduled it, a status, and an RFC3339 time window:

```bash
# Runs of one workflow that finished in the last day
curl --config "$API_CURL_CONFIG" -sS -G "$NEXUS_API_URL/executions" \
  --data-urlencode "dag_object_id=0x41aa..." \
  --data-urlencode "status=finished" \
  --data-urlencode "created_after=2026-08-19T00:00:00Z"
```

Filter values are exact: `status` takes the API's snake\_case vocabulary (`requested`, `running`, `pending_settlement`, `finished`, `failed`, …). An unknown value is rejected with HTTP 400 and the documented `INVALID_FILTER_VALUE` error envelope; it is not an empty projection.

#### Drill into one run

Everything an execution produced hangs off its object id as a sub-resource:

```bash
ID="0x9f2c..."
curl --config "$API_CURL_CONFIG" -sS "$NEXUS_API_URL/executions/$ID"                 # status + walk counters
curl --config "$API_CURL_CONFIG" -sS "$NEXUS_API_URL/executions/$ID/walks"           # each walk's path and state
curl --config "$API_CURL_CONFIG" -sS "$NEXUS_API_URL/executions/$ID/events"          # the run's event trail
curl --config "$API_CURL_CONFIG" -sS "$NEXUS_API_URL/executions/$ID/payment-ledger"  # what it cost, entry by entry
```

The Run Watcher will use exactly two of these calls: the filtered list for its table, and the per-execution detail when you click a row. The full sub-resource catalog — verdicts, failures, gas, submission failures — is in the [API reference](https://api.taluslabs.dev/docs).

#### Read an error on purpose

Ask for a resource your key has no scope for:

```bash
curl --config "$API_CURL_CONFIG" -si "$NEXUS_API_URL/tools" | head -20
```

```
HTTP/1.1 403 Forbidden
x-error-code: INSUFFICIENT_API_KEY_SCOPE

{"error_code":"INSUFFICIENT_API_KEY_SCOPE","description":"API key lacks the required scope"}
```

Most resource errors follow this contract: a machine-readable `error_code` (also mirrored in the `x-error-code` header when the error envelope is emitted) and a human-readable `description`. The codes worth branching on in a dApp are `401` missing/invalid key, `403` missing scope, `404` unknown object ID, and `429` an empty scope token bucket — back off and retry. The timeout/body-size/malformed-request layer is different: `408`, `413`, and malformed requests can be envelope-free, so branch on the HTTP status and `content-type`, capture `x-request-id` when present, and do not attempt to parse a missing JSON body.

| Response                | Body expectation                                                    | Safe client action                                                                             |
| ----------------------- | ------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| `4xx`/`5xx` with JSON   | `error_code` plus `description`; `x-error-code` may mirror the code | Branch on the code/status and show the description without treating it as on-chain state.      |
| `408 Request Timeout`   | The timeout layer may return no JSON envelope                       | Retry only under the service's idempotent read policy and preserve the request ID if present.  |
| `413 Payload Too Large` | The body may be empty or a deployment-specific plain response       | Reduce the request/page or payload before retrying; do not decode it as an API error object.   |
| Malformed request       | The parser may reject before the API envelope exists                | Fix serialization/headers and branch on status/content type rather than assuming `error_code`. |

{% hint style="success" %}
**Checkpoint.** You can do three things from muscle memory now: page a list to the end (`next_token: null`), narrow it with two filters at once, and read an emitted error envelope's `error_code` without looking at the prose. Envelope-free timeout, body-size, and malformed-request responses still require status/content-type branching. That is the entire REST surface — every other family works identically.
{% endhint %}

#### Close this step before opening the next

When the API reads are complete, clean this page's configuration and disarm its trap before the stream page installs its own cleanup. If a command fails first, the active `EXIT` trap still removes the configuration:

```bash
cleanup_api_key
trap - EXIT
unset -f cleanup_api_key
```

Next: [watch the network move instead of polling it](/talus-docs-v2.1.0/guides/nexus-api/tutorial/03-stream-events.md).


---

# 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 following URL with the `ask` and `goal` query parameters:

```
GET https://docs.talus.network/talus-docs-v2.1.0/guides/nexus-api/tutorial/02-read-the-network.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `build a script that syncs our docs to a CMS` lets GitBook tailor the answer to that use case.

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.
