Skip to main content
Automate weekly GEO performance reports with trend analysis and week-over-week comparisons.
This guide uses the API client from the Guides index. Copy it to your project first.

What You’ll Build

A weekly report showing:
  • Current vs previous week metrics comparison
  • Trend indicators (up/down/stable)
  • Percentage change for each KPI

Generate Weekly Report

async function generateWeeklyReport(client, brandId) {
  const now = new Date();

  // Calculate date ranges
  const thisWeekEnd = now.toISOString().split('T')[0];
  const thisWeekStart = new Date(now - 7 * 24 * 60 * 60 * 1000).toISOString().split('T')[0];
  const lastWeekEnd = thisWeekStart;
  const lastWeekStart = new Date(now - 14 * 24 * 60 * 60 * 1000).toISOString().split('T')[0];

  // Fetch both periods in parallel
  const [thisWeek, lastWeek] = await Promise.all([
    client.getPerformance(brandId, { startDate: thisWeekStart, endDate: thisWeekEnd }),
    client.getPerformance(brandId, { startDate: lastWeekStart, endDate: lastWeekEnd }),
  ]);

  const calculateChange = (current, previous) => {
    if (!previous || previous === 0) return null;
    return ((current - previous) / previous * 100).toFixed(1);
  };

  const getTrend = (current, previous) => {
    if (current > previous) return 'up';
    if (current < previous) return 'down';
    return 'stable';
  };

  const metrics = [
    { name: 'GEO Score', key: 'global' },
    { name: 'Mention Rate', key: 'mentionRate' },
    { name: 'Source Rate', key: 'sourceRate' },
    { name: 'Share of Voice', key: 'shareOfVoice' },
    { name: 'Sentiment', key: 'sentiment' },
  ];

  return {
    generatedAt: new Date().toISOString(),
    period: {
      thisWeek: { start: thisWeekStart, end: thisWeekEnd },
      lastWeek: { start: lastWeekStart, end: lastWeekEnd },
    },
    metrics: metrics.map(m => ({
      name: m.name,
      current: thisWeek.scores[m.key],
      previous: lastWeek.scores[m.key],
      change: calculateChange(thisWeek.scores[m.key], lastWeek.scores[m.key]),
      trend: getTrend(thisWeek.scores[m.key], lastWeek.scores[m.key]),
    })),
    methodology: {
      thisWeek: thisWeek.methodology.responsesTotal,
      lastWeek: lastWeek.methodology.responsesTotal,
    },
  };
}

Usage

const client = new QwairyClient(process.env.QWAIRY_API_TOKEN);
const report = await generateWeeklyReport(client, 'your-brand-id');

// Console output
console.log(`\n Weekly GEO Report: ${report.period.thisWeek.start} to ${report.period.thisWeek.end}\n`);
console.log('Metric               Current    Previous    Change');
console.log('─'.repeat(55));

for (const m of report.metrics) {
  const trend = m.trend === 'up' ? '↑' : m.trend === 'down' ? '↓' : '→';
  const changeStr = m.change !== null ? `${m.change > 0 ? '+' : ''}${m.change}%` : 'N/A';
  console.log(`${m.name.padEnd(20)} ${String(m.current).padEnd(10)} ${String(m.previous).padEnd(11)} ${trend} ${changeStr}`);
}

Example Output

Console:
Weekly GEO Report: 2024-12-12 to 2024-12-19

Metric               Current    Previous    Change
───────────────────────────────────────────────────────
GEO Score            62         58          ↑ +6.9%
Mention Rate         45.2       42.1        ↑ +7.4%
Source Rate          23.9       25.0        ↓ -4.4%
Share of Voice       8.13       7.5         ↑ +8.4%
Sentiment            78.1       76.2        ↑ +2.5%
JSON:
{
  "generatedAt": "2024-12-19T10:30:00.000Z",
  "period": {
    "thisWeek": { "start": "2024-12-12", "end": "2024-12-19" },
    "lastWeek": { "start": "2024-12-05", "end": "2024-12-12" }
  },
  "metrics": [
    { "name": "GEO Score", "current": 62, "previous": 58, "change": "6.9", "trend": "up" },
    { "name": "Mention Rate", "current": 45.2, "previous": 42.1, "change": "7.4", "trend": "up" },
    { "name": "Source Rate", "current": 23.9, "previous": 25.0, "change": "-4.4", "trend": "down" },
    { "name": "Share of Voice", "current": 8.13, "previous": 7.5, "change": "8.4", "trend": "up" },
    { "name": "Sentiment", "current": 78.1, "previous": 76.2, "change": "2.5", "trend": "up" }
  ]
}

Send Report

Deliver the report via Slack or email.
async function sendToSlack(report, webhookUrl) {
  const formatMetric = (m) => {
    const icon = m.trend === 'up' ? ':chart_with_upwards_trend:' : m.trend === 'down' ? ':chart_with_downwards_trend:' : ':arrow_right:';
    const sign = m.change > 0 ? '+' : '';
    return `${icon} *${m.name}*: ${m.current} (${sign}${m.change}%)`;
  };

  const blocks = [
    {
      type: 'header',
      text: { type: 'plain_text', text: `Weekly GEO Report` },
    },
    {
      type: 'section',
      text: {
        type: 'mrkdwn',
        text: `*Period:* ${report.period.thisWeek.start} to ${report.period.thisWeek.end}`,
      },
    },
    {
      type: 'section',
      text: {
        type: 'mrkdwn',
        text: report.metrics.map(formatMetric).join('\n'),
      },
    },
  ];

  await fetch(webhookUrl, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ blocks }),
  });
}

// Usage
await sendToSlack(report, process.env.SLACK_WEBHOOK_URL);

Scheduling

Run this report automatically:
PlatformMethod
Cron0 9 * * MON (every Monday at 9am)
GitHub Actionsschedule: cron: '0 9 * * 1'
AWS LambdaEventBridge rule with cron expression
Google CloudCloud Scheduler + Cloud Functions

Next Steps