Best Way to Sync Granola to Postgres (2026)

Intergalactic Data Labs
โ
โ
14 min read
Granola meeting notes land in Postgres one of five ways as of October 2026. The options are Airbyte's community source, Bruin's ingestr, Filament's manifest connector, a Zapier trigger, or a script against the public API. Transcript completeness, deletion handling, and how typed the destination tables end up are what decide between them. None of the big hosted ELT vendors ship a Granola connector yet.
Method | Transcripts | Incremental | Deletes | Tables |
|---|---|---|---|---|
Airbyte source | Paged, append only | Notes | No | Final tables plus metadata columns |
ingestr | Inline JSON column | Notes | No | notes and folders |
Filament | Paged, one row per item | Notes | Via full replace | Typed, add-only |
Zapier | In trigger payload | New notes only | No | You design |
Own script | Your code | Your code | Your code | You design |
What the Granola API gives you
Granola's public API, released in February 2026, is a REST interface for notes, transcripts, folders, and webhooks. The changelog shows it moved quickly through versions 1.1 to 1.5 this year, adding folders, access scopes, speaker attribution, a paged transcript endpoint, and private notes.
The API only hands over finished meetings, and only the ones your key can see. Granola's introduction is blunt about the first rule. "The API only returns notes that have a generated AI summary and transcript." Notes still processing are absent from List Notes and return a 404 from Get Note.
Keys come in two shapes. A personal key reads notes you own or that were shared with you, while a workspace key reads public workspace notes and spaces with API access enabled. Notes are private by default, and Granola's API help page says workspace keys cannot read private folders that were never shared.
Four endpoints carry the data. List Notes pages through IDs and titles with an updated_after filter, Get Note adds summaries, attendees, and folder membership. Get Transcript pages through transcript items with speaker and timestamps, and List Folders returns the tree through parent_folder_id.
Two limits shape every tool built on top. Rate limits are 5 requests per second sustained with a short burst allowance of 25, applied per user or workspace, per the introduction.
The second limit is transcript size. Get Note returns a 413 with the code TRANSCRIPT_TOO_LARGE on long meetings, which is why the paged transcript endpoint exists.
Plan gating is the other hard wall. Granola's pricing page lists API access as not included on Basic and included on Business and Enterprise. There is no sandbox, so Granola's own advice is to test against a dedicated folder with a handful of notes.
What decides the right method
Five criteria separate a sync you can trust from one you will rebuild. They come straight from how the Granola API behaves, so they apply to every tool.
Criterion | Why it matters for Granola |
|---|---|
Transcript completeness | Inline transcripts can fail with a 413, so only the paged endpoint reads every meeting |
Keyless transcript rows | Transcript items have no ID, so an upsert cannot deduplicate them |
Deletion handling | updated_after never reports a deleted note, so something must take a fresh snapshot |
Request budget | Each note costs one detail call and at least one transcript call per full run inside a shared 5 per second limit |
Destination shape | Typed columns and a primary key per table, or one JSON blob you parse later |
Deletion deserves the extra sentence. Airbyte's Granola docs state that the API does not return deleted notes or mark them as deleted, so no cursor-based read, from any vendor, will ever remove a row. The method either schedules a full replace or you accept drift.
Upstream retention makes that worse. Granola lets workspaces auto-delete transcripts after as little as one day, and the deletion is permanent. If transcripts matter, the sync is the backup, so it needs to run before the retention window closes.
Every way to sync Granola to Postgres
Two community connectors, one event trigger, one hand-built path, and one typed-table engine actually run this route today. Fivetran, Hevo, Stitch, and Estuary all return nothing for Granola in their connector catalogs, and dlt has no verified source, so those paths mean building the connector yourself.
Tool | Granola connector | Deployment | License | Pricing unit |
|---|---|---|---|---|
Airbyte | Yes, alpha, community | Cloud or self-hosted | ELv2 | Credits |
ingestr | Yes, notes and folders | CLI you run | FSL-1.1-ALv2 | Free CLI |
Filament | Yes, alpha | Binary, Go library, Helm | Apache 2.0 | None from the tool |
Zapier | Two triggers | Hosted | Proprietary | Tasks |
Fivetran | No, Connector SDK | Hosted | Proprietary | Monthly active rows |
dlt | No, REST API source | Library you run | Apache 2.0 | Free library, dltHub paid |
Airbyte
Airbyte's Granola source reached version 1.0.0 on October 2, 2026, and its metadata file marks it alpha with community support under ELv2. The notes stream is incremental on updated_at, while detailed notes and transcripts are full refresh. It is a real, maintained connector that runs in both Cloud and the self-hosted Core edition.
The limits are documented on the same page. There is no folders stream, every sync re-requests details for every note in range, and notes with an oversized transcript produce no detailed_notes record at all. Deleted notes stay until a full refresh with overwrite. The Postgres destination docs add four airbyte metadata columns and warn that Postgres "is not a data warehouse" and suits volumes under 10 GB.
ingestr
Bruin's ingestr supports Granola as a source for notes and folders, with incremental notes merged on updated_at and a Postgres destination. One CLI command moves both tables. Transcripts arrive inline through include=transcript and land as a JSON column inside the notes table, which the docs qualify with "when Granola returns them". There is no per-segment transcript table, and the license is the Functional Source License, not Apache.
Zapier
Granola's Zapier app offers two triggers, one when a note is added to a folder and one when a note is shared to Zapier. The payload includes the summary in Markdown and the full transcript, per Granola's Zapier guide, which makes it the fastest path to one row per meeting.
The trigger only fires for notes added after the Zap exists, so there is no backfill, no folder sync, and no re-send when a summary is later edited. Zapier bills per task, so cost tracks meeting volume.
Webhooks or your own script
Granola's webhooks fire on note.generated, note.edited, and note.access_granted, carry no note content, and retry for four days. Pair them with a receiver that calls Get Note and Get Transcript, or poll List Notes with updated_after on a schedule. Either way you write the pagination, the 413 fallback, the backoff, the idempotent upsert, and the deletion reconciliation yourself.
Where Filament fits
Filament reads Granola through a YAML HTTP manifest, writes typed Postgres tables, and clears every criterion above except maturity, which its docs state plainly. Here is how it scores against each one, with the connector's source page and the Postgres sink page as the evidence.
Criterion | What Filament does |
|---|---|
Transcript completeness | Reads the paged endpoint 100 items at a time, one row per item |
Keyless transcript rows | Docs direct full replace for transcripts |
Deletion handling | Full replacement removes deleted notes and refreshes folder membership |
Request budget | 5 requests per second enforced in the manifest, 429 retried honoring Retry-After |
Destination shape | Typed tables, primary keys preserved, jsonb for nested objects, add-only evolution |
Transcripts are where the design shows. The source page says to "Use full replace for transcripts. Granola does not give transcript items stable IDs, and speaker names and timestamps are not guaranteed to be unique." That is the right call for an API whose transcript schema has no item ID, and it is the same reason Airbyte's transcript stream has no primary key.
The Postgres sink is beta and derives native column types from the source schema, preserving primary keys and NOT NULL constraints. Upserts run as a binary COPY into a temporary table merged with ON CONFLICT inside one transaction, so retries converge. New source fields appear through ADD COLUMN IF NOT EXISTS, and evolution is add-only.
Every batch is also checksummed in memory. Per the integrity and checkpoints page, a CRC32-C mismatch between the pipeline and the sink fails the run, and checkpoints after each written batch let an interrupted incremental run resume.
Maturity is the fifth criterion and the honest caveat. The Granola source is alpha today, per its docs, checked against Granola's API docs and tested with mock responses but not yet against a live account, with the Postgres sink at beta. Filament is Apache 2.0 and pre-1.0, and Galaxy builds and maintains it. No speed claim applies to this route, because Filament's benchmark program has only measured database-to-database full loads.
How to sync Granola to Postgres with Filament
One YAML file, one API key, and two pipelines cover the whole route. The steps below follow the CLI guide and the installation page, with Granola fields substituted by the documented rule that connector fields become config keys and secrets can read from the environment.
Step 1. Create a Granola API key
In Granola open Settings, then Connectors, then API keys, and create a key with the scopes you need. A workspace key covers team folders, while a personal key is the only way to read your own private notes. Export it as GRANOLA_API_KEY and keep a Postgres DSN in POSTGRES_SINK_DSN.
Step 2. Install the CLI
The local CLI runs on file-backed SQLite with an in-process event bus, so it needs no Postgres or NATS of its own. Homebrew users can run brew install galaxy-io/tap/filament instead.
Step 3. Describe the route in filament.yaml
This file lives at ~/.config/filament/filament.yaml and mirrors the documented local config shape.
The snapshot pipeline is the one that removes deleted notes and rebuilds transcripts cleanly. The catch-up pipeline sends the saved updated_at as updated_after with a five-minute lookback under the checkpoint key notes_updated_at, so it stays cheap between snapshots.
Step 4. Discover and run
Discovery lists the five resources the manifest exposes. The first run creates a schema named after the source connection, so granola here, then one typed table per resource, and streams progress until it finishes. Partial data is visible mid-run because batches commit as they apply, which is normal for this sink.
Step 5. Verify in Postgres
The transcripts table holds one row per spoken segment with speaker as jsonb, so attribution and diarization labels are queryable. Summaries sit in note_details.summary_markdown, and attendees and folder_membership are jsonb columns you can unnest.
Step 6. Schedule it
Run the catch-up pipeline hourly and the snapshot nightly, or attach a schedule through the pipelines API. Teams embedding the engine call app.Run from Go per the embedded guide, and Kubernetes with Helm is the documented production path.
Common pitfalls with any tool
Most failed Granola syncs are scope or timing problems, not tool problems. These five show up regardless of which method you pick.
An empty result set usually means the key's scopes do not cover the folder, or an admin turned off public folder access for the workspace.
A meeting that just ended is missing because the summary has not generated yet, so do not alert on a lag shorter than Granola's processing time.
Transcripts duplicate when a tool upserts keyless rows, because repeated keyless rows accumulate on every full read unless the write is a replace.
The 5 per second limit is shared across every client on the same key scope, so a sync, an MCP session, and a Zap all draw down the same quota.
Private notes come back null from workspace keys, so a sync that needs the owner's own notes has to run under that owner's personal key.
Which method fits which job
Pick by the constraint that breaks first. For a team that wants typed tables, full transcripts, and deletions handled without writing code, Filament is the pick, tested first in a dedicated folder as Granola itself advises.
Job | Pick | Why |
|---|---|---|
Typed tables with full transcripts and folders | Filament | Paged transcripts, replace-based deletion, add-only typed schema |
Already on Airbyte Cloud or Core | Airbyte | Maintained alpha source, incremental notes |
Notes and folders only, one command | ingestr | Merge on updated_at, transcript as JSON column |
One row per new meeting into an existing app | Zapier | Payload carries summary and transcript |
Custom shape or near real-time | Webhooks plus script | Three events, four-day retry |
Related guides
Best Way to Sync Stripe to Snowflake (2026) applies the same criteria to a billing route.
REST API to Postgres with a YAML manifest shows how a manifest like Granola's is written.
Full Load vs Incremental vs CDC explains the two read modes used above.
Best Way to Sync Notion to Postgres (2026) and Best Way to Sync GitHub to Postgres (2026) are the next manifest routes in this series.
Introducing Filament is the announcement with the deployment options.
Frequently asked questions
Does Granola have an API?
Yes. Granola shipped a public REST API in February 2026. It lists notes, returns note details with summaries and attendees, pages through transcripts, lists folders, and reads webhook configuration. Every call is a bearer request to public-api.granola.ai with a key that starts with grn_. Keys require a Business or Enterprise plan, and there is no sandbox environment.
Which Granola plan do I need to sync notes to a database?
Business or Enterprise. Granola's pricing page lists API access as not included on Basic and included on Business and Enterprise. Zapier and the native Notion, Slack, and CRM integrations also sit on Business and above. Enterprise admins can additionally restrict which access scopes members may put on a key.
Can I get full meeting transcripts out of Granola?
Yes, through the paged transcript endpoint. Get Note can return a transcript inline, but returns a 413 with the code TRANSCRIPT_TOO_LARGE when the meeting is long. The dedicated endpoint pages through transcript items 100 at a time, each with speaker, text, start time, and end time, so a tool that uses it reads every meeting in full.
Why does the Granola API return no notes?
Almost always scope. A key only returns notes that match its access scopes, notes are private by default, and workspace keys cannot read private folders. The API also hides any note that has no generated summary and transcript yet. Check the key's scopes, the folder's visibility, and whether the meeting has finished processing before suspecting the tool.
Does Airbyte support Granola?
Yes. Airbyte published a community Granola source at version 1.0.0 on October 2, 2026. It is alpha, manifest-only, and syncs notes incrementally on updated_at, plus detailed notes and transcripts as full refreshes. It has no folders stream, cannot report deletions, and its Postgres destination adds four airbyte metadata columns to every table.
Can Fivetran or dlt sync Granola to Postgres?
Not with a built-in connector. Fivetran's connector catalog has no Granola entry, so you would write one with its Connector SDK. dlt has no Granola verified source, so you would configure its generic REST API source by hand. Both routes mean you own the pagination, the 413 fallback, and the lookback logic yourself.
How does Filament handle Granola transcripts?
It reads them from the paged transcript endpoint, 100 items per request, and writes one row per transcript item with the note ID, speaker, text, and start and end times. Because Granola gives transcript items no stable ID, the docs tell you to write transcripts with full replace so every run produces a clean snapshot rather than accumulating duplicates.
Does Filament sync Granola incrementally?
Notes do. Each run sends the saved updated_at checkpoint as updated_after with a five-minute lookback, under the key notes_updated_at. Note details, transcripts, folders, and webhook configuration are read in full. Incremental reads cannot see deletions, so pair the incremental pipeline with a scheduled full replace run.
What tables does a Granola to Postgres sync produce?
With a typed-table tool you get notes, note_details, transcripts, folders, and optionally webhook_endpoints. Notes carry IDs, titles, owners, and timestamps. Note details add summary text and Markdown, attendees, calendar details, and folder membership as jsonb. Transcripts hold one row per spoken segment. Folders include parent_folder_id so you can rebuild the hierarchy in SQL.
Is Granola's CSV export enough instead of a sync?
Only for a one-off backup of your own notes. The export is emailed within a few hours, is limited to one request per account every 24 hours, includes only notes you own, and has no admin or workspace-wide version. Enterprise plans disable it by default. A team table in Postgres needs the API.
More articles
Questions
Answered
FAQ
What does Galaxy do?
What is Filament?
What is enterprise context management?
What does working with Galaxy look like?
How do you handle security and compliance?
Why does Galaxy build in the open?
Company
Copyright ยฉ 2026 Galaxy. All rights reserved.