Back to Blog
Integrations March 15, 2026

Powering a Static Site with csv-api

Use csv-api as a lightweight CMS backend for Jekyll, Hugo, or any static site generator.

The problem with static site data

Static site generators like Jekyll, Hugo, and Eleventy are great for performance and simplicity. But what happens when you need dynamic data — a team directory, an event list, a product catalog, or a FAQ page that non-developers need to update?

Traditionally, you'd hardcode the data in YAML/JSON/Markdown files, use a headless CMS, or set up a full backend. csv-api offers a simpler middle ground: maintain your data in a spreadsheet, upload it once, and fetch it at build time or client-side.

Approach 1: Fetch at build time

Most static site generators support data fetching during the build process. This means your API data gets baked into static HTML — no client-side JavaScript needed, no API key exposed.

Jekyll (_plugins/csv_api.rb):

require "net/http"
require "json"

module Jekyll
  class CsvApiGenerator < Generator
    def generate(site)
      uri = URI("https://csv-api.com/api/v1/datasets/YOUR_ID/records?per_page=100")
      req = Net::HTTP::Get.new(uri)
      req["Authorization"] = "Bearer #{ENV['CSV_API_KEY']}"

      res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http|
        http.request(req)
      }

      data = JSON.parse(res.body)
      site.data["team_members"] = data["data"]
    end
  end
end

Then use it in a template (team.html):

{% for member in site.data.team_members %}
  <div class="team-card">
    <h3>{{ member.name }}</h3>
    <p>{{ member.role }}</p>
    <p>{{ member.bio }}</p>
  </div>
{% endfor %}

Approach 2: Hugo data templates

Hugo can fetch remote data with the getJSON function. However, for authenticated APIs, it's easier to use a build script that fetches the data and saves it as a JSON file:

fetch-data.sh (run before hugo build):

#!/bin/bash
curl -s -H "Authorization: Bearer $CSV_API_KEY" \
  "https://csv-api.com/api/v1/datasets/YOUR_ID/records?per_page=100" \
  | jq '.data' > data/events.json

Then reference in your Hugo template:

{{ range $.Site.Data.events }}
  <article>
    <h2>{{ .title }}</h2>
    <time>{{ .date }}</time>
    <p>{{ .description }}</p>
  </article>
{{ end }}

Approach 3: Client-side fetching

For data that changes frequently, you can fetch it client-side. This works with any static site since it's just JavaScript. csv-api includes CORS headers, so cross-origin requests work out of the box.

<div id="faq-list">Loading...</div>

<script>
  fetch("https://csv-api.com/api/v1/datasets/YOUR_ID/records?sort=order&per_page=100", {
    headers: { "Authorization": "Bearer YOUR_API_KEY" }
  })
    .then(res => res.json())
    .then(({ data }) => {
      document.getElementById("faq-list").innerHTML = data.map(faq =>
        `<details>
          <summary>${faq.question}</summary>
          <p>${faq.answer}</p>
        </details>`
      ).join("");
    });
</script>

Remember: Client-side fetching exposes your API key to anyone viewing the page source. This is acceptable for public, read-only data. For sensitive data, use build-time fetching or a server-side proxy.

Real-world use case: csv-api.com itself

We practice what we preach. The csv-api landing page loads its feature cards and workflow sections from csv-api datasets. The data is stored in CSV-backed dynamic tables and rendered at page load — with a hardcoded fallback in case the datasets aren't available.

This means updating the landing page content is as simple as editing rows in the dataset — no code changes, no deploy needed.

Tips for static site integration

  • Use fields to minimize payload — only request the columns your templates actually use.
  • Add a sort column to your CSV if you need to control display order (e.g., ?sort=order).
  • Use a boolean column for visibility — add a published column and filter with ?filter[published]=true to draft and publish content.
  • Set up a CI/CD webhook to rebuild your site when data changes, keeping the static output fresh.

We use essential cookies to keep you logged in. No tracking or analytics. Privacy policy