DEV Community

이성열 (월억)
이성열 (월억)

Posted on

I Built a Free Tool to Compare Economies of 200+ Countries (World Bank API + Next.js)

I wanted to compare South Korea and Japan's economies for a research project. Spent 30 minutes clicking around the World Bank website, trying to export data, struggling with their clunky interface.

So I built my own tool. In one day.

What I Built

CountryCompare — a free, interactive dashboard that lets you compare economic indicators between countries using World Bank data.

Try it: country-compare.com

What You Can Do

  • Compare 2 countries side by side (free) or up to 10 (Pro)
  • View 50 economic indicators across 6 categories: Economy, Labor, Society, Energy, Environment, Trade
  • See 25 years of historical data in interactive charts
  • Download PDF reports and CSV data
  • Browse 50 country profiles with key economic snapshots
  • Dark mode included

The Numbers

  • 1,290 auto-generated pages (every country pair gets its own SEO-optimized page)
  • 50 indicators from World Bank Open Data
  • 50 country profiles
  • $0 hosting cost (Vercel free tier)
  • $0 data cost (World Bank API is free, no key required)

Tech Stack

  • Next.js 14 (App Router, TypeScript)
  • Chart.js + react-chartjs-2 for interactive charts
  • Tailwind CSS for styling + dark mode
  • Vercel for hosting (free tier)
  • World Bank API for data (free, no API key needed)

How the World Bank API Works

The World Bank API is surprisingly simple and completely free:

GET https://api.worldbank.org/v2/country/US;JP/indicator/NY.GDP.MKTP.CD?format=json&date=2000:2024&per_page=500
Enter fullscreen mode Exit fullscreen mode

Breaking it down:

  • /country/US;JP — semicolon-separated ISO2 codes for multiple countries
  • /indicator/NY.GDP.MKTP.CD — the indicator code (this one is GDP in USD)
  • format=json — returns JSON instead of XML
  • date=2000:2024 — 25 years of data in one call

Here's the actual fetch function from my codebase:

const BASE_URL = 'https://api.worldbank.org/v2';

export async function fetchIndicator(
  countryCodes: string[],
  indicatorId: string
) {
  const codes = countryCodes.join(';');
  const url = `${BASE_URL}/country/${codes}/indicator/${indicatorId}?format=json&date=2000:2024&per_page=500`;

  const res = await fetch(url, { next: { revalidate: 86400 } }); // Cache 24h
  const json = await res.json();
  return json[1] || []; // [0] is metadata, [1] is actual data
}
Enter fullscreen mode Exit fullscreen mode

No API key. No rate limits I've ever hit. No authentication. Just clean, free data for 200+ countries.

Key indicator codes I use:

Code Indicator
NY.GDP.MKTP.CD GDP (USD)
NY.GDP.PCAP.CD GDP per Capita
SP.POP.TOTL Population
SL.UEM.TOTL.ZS Unemployment Rate
FP.CPI.TOTL.ZG CPI Inflation
SP.DYN.LE00.IN Life Expectancy
EN.ATM.CO2E.PC CO2 per Capita
SE.ADT.LITR.ZS Literacy Rate
IT.NET.USER.ZS Internet Users %

The full app uses 50 indicators across 6 categories.

SEO Strategy: 1,290 Pages from 50 Countries

This is where it gets interesting. With 50 countries, you get C(50,2) = 1,225 unique comparison pairs. Add 50 country profile pages and some static pages, and you've got 1,290 indexable pages — all auto-generated at build time.

How It Works

// Generate all country-pair slugs
export function getAllCompareSlugs(): string[] {
  const slugs: string[] = [];
  for (let i = 0; i < COUNTRIES.length; i++) {
    for (let j = i + 1; j < COUNTRIES.length; j++) {
      slugs.push(`${COUNTRIES[i].slug}-vs-${COUNTRIES[j].slug}`);
    }
  }
  return slugs;
}

// Next.js static generation
export function generateStaticParams() {
  return getAllCompareSlugs().map((slug) => ({ slug }));
}
Enter fullscreen mode Exit fullscreen mode

This generates URLs like:

  • /compare/united-states-vs-china
  • /compare/south-korea-vs-japan
  • /compare/germany-vs-france

Each page gets its own metadata and OG image:

export async function generateMetadata({ params }): Promise<Metadata> {
  return {
    title: `${countryA} vs ${countryB} - Economic Comparison`,
    openGraph: {
      images: [{
        url: `/api/og?a=${countryA}&b=${countryB}`,
        width: 1200,
        height: 630,
      }],
    },
  };
}
Enter fullscreen mode Exit fullscreen mode

A dynamic sitemap feeds all 1,290 URLs to search engines with tiered priorities — homepage at 1.0, comparisons at 0.8, country profiles at 0.7.

Result: Every country pair is a long-tail keyword target. Someone Googling "USA vs China economy comparison" could land on a pre-built, data-rich page.

Free vs Pro

I wanted the core tool to be genuinely useful for free. Here's the split:

Feature Free Pro ($9/mo)
Indicators 5 (Overview) 50 (all categories)
Countries Compare 2 Compare up to 10
Data Range 10 years (2014-2024) 25 years (2000-2024)
PDF Reports Watermarked, 5 indicators Full 50-indicator report
CSV Export Included
Ads Yes No

The free tier gives you GDP, Population, Unemployment, Inflation, and Life Expectancy with 10 years of charts. That's enough for a quick comparison or school project.

Pro unlocks the deep dive: 50 indicators across Economy, Labor, Society, Energy, Environment, and Trade — plus multi-country comparison up to 10 countries on one chart.

Payment goes through Gumroad. License keys are verified server-side and stored as JWT tokens:

export function createToken(licenseKey: string): string {
  return jwt.sign(
    { licenseKey, pro: true },
    JWT_SECRET,
    { expiresIn: '7d' }
  );
}
Enter fullscreen mode Exit fullscreen mode

What I Learned

1. The World Bank API is an underrated goldmine

Free, no auth, well-structured, 200+ countries, 1,400+ indicators. If you're building anything data-related, check it out before scraping Wikipedia.

2. Combinatorics is your SEO friend

50 countries × 49 partners ÷ 2 = 1,225 unique pages. Each one is a potential search result. generateStaticParams() in Next.js makes this trivial.

3. Build the free tier first, pro features second

I built the complete 2-country comparison with 5 indicators first. It worked. It was useful. Only then did I add the paywall for power features. This keeps you honest about whether the core product has value.

4. Dark mode is table stakes in 2025

I almost skipped it. Glad I didn't. The implementation is straightforward with Tailwind's dark: prefix + a localStorage toggle + a script in <head> to prevent flash:

<script dangerouslySetInnerHTML={{ __html: `(function(){
  try {
    var t = localStorage.getItem('theme');
    if (t === 'dark' || (!t && window.matchMedia('(prefers-color-scheme:dark)').matches)) {
      document.documentElement.classList.add('dark');
    }
  } catch(e) {}
})()`}} />
Enter fullscreen mode Exit fullscreen mode

5. PDF generation on the server is worth the effort

Using jspdf + jspdf-autotable on a Next.js API route means users get a clean, branded PDF without any client-side library loading. The Pro report generates a cover page, executive summary, and one data table per indicator — up to 50 pages of data.

Try It Out

The free tier is fully functional — compare any 2 countries across 5 key indicators with interactive charts, right now.

Feedback welcome. What indicators or features would you want to see added?


Built in one day with Next.js, Chart.js, Tailwind CSS, and the World Bank API. Hosted for free on Vercel.

Top comments (0)