Skip to main content

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

Installation

Install the required SDK for your language:
npm install @duneanalytics/client-sdk

Quick Start

1. Fetch Query Results

Start by fetching results from an existing Dune query:
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;

2. Build Interactive Visualizations

Create charts using popular visualization libraries:
Plotly
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()
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).

3. Parameterized Dashboards

Create dashboards with user-configurable filters using saved queries:
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:
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:
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()
The Python Dune SDK does not support async/await. For parallel query execution, use ThreadPoolExecutor as shown above to execute multiple queries concurrently.

Export Data

Provide data export functionality:
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:
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

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

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