All posts
Blog

Export Google Sheets Data as CSV via API

Use the SheetsAPI format=csv query parameter to get a Google Sheet as a downloadable CSV file — with examples in curl, fetch, and a Next.js download button.

2 min read

One parameter, CSV output

GKit SheetsAPI returns JSON by default. Add ?format=csv to any list endpoint and you get back a plain-text CSV file with a header row — ready to open in Excel, import into another tool, or stream to the browser as a download.

GET /api/spreadsheets/{userKey}/{sheetName}?format=csv

The response has Content-Type: text/csv and the first row is the column header from your sheet.

curl

The simplest way to test it:

curl -o contacts.csv \
  -H "Authorization: Bearer sk_..." \
  "https://sheetsapi.gkit.mreshank.com/api/spreadsheets/uk_abc123/Contacts?format=csv"

-o contacts.csv writes the response body straight to a file. Open it in any spreadsheet app.

To inspect it inline without saving:

curl -s \
  -H "Authorization: Bearer sk_..." \
  "https://sheetsapi.gkit.mreshank.com/api/spreadsheets/uk_abc123/Contacts?format=csv"

Output:

name,email,plan
Ada Lovelace,ada@example.com,pro
Grace Hopper,grace@example.com,free

fetch in JavaScript

async function fetchCsv(userKey, sheetName) {
  const res = await fetch(
    `https://sheetsapi.gkit.mreshank.com/api/spreadsheets/${userKey}/${sheetName}?format=csv`,
    {
      headers: { Authorization: `Bearer ${process.env.GKIT_API_KEY}` },
    }
  );
 
  if (!res.ok) throw new Error(`Failed: ${res.status}`);
  return res.text(); // plain CSV string
}
 
const csv = await fetchCsv("uk_abc123", "Contacts");
console.log(csv);
// name,email,plan
// Ada Lovelace,ada@example.com,pro

From there you can parse it with any CSV library (papaparse, csv-parse) or send it straight to the user.

Next.js download button

The pattern below fetches the CSV on the client, wraps it in a Blob, creates a temporary object URL, clicks it programmatically, then immediately revokes the URL — the browser's native download dialog appears with no server route required.

"use client";
 
import { useState } from "react";
 
interface DownloadCsvButtonProps {
  userKey: string;
  sheetName: string;
  fileName?: string;
}
 
export function DownloadCsvButton({
  userKey,
  sheetName,
  fileName = `${sheetName}.csv`,
}: DownloadCsvButtonProps) {
  const [loading, setLoading] = useState(false);
 
  async function handleDownload() {
    setLoading(true);
    try {
      const res = await fetch(
        `/api/sheets-csv?userKey=${userKey}&sheet=${sheetName}`
      );
      if (!res.ok) throw new Error("Export failed");
 
      const csv = await res.text();
      const blob = new Blob([csv], { type: "text/csv;charset=utf-8;" });
      const url = URL.createObjectURL(blob);
 
      const a = document.createElement("a");
      a.href = url;
      a.download = fileName;
      a.click();
 
      URL.revokeObjectURL(url);
    } finally {
      setLoading(false);
    }
  }
 
  return (
    <button onClick={handleDownload} disabled={loading}>
      {loading ? "Exporting…" : "Download CSV"}
    </button>
  );
}

The component calls a thin Next.js route handler (/api/sheets-csv) that proxies the request server-side so your API key never reaches the browser:

// app/api/sheets-csv/route.ts
import { NextRequest, NextResponse } from "next/server";
 
export async function GET(req: NextRequest) {
  const { searchParams } = req.nextUrl;
  const userKey = searchParams.get("userKey");
  const sheet = searchParams.get("sheet");
 
  if (!userKey || !sheet) {
    return NextResponse.json({ error: "Missing params" }, { status: 400 });
  }
 
  const upstream = await fetch(
    `https://sheetsapi.gkit.mreshank.com/api/spreadsheets/${userKey}/${sheet}?format=csv`,
    {
      headers: { Authorization: `Bearer ${process.env.GKIT_API_KEY}` },
    }
  );
 
  if (!upstream.ok) {
    return NextResponse.json({ error: "Upstream failed" }, { status: 502 });
  }
 
  const csv = await upstream.text();
  return new NextResponse(csv, {
    headers: {
      "Content-Type": "text/csv",
      "Content-Disposition": `attachment; filename="${sheet}.csv"`,
    },
  });
}

Drop <DownloadCsvButton userKey="uk_abc123" sheetName="Contacts" /> anywhere in your app and users get a one-click export of live sheet data.

Combining with pagination parameters

?format=csv works alongside limit and offset if you only want a slice of the sheet:

# First 50 rows as CSV
curl "https://sheetsapi.gkit.mreshank.com/api/spreadsheets/uk_abc123/Orders?format=csv&limit=50&offset=0"

For a full export, omit limit — the default returns all rows.

What's next

Share