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

# Scenario Analysis Guide

> Analyze how news events and macro shocks affect a portfolio using the Pulse API.

## Overview

The Pulse API lets you answer three core questions about a portfolio:

1. **"What would happen to my portfolio if X occurred?"** — Describe any scenario in plain English and get a probability-weighted impact analysis.
2. **"How does this Dow Jones article affect my holdings?"** — Fetch a pre-computed article scenario and calculate its portfolio exposure.
3. **"What happens if macro conditions change?"** — Run what-if analysis with specific macro factor shocks (USD strength, oil prices, etc.).

All workflows require a portfolio expressed as a map of `tilt_asset_id → weight`. This guide walks through resolving tickers, building portfolio weights, and calling the scenario and risk endpoints.

<Tip>
  Looking for the article-scenarios endpoint? The public Pulse route is `POST /api/v1/backchannel/scenarios/{id}`. See [Analyze a Pre-Computed Dow Jones Article](#step-5-analyze-a-pre-computed-dow-jones-article).
</Tip>

## Step 1: Resolve Tickers to Tilt Asset IDs

All portfolio-related endpoints accept weights keyed by **Tilt Asset ID** — an internal identifier for each security. Use the Search Tickers endpoint to resolve symbols.

```bash theme={null}
curl -X GET "https://api.tilt.io/api/v1/tickers/search?symbols=AAPL,MSFT,NVDA" \
  -H "X-Api-Key: YOUR_API_KEY"
```

Response:

```json theme={null}
{
  "count": 6,
  "results": [
    {
      "symbol": "AAPL",
      "name": "Apple, Inc.",
      "exchange": "NAS",
      "ticker_type": "common_stock",
      "tilt_asset_id": "MH33D6-R"
    },
    {
      "symbol": "AAPL",
      "name": "Apple, Inc.",
      "exchange": "TSE",
      "ticker_type": "dr",
      "tilt_asset_id": "W0R2RY-R"
    },
    ...
  ]
}
```

<Note>
  A symbol may return multiple results across exchanges (e.g. NAS and TSE listings). Filter by `ticker_type: "common_stock"` and the relevant exchange to get the primary US listing.
</Note>

Extract the `tilt_asset_id` from each result.

[View API Reference →](/api-reference/endpoint/search-tickers)

<Tip>
  You can resolve multiple symbols in a single call by passing a comma-separated list. Cache the mapping — tilt asset IDs are stable identifiers that don't change.
</Tip>

## Step 2: Build Portfolio Weights

Construct a `portfolio_weights` object mapping each `tilt_asset_id` to a decimal weight (summing to 1.0):

```json theme={null}
{
  "MH33D6-R": 0.40,
  "P8R3C2-R": 0.35,
  "K7TPSX-R": 0.25
}
```

<Note>
  Weights should sum to 1.0. If they don't, the API will still work but exposure calculations will be proportional to the weights provided.
</Note>

This `portfolio_weights` object is reused across all the endpoints below.

## Step 3: Scenario Portfolio Analysis

The simplest way to analyze portfolio impact. Describe any scenario in plain English and the API generates a probability tree of outcomes with portfolio-level returns.

### Recommended: Use `flat_scenarios` mode

We recommend using `flat_scenarios: true` for most integrations. This generates a clean, single-level structure where each scenario directly contains its macro shocks — no nested sub-scenarios to traverse. The result is simpler to parse and display.

```bash theme={null}
curl -X POST "https://tilt.io/api/v1/backchannel/risk/scenario_portfolio_analysis" \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "scenario_description": "China begins naval blockade of Taiwan, semiconductor supply chain completely severed",
    "portfolio_weights": {
      "MH33D6-R": 0.40,
      "P8R3C2-R": 0.35,
      "K7TPSX-R": 0.25
    },
    "flat_scenarios": true,
    "search_for_context": true,
    "include_security_details": true
  }'
```

### Key parameters

| Parameter | Type | Default | Description |
| - | - | - | - |
| `scenario_description` | string | — | Plain English description of the scenario to analyze. |
| `portfolio_weights` | object | — | Map of `tilt_asset_id` → weight. |
| `as_of_date` | string | today | Date for the analysis (YYYY-MM-DD). |
| `flat_scenarios` | boolean | `false` | **Recommended: `true`.** Generates flat scenarios where each scenario directly contains its macro shocks. When `false`, generates a two-level tree with sub-scenarios. |
| `search_for_context` | boolean | `true` | Search for relevant background articles to improve scenario quality. |
| `include_security_details` | boolean | `true` | Include per-security breakdown in each leaf scenario. |
| `aggregate_scenarios` | boolean | `false` | Collapse the scenario tree into summary stats (mean/std per top-level scenario). |
| `force_regenerate` | boolean | `false` | Bypass the scenario cache and regenerate from scratch. |

### Caching behavior

Scenario generation results are **automatically cached**. The first call for a given scenario generates fresh results (10–15 seconds). All subsequent calls with the same `scenario_description`, `as_of_date`, `search_for_context`, and `flat_scenarios` combination return cached results in **under 1 second**.

The cache is keyed on these four parameters. The portfolio weights are **not** part of the cache key — portfolio impact computation happens after scenario generation, so a single cached scenario tree can be reused for any portfolio instantly.

<Tip>
  For a Dow Jones article that has already been processed, do not send the article text to this endpoint. Use the pre-computed article scenario endpoint in Step 5. Reserve `scenario_portfolio_analysis` for hypothetical scenarios and articles that do not have a pre-computed scenario.
</Tip>

If you only need to rank articles by their relevance to a portfolio, use [Article Portfolio Relevance](/api-reference/backchannel/article-portfolio-relevance). It returns a comparable score without generating the scenario tree or per-security scenario returns.

Use `force_regenerate: true` to bypass the cache if you need fresh results for an already-cached scenario.

### Response structure

The response includes an AI-generated scenario tree, leaf-level portfolio impacts, and a probability-weighted expected return:

```json theme={null}
{
  "success": true,
  "scenario_description": "China begins naval blockade of Taiwan...",
  "as_of_date": "2026-03-10",
  "expected_portfolio_return_pct": -0.0607,
  "cache_hit": true,
  "portfolio_summary": {
    "num_scenarios": 3,
    "num_leaf_scenarios": 3,
    "num_securities": 3
  },
  "scenario_tree": [
    {
      "probability": 40,
      "title": "Full blockade with military escalation",
      "description": "...",
      "macro_shocks": [
        {"indicator": "VIXCLS", "impact": 0.25},
        {"indicator": "rbics_55", "impact": -0.15}
      ]
    }
  ],
  "leaf_scenarios": [
    {
      "path": ["Full blockade with military escalation"],
      "joint_probability_pct": 40,
      "portfolio_return_pct": -0.12,
      "portfolio_expected_return_pct": -0.12,
      "contribution_to_expected_return_pct": -0.048,
      "top_movers": [
        {"tilt_id": "K7TPSX-R", "ticker": "NVDA", "expected_return_pct": -0.22, "contribution_to_portfolio_pct": -0.055},
        {"tilt_id": "P8R3C2-R", "ticker": "MSFT", "expected_return_pct": 0.01, "contribution_to_portfolio_pct": 0.004}
      ],
      "security_details": [
        {
          "tilt_id": "MH33D6-R",
          "ticker": "AAPL",
          "company_name": "Apple, Inc.",
          "weight": 0.40,
          "expected_return_pct": -0.1559,
          "contribution_to_portfolio_pct": -0.0624,
          "factor_contributions": {}
        }
      ]
    }
  ],
  "context_articles": ["Taiwan Export Growth Lost Momentum in February", "..."],
  "context_article_ids": ["a1b2c3d4-...", "e5f6g7h8-..."],
  "warnings": null,
  "generated_at": "2026-03-10T14:30:00"
}
```

<Note>
  When `flat_scenarios: true`, each scenario in `scenario_tree` contains its `macro_shocks` directly (no `sub_scenarios`), and `leaf_scenarios` has one entry per scenario. When `flat_scenarios: false` (default), scenarios have nested `sub_scenarios` and `leaf_scenarios` contains one entry per sub-scenario with joint probabilities.
</Note>

**Key fields:**

| Field | Description |
| - | - |
| `expected_portfolio_return_pct` | Probability-weighted expected portfolio return across all leaf scenarios, in percentage points (1.0 = 1%). |
| `portfolio_summary` | Counts of scenarios, leaf scenarios, and securities analyzed. |
| `scenario_tree` | The full probability tree. Flat when `flat_scenarios: true`, nested with `sub_scenarios` when `false`. |
| `leaf_scenarios` | Flattened leaf nodes with joint probabilities, portfolio returns, top movers, and per-security details. |
| `leaf_scenarios[].portfolio_return_pct` | Portfolio return for this specific leaf scenario. |
| `leaf_scenarios[].contribution_to_expected_return_pct` | This leaf's contribution to the overall expected return (joint\_probability \* portfolio\_return). |
| `leaf_scenarios[].top_movers` | Top 3 gainers and top 3 losers by contribution to portfolio return. |
| `context_articles` | Headlines of articles used as background context (when `search_for_context` is true). |
| `context_article_ids` | Point IDs of context articles used as background context. |
| `cache_hit` | `true` if the scenario tree was served from cache, `false` if freshly generated. |
| `warnings` | Array of warnings (e.g., unknown tilt\_ids skipped). `null` if no warnings. |

<Note>
  This endpoint uses AI to generate scenarios on first encounter (\~10–15 seconds). Cached scenarios return in under 1 second. For instant results on pre-computed news scenarios, use the Search Scenarios endpoint below instead.
</Note>

[View API Reference →](/api-reference/backchannel/scenario-portfolio-analysis)

### Flat vs. hierarchical scenarios

| | `flat_scenarios: true` | `flat_scenarios: false` (default) |
| - | - | - |
| **Structure** | 3 top-level scenarios, each with `macro_shocks` directly | 3–5 top-level scenarios, each with one or more `sub_scenarios` containing `macro_shocks` |
| **Leaf count** | 3 leaves (one per scenario) | Variable; one leaf per sub-scenario |
| **Best for** | Simple UIs, mobile cards, quick summaries | Detailed analysis, drill-down interfaces |
| **Parse complexity** | Flat list — no nesting to traverse | Two levels of nesting |

## Step 4: Search News Scenarios with Portfolio Exposure

Search for curated, pre-computed news scenarios and see how each one affects your portfolio. This is instant (no AI generation) and useful for browsing recent events.

```bash theme={null}
curl -X POST "https://tilt.io/api/v1/backchannel/scenarios/search" \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "queries": [{"weight": 1, "query": "tariffs trade war"}],
    "portfolio_weights": {
      "MH33D6-R": 0.40,
      "P8R3C2-R": 0.35,
      "K7TPSX-R": 0.25
    },
    "aggregate_scenarios": true,
    "limit": 5
  }'
```

### Key parameters

| Parameter | Type | Default | Description |
| - | - | - | - |
| `queries` | array | — | Weighted search queries. Each has `query` (string) and `weight` (0–1). |
| `subject_code` | string | `null` | Filter by subject code (e.g. `"Technology"`, `"Tariffs & Trade"`). Can be used alone or with `queries`. |
| `portfolio_weights` | object | `null` | Map of `tilt_asset_id` → weight. When provided, each result includes a `portfolio_exposure` field. |
| `aggregate_scenarios` | boolean | `false` | When `true`, collapses the scenario tree into probability-weighted summary stats. |
| `order_by_exposure` | boolean | `false` | When `true`, sorts results by portfolio impact instead of relevance. Requires `portfolio_weights`. |
| `limit` | integer | `10` | Number of results (1–100). |
| `min_importance` | integer | `null` | Filter to scenarios with importance rating >= this value (1–10). |

<Note>
  At least one of `queries` or `subject_code` must be provided. Sending an empty request body will return a `422 Validation Error`.
</Note>

### Response structure

The response wraps results in `articles`. Each article is a news scenario with timeline, probability tree, and (when `portfolio_weights` is provided) portfolio exposure:

```json theme={null}
{
  "success": true,
  "articles_found": 5,
  "articles": [
    {
      "point_id": "3fb7a574-c0d7-53b0-96cc-b98a2882f1c7",
      "headline": "Nvidia Invests in Mira Murati's Thinking Machines Lab",
      "date": "2026-03-10T00:00:00",
      "tilt_source": "WSJ",
      "importance_rating": 6,
      "timeline": [
        {"date": "2024-09-28", "summary": "OpenAI CTO Mira Murati resigns amid tensions"},
        {"date": "2026-01-26", "summary": "Nvidia invests $2B in CoreWeave for 5GW AI-factories"}
      ],
      "scenarios": [
        {
          "probability": 34,
          "title": "Compute-fueled breakout",
          "description": "Thinking Machines converts Nvidia's investment into a fast ramp of training and inference capacity...",
          "macro_shocks": [
            {"indicator": "rbics_55", "impact": 0.012},
            {"indicator": "VIXCLS", "impact": -0.6},
            {"indicator": "company_K7TPSX-R", "impact": 0.09}
          ],
          "sub_scenarios": [
            {
              "probability": 60,
              "title": "Frontier model debut",
              "description": "...",
              "macro_shocks": [...]
            }
          ]
        }
      ],
      "portfolio_exposure": {
        "expected_return_pct": 0.0049,
        "mad_pct": 0.0441,
        "num_leaf_scenarios": 8,
        "scenario_level_details": [
          {
            "title": "Compute-fueled breakout",
            "probability_pct": 34,
            "portfolio_return_mean_pct": 0.0693,
            "portfolio_return_std_pct": 0.0309,
            "sub_scenario_count": 2
          }
        ]
      }
    }
  ]
}
```

**Key fields in `portfolio_exposure`:**

| Field | Description |
| - | - |
| `expected_return_pct` | Probability-weighted expected portfolio return across all leaf scenarios, in percentage points (1.0 = 1%). |
| `mad_pct` | Probability-weighted median absolute portfolio return across leaf scenarios, in percentage points. |
| `num_leaf_scenarios` | Number of leaf nodes in the scenario tree used for the calculation. |
| `scenario_level_details` | Per-scenario breakdown with mean return, standard deviation, and sub-scenario count. |

[View API Reference →](/api-reference/backchannel/search-scenarios)

## Step 5: Analyze a Pre-Computed Dow Jones Article

Dow Jones articles are processed into article scenarios after ingestion. Once you have the scenario `point_id`, fetch the article's timeline, probability tree, macro and company shocks, related securities, and optional portfolio exposure:

```bash theme={null}
curl -X POST "https://tilt.io/api/v1/backchannel/scenarios/3fb7a574-c0d7-53b0-96cc-b98a2882f1c7" \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "include_content": true,
    "portfolio_weights": {
      "MH33D6-R": 0.40,
      "P8R3C2-R": 0.35,
      "K7TPSX-R": 0.25
    },
    "aggregate_scenarios": false
  }'
```

`{id}` must be the article scenario's `point_id`, such as the value returned by the search endpoint in Step 4. It is not the source Dow Jones article ID. The current public endpoint does not resolve a raw Dow Jones article ID directly.

When `portfolio_weights` is provided, the response includes `portfolio_exposure` with probability-weighted expected return, median absolute return, and per-leaf portfolio returns. When `aggregate_scenarios` is `true`, the per-leaf detail is replaced by scenario-level mean and standard-deviation summaries.

<Note>
  If your integration warms the user's portfolio data before this request, retain the resulting weights and include them as `portfolio_weights`. This endpoint currently accepts portfolio weights rather than a user ID.
</Note>

[View API Reference →](/api-reference/backchannel/get-scenario)

## Step 6: Macro Factor Analysis

### Get Macro Sensitivities

Understand which macro factors your portfolio is most exposed to:

```bash theme={null}
curl -X POST "https://tilt.io/api/v1/backchannel/risk/sensitivities" \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "portfolio_weights": {
      "MH33D6-R": 0.40,
      "P8R3C2-R": 0.35,
      "K7TPSX-R": 0.25
    },
    "limit": 5
  }'
```

The response ranks macro factors by portfolio-level exposure, showing which factors would move your portfolio the most:

```json theme={null}
{
  "success": true,
  "type": "portfolio",
  "valid_securities": 3,
  "total_weight": 1,
  "portfolio_macro_exposures": [
    {
      "macro_factor": "macro_DTWEXBGS",
      "description": "US Dollar Index (Trade Weighted)",
      "portfolio_exposure": -2.1324,
      "top_contributors": [
        {"tilt_id": "MH33D6-R", "weight": 0.4, "sensitivity": -2.0164, "contribution": -0.8065},
        {"tilt_id": "K7TPSX-R", "weight": 0.25, "sensitivity": -3.1648, "contribution": -0.7912},
        {"tilt_id": "P8R3C2-R", "weight": 0.35, "sensitivity": -1.5277, "contribution": -0.5347}
      ]
    }
  ]
}
```

[View API Reference →](/api-reference/backchannel/get-macro-sensitivities)

### Run a Macro Scenario

Use the sensitivities output to stress-test with specific macro shocks. The `shocks` object maps factor identifiers to shock magnitudes in standard deviations.

<Note>
  The sensitivities endpoint returns factor names with a `macro_` prefix (e.g. `macro_DTWEXBGS`). When passing shocks to the scenario endpoint, **drop the prefix** — use the raw identifier (e.g. `DTWEXBGS`).
</Note>

```bash theme={null}
curl -X POST "https://tilt.io/api/v1/backchannel/risk/scenario" \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "portfolio_weights": {
      "MH33D6-R": 0.40,
      "P8R3C2-R": 0.35,
      "K7TPSX-R": 0.25
    },
    "shocks": {
      "DTWEXBGS": 0.5
    },
    "include_security_details": true
  }'
```

The response shows the expected portfolio return and per-security breakdown:

```json theme={null}
{
  "success": true,
  "portfolio_expected_return_pct": -1.0662,
  "shocks_applied": {
    "DTWEXBGS": 0.5
  },
  "factor_summary": [
    {
      "factor": "DTWEXBGS",
      "type": "fred",
      "description": "US Dollar Index (Trade Weighted)",
      "shock": 0.5,
      "portfolio_impact_pct": -1.0662
    }
  ],
  "security_details": [
    {
      "tilt_id": "MH33D6-R",
      "ticker": "AAPL",
      "company_name": "Apple, Inc.",
      "weight": 0.40,
      "expected_return_pct": -0.8066,
      "contribution_to_portfolio_pct": -0.3226,
      "factor_contributions": {
        "DTWEXBGS": -0.8066
      }
    }
  ]
}
```

[View API Reference →](/api-reference/backchannel/run-macro-scenario)

## Putting It Together

A typical integration flow:

1. **Resolve tickers** once and cache the `tilt_asset_id` mapping.
2. **Run scenario portfolio analysis** with a plain English description to quickly assess portfolio impact for any hypothetical event. Use `flat_scenarios: true` for the simplest response structure. This is the easiest starting point.
3. **Search news scenarios** with `portfolio_weights` to find real-world events that matter to the portfolio. Use `order_by_exposure: true` to surface the highest-impact stories first.
4. **Fetch individual scenarios** with `portfolio_weights` for detailed drill-down with leaf-level probability breakdowns.
5. **Get macro sensitivities** to understand the portfolio's factor exposures.
6. **Run macro scenarios** to stress-test against specific factor shocks (e.g., "what if the US Dollar Index rises 0.5 standard deviations?").

<Tip>
  **Scenario Portfolio Analysis** (Step 2) generates scenarios on-the-fly using AI. The first call takes \~10–15 seconds, but results are automatically cached — subsequent calls for the same scenario return in under 1 second, for any portfolio. **Search Scenarios** (Step 3) returns pre-computed news scenarios instantly. Use scenario portfolio analysis for hypothetical what-if questions, and search scenarios for browsing recent real-world events.
</Tip>

## Rate Limits

All endpoints are rate-limited at **120 requests per minute** per API key. The `scenario_portfolio_analysis` endpoint has an additional limit of **10 requests per minute** to protect the AI generation backend.

If you exceed the limit, you'll receive a `429 Too Many Requests` response:

```json theme={null}
{
  "error": "Rate limit exceeded",
  "message": "Too many requests. Please slow down and try again."
}
```

## Error Handling

All error responses follow a consistent format:

```json theme={null}
{
  "error": "Error type",
  "message": "Human-readable description"
}
```

| Status Code | Meaning |
| - | - |
| `400` | Bad request (e.g., empty portfolio weights) |
| `401` | Missing or invalid authentication. Use the `X-Api-Key` header. |
| `403` | Invalid API key |
| `415` | Unsupported Content-Type. Use `application/json`. |
| `422` | Validation error with field-level details |
| `429` | Rate limit exceeded |
| `500` | Internal server error |


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