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

# Build Custom Dashboards & Reports

> Fetch query results, manage executions, and create dynamic visualizations in your own applications

## Overview

Perfect for product teams embedding analytics into their applications, creating white-label solutions, or building custom reporting tools on top of Dune's data.

## What You'll Learn

* Fetch and display query results
* Build interactive visualizations
* Manage query executions
* Create parameterized dashboards
* Handle real-time updates
* Export data in multiple formats

## Prerequisites

* A Dune account ([create one free](https://dune.com/auth/register))
* An API key ([get your API key](https://dune.com/apis?tab=keys))
* Basic knowledge of your frontend framework (React, Vue, etc.)

## Installation

Install the required SDK for your language:

<CodeGroup>
  ```bash TypeScript theme={null}
  npm install @duneanalytics/client-sdk
  ```

  ```bash Python theme={null}
  pip install dune-client
  ```
</CodeGroup>

## Quick Start

### 1. Fetch Query Results

Start by fetching results from an existing Dune query:

<CodeGroup>
  ```javascript React theme={null}
  import { useEffect, useState } from 'react';
  import { DuneClient } from '@duneanalytics/client-sdk';

  function DashboardWidget() {
    const [data, setData] = useState(null);
    const [loading, setLoading] = useState(true);
    
    const dune = new DuneClient(process.env.DUNE_API_KEY);
    
    useEffect(() => {
      async function fetchData() {
        try {
          // Execute query and get results
          const executionResult = await dune.runQuery({ queryId: 3493826 });
          setData(executionResult.result?.rows);
        } catch (error) {
          console.error('Error fetching data:', error);
        } finally {
          setLoading(false);
        }
      }
      
      fetchData();
    }, []);
    
    if (loading) return <div>Loading...</div>;
    
    return (
      <div>
        <h2>DEX Volume (24h)</h2>
        <table>
          <thead>
            <tr>
              <th>Protocol</th>
              <th>Volume</th>
            </tr>
          </thead>
          <tbody>
            {data?.map((row, i) => (
              <tr key={i}>
                <td>{row.protocol}</td>
                <td>${row.volume.toLocaleString()}</td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>
    );
  }

  export default DashboardWidget;
  ```

  ```python Python (Flask) theme={null}
  from flask import Flask, jsonify, render_template
  from dune_client.client import DuneClient
  from dune_client.query import QueryBase
  app = Flask(__name__)
  # DuneClient will read the DUNE_API_KEY environment variable
  dune = DuneClient()

  @app.route('/api/dashboard/dex-volume')
  def get_dex_volume():
      """API endpoint for DEX volume data"""
      query = QueryBase(query_id=3493826)
      results = dune.run_query(query)
      return jsonify({
          'data': results.get_rows(),
          'metadata': {
              'execution_id': results.execution_id,
              'row_count': len(results.get_rows())
          }
      })

  @app.route('/dashboard')
  def dashboard():
      """Render dashboard page"""
      return render_template('dashboard.html')

  if __name__ == '__main__':
      app.run(debug=True)
  ```

  ```python Python (Streamlit) theme={null}
  import streamlit as st
  from dune_client.client import DuneClient
  from dune_client.query import QueryBase
  import pandas as pd
  import plotly.express as px

  # DuneClient will read the DUNE_API_KEY environment variable
  dune = DuneClient()

  st.set_page_config(page_title="DEX Analytics", layout="wide")

  # Dashboard title
  st.title("📊 DEX Analytics Dashboard")

  # Fetch data
  @st.cache_data(ttl=300)  # Cache for 5 minutes
  def load_data():
      query = QueryBase(query_id=3493826)
      results = dune.run_query(query)
      return pd.DataFrame(results.get_rows())

  df = load_data()

  # Display metrics
  col1, col2, col3 = st.columns(3)
  with col1:
      st.metric("Total Volume", f"${df['volume'].sum():,.0f}")
  with col2:
      st.metric("Total Trades", f"{df['trade_count'].sum():,}")
  with col3:
      st.metric("Protocols", len(df))

  # Visualization
  fig = px.bar(df, x='protocol', y='volume', title='Volume by Protocol')
  st.plotly_chart(fig, use_container_width=True)

  # Data table
  st.dataframe(df, use_container_width=True)
  ```
</CodeGroup>

### 2. Build Interactive Visualizations

Create charts using popular visualization libraries:

```python Plotly theme={null}
from dune_client.client import DuneClient
import plotly.graph_objects as go
import pandas as pd

dune = DuneClient(api_key="YOUR_API_KEY")

# Fetch data
sql = """
SELECT 
  DATE_TRUNC('hour', block_time) as time,
  blockchain,
  SUM(amount_usd) as volume
FROM dex.trades
WHERE block_time > now() - interval '24' hour
GROUP BY 1, 2
ORDER BY 1
"""

results = dune.run_sql(query_sql=sql)
df = pd.DataFrame(results.get_rows())

# Create interactive chart
fig = go.Figure()

for blockchain in df['blockchain'].unique():
    chain_data = df[df['blockchain'] == blockchain]
    fig.add_trace(go.Scatter(
        x=chain_data['time'],
        y=chain_data['volume'],
        name=blockchain,
        mode='lines'
    ))

fig.update_layout(
    title='DEX Volume by Chain',
    xaxis_title='Time',
    yaxis_title='Volume (USD)',
    hovermode='x unified'
)

fig.show()
```

<Note>
  **Important Limitations:**

  * **TypeScript SDK:** The TypeScript SDK (`@duneanalytics/client-sdk`) only supports saved queries, not direct SQL execution. For JavaScript/TypeScript applications, create a saved query on Dune and use `dune.runQuery({ queryId })`.

  * **Python SDK:** Direct SQL execution with `dune.run_sql()` requires a Dune Plus subscription. For most users, use saved queries with `query = QueryBase(query_id=123)` and `dune.run_query(query)`.

  The example above shows SQL execution using the Python SDK (Plus subscription required).
</Note>

### 3. Parameterized Dashboards

Create dashboards with user-configurable filters using saved queries:

```typescript theme={null}
import { useState, useEffect } from 'react';
import { DuneClient, QueryParameter } from '@duneanalytics/client-sdk';

interface DashboardFilters {
  blockchain: string;
  protocol: string;
  timeRange: string;
}

function ParameterizedDashboard() {
  const [filters, setFilters] = useState<DashboardFilters>({
    blockchain: 'ethereum',
    protocol: 'all',
    timeRange: '24h'
  });
  const [data, setData] = useState(null);
  
  const dune = new DuneClient(process.env.DUNE_API_KEY!);
  
  useEffect(() => {
    async function fetchData() {
      // Use a saved query with parameters instead of SQL
      const queryId = 3493826; // Your parameterized query ID
      
      const executionResult = await dune.runQuery({
        queryId,
        query_parameters: [
          QueryParameter.text("blockchain", filters.blockchain),
          QueryParameter.text("time_range", filters.timeRange),
        ]
      });
      setData(executionResult.result?.rows);
    }
    
    fetchData();
  }, [filters]);
  
  return (
    <div>
      <div className="filters">
        <select 
          value={filters.blockchain}
          onChange={(e) => setFilters({...filters, blockchain: e.target.value})}
        >
          <option value="all">All Chains</option>
          <option value="ethereum">Ethereum</option>
          <option value="polygon">Polygon</option>
          <option value="arbitrum">Arbitrum</option>
        </select>
        
        <select 
          value={filters.timeRange}
          onChange={(e) => setFilters({...filters, timeRange: e.target.value})}
        >
          <option value="24h">Last 24 Hours</option>
          <option value="7d">Last 7 Days</option>
          <option value="30d">Last 30 Days</option>
        </select>
      </div>
      
      <div className="data-display">
        {data && <VolumeTable data={data} />}
      </div>
    </div>
  );
}
```

### 4. Real-time Dashboard Updates

Implement automatic data refreshes:

```javascript theme={null}
import { useEffect, useState, useCallback } from 'react';
import { DuneClient } from '@duneanalytics/client-sdk';

function RealtimeDashboard() {
  const [data, setData] = useState(null);
  const [lastUpdate, setLastUpdate] = useState(null);
  const [autoRefresh, setAutoRefresh] = useState(true);
  
  const dune = new DuneClient(process.env.DUNE_API_KEY);
  
  const fetchData = useCallback(async () => {
    try {
      const executionResult = await dune.runQuery({ queryId: 3493826 });
      
      setData(executionResult.result?.rows);
      setLastUpdate(new Date());
    } catch (error) {
      console.error('Failed to fetch data:', error);
    }
  }, []);
  
  useEffect(() => {
    // Initial fetch
    fetchData();
    
    // Set up auto-refresh
    let interval;
    if (autoRefresh) {
      interval = setInterval(fetchData, 60000); // Refresh every minute
    }
    
    return () => {
      if (interval) clearInterval(interval);
    };
  }, [fetchData, autoRefresh]);
  
  return (
    <div>
      <div className="controls">
        <button onClick={fetchData}>Refresh Now</button>
        <label>
          <input
            type="checkbox"
            checked={autoRefresh}
            onChange={(e) => setAutoRefresh(e.target.checked)}
          />
          Auto-refresh
        </label>
        {lastUpdate && (
          <span>Last updated: {lastUpdate.toLocaleTimeString()}</span>
        )}
      </div>
      
      <div className="dashboard-content">
        {data && <DashboardContent data={data} />}
      </div>
    </div>
  );
}
```

## Advanced Patterns

### Multi-Query Dashboard

Combine data from multiple queries:

```python theme={null}
from dune_client.client import DuneClient
from dune_client.query import QueryBase
from concurrent.futures import ThreadPoolExecutor

dune = DuneClient(api_key="YOUR_API_KEY")

def build_comprehensive_dashboard():
    """Fetch data from multiple queries in parallel"""
    
    query_ids = {
        'volume': 3493826,
        'users': 3493827,
        'protocols': 3493828,
        'chains': 3493829
    }
    
    # Use ThreadPoolExecutor for parallel execution
    with ThreadPoolExecutor(max_workers=4) as executor:
        futures = {
            name: executor.submit(
                lambda qid: dune.run_query(QueryBase(query_id=qid)),
                query_id
            )
            for name, query_id in query_ids.items()
        }
        
        # Build dashboard data structure
        dashboard = {
            name: future.result().get_rows()
            for name, future in futures.items()
        }
    
    return dashboard

# Run function (no async/await needed)
dashboard_data = build_comprehensive_dashboard()
```

<Note>
  The Python Dune SDK does not support async/await. For parallel query execution, use `ThreadPoolExecutor` as shown above to execute multiple queries concurrently.
</Note>

### Export Data

Provide data export functionality:

```typescript theme={null}
import { DuneClient } from '@duneanalytics/client-sdk';

class DataExporter {
  private dune: DuneClient;
  
  constructor(apiKey: string) {
    this.dune = new DuneClient(apiKey);
  }
  
  async exportToCSV(queryId: number, filename: string) {
    // Use Dune's CSV endpoint
    const response = await fetch(
      `https://api.dune.com/api/v1/query/${queryId}/results/csv`,
      {
        headers: {
          'X-Dune-Api-Key': process.env.DUNE_API_KEY!
        }
      }
    );
    
    const csv = await response.text();
    
    // Trigger download
    const blob = new Blob([csv], { type: 'text/csv' });
    const url = window.URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = filename;
    a.click();
  }
  
  async exportToJSON(queryId: number) {
    const executionResult = await this.dune.runQuery({ queryId });
    
    const json = JSON.stringify(executionResult.result?.rows, null, 2);
    const blob = new Blob([json], { type: 'application/json' });
    const url = window.URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = 'data.json';
    a.click();
  }
}

// Usage
function ExportButton({ queryId }: { queryId: number }) {
  const exporter = new DataExporter(process.env.DUNE_API_KEY!);
  
  return (
    <div>
      <button onClick={() => exporter.exportToCSV(queryId, 'data.csv')}>
        Export CSV
      </button>
      <button onClick={() => exporter.exportToJSON(queryId)}>
        Export JSON
      </button>
    </div>
  );
}
```

### Caching Strategy

Implement smart caching for better performance:

```python theme={null}
from flask import Flask, jsonify
from flask_caching import Cache
from dune_client.client import DuneClient
from dune_client.query import QueryBase
import os

app = Flask(__name__)
cache = Cache(app, config={'CACHE_TYPE': 'simple'})
# DuneClient will read the DUNE_API_KEY environment variable
dune = DuneClient()

@app.route('/api/data/<int:query_id>')
@cache.cached(timeout=300)  # Cache for 5 minutes
def get_query_data(query_id):
    """Cached endpoint for query results"""
    query = QueryBase(query_id=query_id)
    results = dune.run_query(query)
    return jsonify({
        'data': results.get_rows(),
        'cached': False,  # First request
        'execution_id': results.execution_id
    })

@app.route('/api/data/<int:query_id>/refresh')
def refresh_data(query_id):
    """Force refresh cached data"""
    cache.delete(f'view//api/data/{query_id}')
    query = QueryBase(query_id=query_id)
    results = dune.run_query(query)
    return jsonify({
        'data': results.get_rows(),
        'cached': False,
        'refreshed': True
    })
```

## Dashboard Templates

### Analytics Dashboard

```javascript theme={null}
// Complete analytics dashboard example using saved queries
import { useEffect, useState } from 'react';
import { DuneClient } from '@duneanalytics/client-sdk';
import { LineChart, BarChart, PieChart } from 'recharts';

function AnalyticsDashboard() {
  const [metrics, setMetrics] = useState({
    totalVolume: 0,
    activeUsers: 0,
    avgTransactionSize: 0,
    topProtocols: []
  });
  
  const dune = new DuneClient(process.env.DUNE_API_KEY);
  
  useEffect(() => {
    async function loadDashboard() {
      // Use saved query IDs for each metric
      const volumeQueryId = 123456;   // Your volume query ID
      const usersQueryId = 123457;    // Your users query ID
      const protocolsQueryId = 123458; // Your protocols query ID
      
      const [volume, users, protocols] = await Promise.all([
        dune.runQuery({ queryId: volumeQueryId }),
        dune.runQuery({ queryId: usersQueryId }),
        dune.runQuery({ queryId: protocolsQueryId })
      ]);
      
      setMetrics({
        totalVolume: volume.result?.rows[0].total,
        activeUsers: users.result?.rows[0].total,
        avgTransactionSize: volume.result?.rows[0].total / users.result?.rows[0].total,
        topProtocols: protocols.result?.rows
      });
    }
    
    loadDashboard();
    
    // Refresh every 5 minutes
    const interval = setInterval(loadDashboard, 300000);
    return () => clearInterval(interval);
  }, []);
  
  return (
    <div className="dashboard">
      <h1>DEX Analytics</h1>
      
      <div className="metrics-grid">
        <MetricCard 
          title="24h Volume" 
          value={`$${metrics.totalVolume.toLocaleString()}`} 
        />
        <MetricCard 
          title="Active Users" 
          value={metrics.activeUsers.toLocaleString()} 
        />
        <MetricCard 
          title="Avg Transaction" 
          value={`$${metrics.avgTransactionSize.toLocaleString()}`} 
        />
      </div>
      
      <div className="charts-grid">
        <BarChart data={metrics.topProtocols} />
      </div>
    </div>
  );
}
```

## Next Steps

<CardGroup cols={2}>
  <Card title="Execute Queries API" icon="play" href="/api-reference/executions/endpoint/execute-sql">
    Full API reference for query execution
  </Card>

  <Card title="Result Filtering" icon="filter" href="/api-reference/executions/filtering">
    Filter and transform data efficiently
  </Card>

  <Card title="CSV Export" icon="file-csv" href="/api-reference/executions/endpoint/get-execution-result-csv">
    Export data in CSV format
  </Card>

  <Card title="Webhooks" icon="webhook" href="/api-reference/webhooks/webhook">
    Get notified when queries complete
  </Card>
</CardGroup>

## Example Use Cases

* **Internal Analytics:** Build custom dashboards for your team
* **White-label Solutions:** Embed Dune-powered analytics in your product
* **Client Reporting:** Generate automated reports for clients
* **Portfolio Trackers:** Create personalized portfolio dashboards
* **Protocol Dashboards:** Build public dashboards for your protocol
