Skip to content
MinuteHaul

How to export transcripts from the Fireflies API (GraphQL, with a free script)

Fireflies' web app downloads transcripts one meeting at a time, and on the Free plan downloads are locked. The official GraphQL API has no such lock: with an API key you can list every meeting and pull each transcript. This guide covers the two queries you need, the paging and rate limits, and a free script that saves each meeting as Markdown.

Disclosure: MinuteHaul is our paid exporter for Fireflies (beta) and tl;dv. The script below is free and doesn't need it. Facts here are from Fireflies' API docs, checked Oct 26, 2026.

1. Get an API key

2. The two queries

# list meetings, max 50 per call; page with skip
query($limit:Int,$skip:Int){ transcripts(limit:$limit, skip:$skip){ id title date duration } }

# one meeting's transcript
query($id:String!){ transcript(id:$id){ sentences{ speaker_name text start_time end_time } summary{ overview action_items keywords } } }

The transcripts query also takes fromDate/toDate (ISO 8601), keyword, mine, organizers and participants filters if you only want part of the archive (Fireflies transcripts query).

3. Rate limits you will hit

PlanPer minutePer day
Free30500
Pro605,000
Business60 per paid seat, pooled (up to 1,200)No daily cap
Enterprise60 per paid seat, pooled (up to 2,400)No daily cap

Per Fireflies' limits page: a Free user's first API request starts a 14-day trial at Pro limits; the daily quota resets at midnight UTC; on a 429, wait the seconds in Retry-After, because requests sent during a block still count toward the next window. A full export costs one request per 50 meetings listed plus one per transcript, so on Free after the trial, an archive of more than about 490 meetings takes more than one day.

4. A free script (Node 18+, no dependencies)

Saves each meeting as YYYY-MM-DD-title-id.md with speaker names and timestamps. Run with FIREFLIES_API_KEY=your_key node fireflies-export.mjs ./out.

// fireflies-export.mjs: save every Fireflies transcript as Markdown.
// Usage: FIREFLIES_API_KEY=xxx node fireflies-export.mjs ./out
import { mkdir, writeFile } from "node:fs/promises";
import { join } from "node:path";

const KEY = process.env.FIREFLIES_API_KEY;
const OUT = process.argv[2] || "./fireflies-export";
if (!KEY) { console.error("Set FIREFLIES_API_KEY"); process.exit(1); }
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

async function gql(query, variables) {
  for (;;) {
    const res = await fetch("https://api.fireflies.ai/graphql", {
      method: "POST",
      headers: { "Content-Type": "application/json", Authorization: `Bearer ${KEY}` },
      body: JSON.stringify({ query, variables }),
    });
    if (res.status === 429) { console.log("429, waiting 60s"); await sleep(60_000); continue; }
    const json = await res.json();
    if (json.errors) throw new Error(JSON.stringify(json.errors));
    await sleep(1_100); // ~55 requests/minute, under Pro's 60
    return json.data;
  }
}

const LIST = `query($limit:Int,$skip:Int){transcripts(limit:$limit,skip:$skip){id title date duration}}`;
const ONE = `query($id:String!){transcript(id:$id){sentences{speaker_name text start_time}}}`;

const slug = (s) => (s || "untitled").toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 60);
const ts = (sec) => new Date(sec * 1000).toISOString().slice(11, 19);

await mkdir(OUT, { recursive: true });
let skip = 0, saved = 0;
for (;;) {
  const { transcripts } = await gql(LIST, { limit: 50, skip });
  if (!transcripts?.length) break;
  for (const t of transcripts) {
    const { transcript } = await gql(ONE, { id: t.id });
    const day = new Date(Number(t.date)).toISOString().slice(0, 10);
    const lines = (transcript?.sentences || []).map(
      (s) => `**${s.speaker_name || "Speaker"}** [${ts(s.start_time)}]: ${s.text}`
    );
    const md = `# ${t.title}\n\nDate: ${day}\n\n${lines.join("\n\n")}\n`;
    await writeFile(join(OUT, `${day}-${slug(t.title)}-${t.id}.md`), md);
    console.log(`saved ${++saved}: ${t.title}`);
  }
  skip += 50;
}
console.log(`Done: ${saved} transcripts in ${OUT}`);

On Free past the trial, raise the 1,100 ms sleep to about 2,100 ms. Add summary{overview action_items} to the transcript query to keep Fireflies' AI notes.

Skip the code

Pick Fireflies in the MinuteHaul web exporter (beta). It runs this same loop from your browser with Fireflies' official API, paces itself under the limits, backs off on 429s, works on the Free plan, and gives you one ZIP with a Markdown file per meeting plus an index. Your 10 most recent meetings are free. Buy unlimited: $19 one-time with a 14-day money-back guarantee.

FAQ

Where do I get a Fireflies API key? In the Fireflies web app, open Integrations, then Fireflies API, and copy the key. Send it as a Bearer token in the Authorization header to https://api.fireflies.ai/graphql.

How many transcripts can one query return? The transcripts query returns at most 50 per call. Use the skip argument to page through the rest, then fetch each meeting's sentences with the transcript query.

What are the Fireflies API rate limits? Per Fireflies' docs: Free 30 requests a minute and 500 a day, Pro 60 a minute and 5,000 a day, Business and Enterprise 60 a minute per paid seat pooled across the team with no daily cap. A Free user's first API request starts a 14-day trial at Pro limits. The daily quota resets at midnight UTC.

What should I do on a 429? Wait the number of seconds in the Retry-After header before retrying. Fireflies' docs say requests sent during a block still count toward the next window, so retrying in a tight loop can keep you blocked.

Related guides

Last checked: Oct 26, 2026. Fireflies' API and plans change; verify against Fireflies' docs before relying on details here.