Skip to content

Handle Grain API rate limiting in the daily meeting fetch - #8

Draft
dplakon wants to merge 1 commit into
mainfrom
fix/grain-rate-limit-handling
Draft

Handle Grain API rate limiting in the daily meeting fetch#8
dplakon wants to merge 1 commit into
mainfrom
fix/grain-rate-limit-handling

Conversation

@dplakon

@dplakon dplakon commented Jul 31, 2026

Copy link
Copy Markdown

Problem

Today's grainiac-orchestrator run could not get off the ground: scripts/fetch_daily_meetings.py today failed with HTTP Error 429: Too Many Requests on every attempt, before returning a single meeting.

Two independent causes:

  1. No retry or backoff. _get raised on the first 429, aborting the whole run.
  2. Unbounded pagination. list_all_recordings walked the entire recording history (up to 100 pages × 20 recordings) on every run, even though the caller only wanted one day. The Grain API advertises a budget of 30 requests per window (x-ratelimit-limit: 30), so the fetch reliably exhausted its quota partway through pagination — it consistently died on page 2 of a fresh run because prior runs had already drained the window.

The API's afterDatetime / beforeDatetime parameters do not filter server-side (confirmed: a request scoped to 2026-07-30 still returned recordings from 2026-07-27), which matches the existing note in the orchestrator skill. But results are returned newest-first, so a single-day fetch never needs to read past the target date.

Changes

scripts/grain_client.py

  • New _request helper with exponential backoff on 429 and 5xx, honouring Retry-After when present.
  • Proactive throttle: pause when x-ratelimit-remaining drops to the threshold, rather than waiting to be rejected.
  • get_transcript_text now shares the same retry path (it previously had its own un-retried request).
  • list_all_recordings accepts stop_before_date and stops paging once an entire page predates the target day.

scripts/fetch_daily_meetings.py

  • Passes the resolved target date as stop_before_date.

Result

A single-day fetch now costs 2 list requests instead of up to 100. Verified end to end against the live API — the run that previously failed now completes:

Resolved 'today' to 2026-07-30 (timezone: America/Los_Angeles)
Fetched 40 total recordings
Found 4 recordings on 2026-07-30
Found 4 external customer meetings

Follow-ups for a human (not fixed here — these are config, not code)

  • GRAINIAC_NOTION_DATABASE_ID is stale. The configured ID (3a043263616d803d98de000c5b916204) returns 404 from the Notion API. The database the pipeline actually writes to is "Account Management" (3a043263-616d-80bd-a6da-f099fa90796a), which holds all 44 account pages and follows the Grainiac page template. The env var should be repointed.
  • GRAINIAC_NOTION_TITLE_PROPERTY is unset. That database's title property is Account name, but notion_client._title_property() defaults to Company, so find_company_page would filter on a non-existent property. The var needs to be set to Account name.

Conversation: https://app.warp.dev/conversation/0c3b3ac6-8718-4b83-990b-d450a1868f9a

Run: https://oz.warp.dev/runs/019fb68b-2f46-73e6-b14c-ca2dc1614964

This PR was generated with Oz.

The daily meeting fetch was failing with HTTP 429 before it could return
any meetings. Two causes:

1. `_get` had no retry or backoff, so a single 429 aborted the whole run.
2. `list_all_recordings` walked the entire recording history (up to 100
   pages) on every run, even when the caller only needed one day. The
   Grain API allows 30 requests per window, so the fetch exhausted its
   budget partway through pagination.

Changes:

- Add retry with exponential backoff (honouring `Retry-After`) for 429
  and 5xx responses, and proactively pause when `x-ratelimit-remaining`
  is nearly exhausted.
- Route `get_transcript_text` through the same retry path.
- Add `stop_before_date` to `list_all_recordings`. Recordings come back
  newest-first and the API's date parameters are ignored server-side, so
  pagination can stop once a whole page predates the target day.
  `fetch_daily_meetings.py` now passes the target date, taking a
  single-day fetch from potentially 100 requests down to two.

Co-Authored-By: Oz <oz-agent@warp.dev>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant