Elixir client for the Granola API.
def deps do
[
{:granola, "~> 1.0.0"}
]
endclient = Granola.new(api_key: "grn_YOUR_API_KEY")API keys can be created in Granola under Settings → API (Business/Enterprise plans).
{:ok, result} = Granola.Notes.list(client)
result.notes # list of note summaries
result.hasMore # true if there are more pages
result.cursor # pass as :cursor to fetch the next pageFilter and paginate:
{:ok, result} = Granola.Notes.list(client,
created_after: ~D[2026-01-01],
page_size: 30
)
# Next page
{:ok, next} = Granola.Notes.list(client, cursor: result.cursor)Available filters: :created_before, :created_after, :updated_after,
:cursor, :page_size (1–30, default 10).
{:ok, note} = Granola.Notes.get(client, "not_1d3tmYTlCICgjy")
note.id # "not_1d3tmYTlCICgjy"
note.title # "Quarterly yoghurt budget review"
note.summary_text # plain text summary
note.summary_markdown # markdown summary
note.owner # %{name: "...", email: "..."}
note.attendees # list of %{name, email}
note.calendar_event # associated calendar event or nil
note.web_url # link to note in Granola web appRequest the full transcript:
{:ok, note} = Granola.Notes.get(client, "not_1d3tmYTlCICgjy", include: :transcript)
for segment <- note.transcript do
IO.puts("#{segment.speaker.source}: #{segment.text}")
endEach transcript segment has :speaker (with :source of "microphone" or
"speaker"), :text, :start_time, and :end_time.
Notes without a generated AI summary return a 404 error.
Granola.Notes.stream/2 lazily paginates through all notes, fetching the next
page only when needed:
Granola.Notes.stream(client, created_after: ~D[2026-01-01])
|> Stream.each(fn note -> IO.puts(note.title) end)
|> Stream.run()Accepts the same filter options as list/2, except :cursor and :page_size.
All functions return {:ok, result} on success or {:error, reason} on failure:
case Granola.Notes.get(client, id) do
{:ok, note} -> note
{:error, {404, _body}} -> :not_found
{:error, {401, _body}} -> :unauthorized
{:error, %Req.TransportError{} = err} -> {:network_error, err}
endUse Req.Test to stub HTTP calls without making real requests:
client = Granola.new(api_key: "grn_test", plug: {Req.Test, __MODULE__})
Req.Test.stub(__MODULE__, fn conn ->
Req.Test.json(conn, %{
"notes" => [],
"hasMore" => false,
"cursor" => nil
})
end)
assert {:ok, result} = Granola.Notes.list(client)The Granola API allows 25 requests per 5 seconds (burst) or 5 requests/second
sustained. Retries are disabled by default in the client; implement your own
retry/backoff if needed (or pass retry: :safe_transient to Granola.new/1).