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:
React
Python (Flask)
Python (Streamlit)
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:
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