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

Execute Queries API

Full API reference for query execution

Result Filtering

Filter and transform data efficiently

CSV Export

Export data in CSV format

Webhooks

Get notified when queries complete

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