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.
Looking to download a sheet by hand? How to save a Google Sheet as CSV covers the File menu, per-tab URL exports and the Excel encoding fix. This post is the programmatic route.
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,proFrom 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
GKit is currently undergoing platform maintenance
Be the first to know when all Google Workspace tools are back online, plus get our engineering deep-dives and product announcements directly to your inbox.