Skip to main content
GET
https://api-mainnet.onzks.com
/
v1
/
score
/
:identity
/
history
Get Score History
curl --request GET \
  --url https://api-mainnet.onzks.com/v1/score/:identity/history \
  --header 'Authorization: Bearer <token>'
{
  "success": true,
  "zksId": {},
  "address": "<string>",
  "history": [
    {
      "timestamp": "<string>",
      "score": 123,
      "rank": 123,
      "change": 123,
      "percentChange": 123
    }
  ],
  "summary": {
    "startScore": 123,
    "endScore": 123,
    "totalChange": 123,
    "percentChange": 123,
    "highestScore": 123,
    "lowestScore": 123,
    "averageScore": 123
  }
}

Overview

Retrieve historical ZKScore data to track score changes over time. This endpoint provides time-series data showing how a user’s score has evolved, enabling trend analysis and progress tracking.
Use this endpoint to build score charts, track improvements, and analyze scoring trends.

Parameters

identity
string
required
ZKS ID (e.g., alice.zks) or wallet address (e.g., 0x742d35Cc...)
timeframe
string
Time period to retrieve (default: 30d)
  • 7d - Last 7 days
  • 30d - Last 30 days
  • 90d - Last 90 days
  • 1y - Last year
  • all - All available history
interval
string
Data point interval (default: day)
  • hour - Hourly data points
  • day - Daily data points
  • week - Weekly data points
  • month - Monthly data points
chainId
number
Specific chain ID to get history for (optional, defaults to aggregated)

Response

success
boolean
Indicates if the request was successful
zksId
string | null
The ZKS ID (without .zks suffix), or null if not set
address
string
The primary wallet address
history
array
Array of historical score data points
summary
object
Summary statistics for the period

Examples

curl "https://api.onzks.com/v1/score/alice.zks/history?timeframe=30d&interval=day" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response Example

{
  "success": true,
  "zksId": "alice",
  "address": "0x742d35cc6635c0532925a3b844d1ff4e1321",
  "timeframe": "30d",
  "interval": "day",
  "history": [
    {
      "timestamp": "2024-01-01T00:00:00Z",
      "score": 9437,
      "rank": 1,
      "change": 0,
      "percentChange": 0
    },
    {
      "timestamp": "2024-01-02T00:00:00Z",
      "score": 9458,
      "rank": 1,
      "change": 21,
      "percentChange": 0.22
    },
    {
      "timestamp": "2024-01-03T00:00:00Z",
      "score": 9492,
      "rank": 1,
      "change": 34,
      "percentChange": 0.36
    },
    // ... more data points
    {
      "timestamp": "2024-01-30T00:00:00Z",
      "score": 9847,
      "rank": 1,
      "change": 42,
      "percentChange": 0.43
    }
  ],
  "summary": {
    "startScore": 9437,
    "endScore": 9847,
    "totalChange": 410,
    "percentChange": 4.34,
    "highestScore": 9850,
    "lowestScore": 9420,
    "averageScore": 9645
  },
  "dataPoints": 30
}

Use Cases

1. Score Chart Visualization

Display score history in a line chart:
async function renderScoreChart(identity) {
  const { history } = await getScoreHistory(identity, '30d', 'day');

  const chartData = {
    labels: history.map(point => new Date(point.timestamp).toLocaleDateString()),
    datasets: [{
      label: 'ZKScore',
      data: history.map(point => point.score),
      borderColor: 'rgb(75, 192, 192)',
      tension: 0.1
    }]
  };

  // Render with Chart.js or similar
  renderChart(chartData);
}

2. Progress Tracking

Track user progress over time:
async function trackProgress(identity) {
  const { summary } = await getScoreHistory(identity, '30d');

  const progress = {
    improvement: summary.totalChange,
    percentImprovement: summary.percentChange,
    trend: summary.totalChange > 0 ? 'improving' : 
           summary.totalChange < 0 ? 'declining' : 'stable'
  };

  console.log(`Progress: ${progress.trend}`);
  console.log(`Improvement: ${progress.improvement} points (${progress.percentImprovement}%)`);

  return progress;
}

3. Trend Analysis

Analyze scoring trends:
async function analyzeTrends(identity) {
  const { history } = await getScoreHistory(identity, '90d', 'week');

  // Calculate moving average
  const movingAverage = [];
  const window = 4; // 4-week moving average

  for (let i = window - 1; i < history.length; i++) {
    const sum = history.slice(i - window + 1, i + 1)
      .reduce((acc, point) => acc + point.score, 0);
    movingAverage.push(sum / window);
  }

  // Identify trend
  const recentAvg = movingAverage.slice(-4).reduce((a, b) => a + b) / 4;
  const olderAvg = movingAverage.slice(0, 4).reduce((a, b) => a + b) / 4;
  const trend = recentAvg > olderAvg ? 'upward' : 'downward';

  return { movingAverage, trend };
}

4. Milestone Detection

Detect when user reaches milestones:
async function detectMilestones(identity) {
  const { history } = await getScoreHistory(identity, 'all');

  const milestones = [
    { score: 1000, name: 'Bronze Tier' },
    { score: 3000, name: 'Silver Tier' },
    { score: 5000, name: 'Gold Tier' },
    { score: 7000, name: 'Platinum Tier' },
    { score: 9000, name: 'Diamond Tier' }
  ];

  const achieved = [];

  milestones.forEach(milestone => {
    const point = history.find(p => p.score >= milestone.score);
    if (point) {
      achieved.push({
        ...milestone,
        achievedAt: point.timestamp,
        score: point.score
      });
    }
  });

  return achieved;
}

Best Practices

1. Choose Appropriate Intervals

Match interval to timeframe:
function getOptimalInterval(timeframe) {
  switch (timeframe) {
    case '7d': return 'hour';
    case '30d': return 'day';
    case '90d': return 'day';
    case '1y': return 'week';
    case 'all': return 'month';
    default: return 'day';
  }
}

// Usage
const interval = getOptimalInterval('30d');
await getScoreHistory(identity, '30d', interval);

2. Cache Historical Data

History doesn’t change frequently:
const historyCache = new Map();

async function getCachedHistory(identity, timeframe, interval) {
  const cacheKey = `history:${identity}:${timeframe}:${interval}`;
  
  if (historyCache.has(cacheKey)) {
    const cached = historyCache.get(cacheKey);
    // Cache for 1 hour
    if (Date.now() - cached.timestamp < 60 * 60 * 1000) {
      return cached.data;
    }
  }

  const data = await getScoreHistory(identity, timeframe, interval);
  historyCache.set(cacheKey, {
    data,
    timestamp: Date.now()
  });

  return data;
}

3. Handle Missing Data

Some periods may have no data:
function fillMissingDataPoints(history, interval) {
  const filled = [];
  let lastScore = history[0]?.score || 0;

  for (let i = 0; i < history.length - 1; i++) {
    filled.push(history[i]);

    const current = new Date(history[i].timestamp);
    const next = new Date(history[i + 1].timestamp);
    const gap = (next - current) / (1000 * 60 * 60 * 24); // days

    // Fill gaps larger than interval
    if (gap > 1) {
      for (let j = 1; j < gap; j++) {
        const interpolated = new Date(current);
        interpolated.setDate(interpolated.getDate() + j);
        
        filled.push({
          timestamp: interpolated.toISOString(),
          score: lastScore,
          rank: history[i].rank,
          change: 0,
          percentChange: 0,
          interpolated: true
        });
      }
    }

    lastScore = history[i].score;
  }

  filled.push(history[history.length - 1]);
  return filled;
}

4. Compare Multiple Users

Compare score histories:
async function compareUsers(identities) {
  const histories = await Promise.all(
    identities.map(id => getScoreHistory(id, '30d', 'day'))
  );

  const comparison = {
    users: identities,
    data: histories.map((h, i) => ({
      identity: identities[i],
      currentScore: h.summary.endScore,
      change: h.summary.totalChange,
      percentChange: h.summary.percentChange
    }))
  };

  // Sort by improvement
  comparison.data.sort((a, b) => b.change - a.change);

  return comparison;
}

Visualization Examples

Line Chart

function createLineChart(history) {
  return {
    type: 'line',
    data: {
      labels: history.map(p => new Date(p.timestamp).toLocaleDateString()),
      datasets: [{
        label: 'Score',
        data: history.map(p => p.score),
        borderColor: 'rgb(75, 192, 192)',
        fill: false
      }]
    },
    options: {
      responsive: true,
      scales: {
        y: {
          beginAtZero: false
        }
      }
    }
  };
}

Area Chart with Trend

function createAreaChart(history) {
  return {
    type: 'line',
    data: {
      labels: history.map(p => new Date(p.timestamp).toLocaleDateString()),
      datasets: [{
        label: 'Score',
        data: history.map(p => p.score),
        borderColor: 'rgb(75, 192, 192)',
        backgroundColor: 'rgba(75, 192, 192, 0.2)',
        fill: true
      }]
    }
  };
}

Troubleshooting

”Insufficient history”

Cause: User doesn’t have enough historical data. Solution:
  • Historical data requires at least 7 days of activity
  • Try a shorter timeframe
  • Check back after more time has passed

”Invalid timeframe”

Cause: Unsupported timeframe value. Solution:
  • Use supported values: 7d, 30d, 90d, 1y, all
  • Check for typos

”Too many data points”

Cause: Requested interval too granular for timeframe. Solution:
  • Use larger intervals for longer timeframes
  • hour for 7d max
  • day for 90d max
  • week for 1y max

Performance Tips

  1. Use Appropriate Intervals: Don’t request hourly data for a year
  2. Cache Results: History doesn’t change frequently
  3. Limit Data Points: Request only what you need to display
  4. Batch Requests: When comparing multiple users, use Promise.all()

Rate Limits

Score history requests are subject to rate limits:
  • Free tier: 30 requests per minute
  • Starter tier: 150 requests per minute
  • Professional tier: 600 requests per minute
  • Enterprise tier: Custom limits