How to Export Gong Call Data Without Hitting API Rate Limits
Yes, but you have to work with two limits at once, not one. Gong's public API throttles requests to 3 per second across every endpoint, and separately caps the account at 10,000 API calls per day, and the second limit is the one that actually stalls a bulk export. Most integrations only pace themselves against the per-second cap, and burn through the daily quota partway through a historical pull. The fix is batching the right endpoints, paging with the cursor Gong gives you instead of guessing offsets, and backing off on a real Retry-After header instead of a fixed sleep.
This is a common failure. A Gong customer on the company's own community forum described trying to pull "around 7533 calls" and getting "api data for only 100 calls" back, with no clear indication of why the extraction stopped short (Gong Visioneers community). Another practitioner on the same forum reported the detailed call-data endpoint returning only 89 calls despite specifying no date or ID filter at all. Neither hit a documented bug. Both hit an endpoint that pages by design and a quota that runs out silently.
The two limits, and which one actually bites
Gong's REST API enforces a per-second throttle of 3 calls, returning HTTP 429 with a Retry-After header when you exceed it, and a separate per-day quota of 10,000 calls per account. The distinction matters because most home-grown scripts only handle the first one. A sleep(0.4) between requests keeps you under 3/sec, but does nothing once the daily counter runs out, and Gong doesn't visibly warn you as you approach it. Airbyte's Gong connector documents both limits directly, and notes that even its own connector "doesn't pace itself against the 10,000-calls-per-day quota, so a large backfill or a frequent sync schedule can still exhaust it" (Airbyte source docs for Gong). If a maintained connector built specifically for this problem calls that out as a known gap, a first-pass export script will hit it faster.
Step 1: use the right endpoint for what you actually need
Gong splits call data across a few endpoints, and picking the wrong one is the fastest way to waste your quota:
GET /v2/callsreturns lightweight call metadata (IDs, timestamps, parties) and is the cheapest way to enumerate what exists in a date range.POST /v2/calls/extensivereturns full call detail (media info, content, parties, context) for a list of call IDs you already have. It's expensive per call, so only request it for the IDs you actually plan to analyze.POST /v2/calls/transcriptreturns transcript sentences and timing, and accepts a batch of call IDs in a single request rather than one call ID per request.
The mistake that burns quota fastest is calling /v2/calls/extensive or /v2/calls/transcript once per call ID in a loop. Both endpoints accept an array of IDs, and Airbyte's connector batches up to 100 call IDs into a single /v2/calls/transcript request instead of sending a hundred separate ones. That's a 100x reduction in call count for the same data, and it's the single biggest lever available before you touch pacing at all.
Step 2: page with the cursor, not a page number
GET /v2/calls and the extensive endpoints return a cursor in the response body when more results exist; you pass that cursor back on the next request rather than incrementing an offset yourself. This matters because Gong's cursors are time-bound. Practitioners on Gong's own community forum have reported a "cursor has expired" error when a paging loop runs too slowly or is paused and resumed later (same Visioneers thread). Treat an expired cursor as a signal to restart that page range from its date boundary, not as a bug to retry blindly; retrying an expired cursor just returns the same error.
Step 3: throttle to 3/sec and respect Retry-After on 429
A queue with a fixed delay between requests (roughly 350ms, to leave headroom under 3/sec) handles the per-second limit. The daily limit needs separate bookkeeping. Track a running count of calls made today, stop proactively before 10,000, and resume the next day rather than discovering the cutoff via a wall of 429s. When Gong does return a 429, read the Retry-After header and wait exactly that long. A fixed backoff guesses wrong in both directions, either wasting time or getting rate-limited again immediately.
A historical pull that actually finishes at Denali Freight
Lior Andrade is a RevOps analyst at Denali Freight, a logistics SaaS company that wanted three years of Gong calls in its warehouse to correlate deal size against topics discussed on the call. The first attempt was a simple loop: list calls with /v2/calls, then hit /v2/calls/extensive once per call ID for detail.
Lior, in the team's #data-eng channel, day one: Pulled the last 90 days fine. Kicked off the full 3-year backfill overnight and woke up to about 4,000 calls done and a wall of 429s after that.
The loop had no daily-quota tracking at all; it only slept between requests, so it sailed straight through 10,000 calls sometime around 3am and spent the rest of the night getting rate-limited on every request. Lior's fix matched the steps above: batch call IDs into groups of 100 for the transcript endpoint instead of one at a time, track a running daily counter and stop the job at 9,500 to leave headroom, and pick the run back up the next day from the last completed cursor.
Lior, three days later: Runs about 8 minutes now instead of erroring out, and it stops itself before the daily cap instead of me finding out from a 429 in the morning. Three years took four days total, which is fine for a one-time backfill.
A one-time backfill split across a few days is a reasonable trade. A team that needs Gong calls flowing continuously, not as a periodic backfill job, runs into a different problem. Someone has to own that script, watch the daily quota, and re-run it on a schedule indefinitely.
Where the DIY export stops making sense
The approach above genuinely works for a bounded, one-time historical pull. It stops making sense once the requirement shifts from "get me the last three years once" to "keep every new call available for analysis as it happens":
- Someone owns the quota math forever. Rate limits don't change, but call volume grows, and a script sized for last year's volume starts colliding with the daily cap again as the team scales.
- A rate-limited script is still just an export. Getting transcripts into a warehouse doesn't dedupe a request mentioned on five different calls into one count, or match the speaker on the call to the same person who filed a support ticket last month.
- Nobody wants to re-run a backfill script for governance. Compliance and security reviews tend to prefer a service with a standing connection over a cron job someone wrote that holds an API key.
That's the point where a growing number of teams stop rate-limiting their way through a historical pull and switch to something that ingests continuously. That's the category Modem is built for. Modem's Gong integration reads transcripts from the workspaces you connect starting from the moment you connect it. There's no bulk backfill to budget a quota against, because it isn't trying to pull three years of history in one run. New calls arrive as they're recorded, and every speaker resolves to the same person and account Modem already knows from Slack, support tickets, and email, so a request mentioned on a call joins the same topic as the version of it that showed up in a support ticket last week. It reads transcripts only, never audio, and never writes back into Gong. We build Modem, so weigh the recommendation against your actual use case: a one-time export for a warehouse analysis is genuinely a scripting problem, not a Modem problem. Standing visibility into what customers say on every call, correlated with what they say everywhere else, is the different job.
If the goal is finding one feature request across every call rather than exporting data wholesale, that's a related but distinct problem covered in how to search every Gong call for a feature request. And if the blocker is Gong seats rather than API limits, see how to give your product team Gong call context without a Gong seat.
The smallest version you can start this week
Batch call IDs into groups of 100 for the transcript endpoint, add a running counter that stops the job at 9,500 daily calls instead of waiting for a 429, and store the last completed cursor so a resumed run doesn't start over. That alone turns an overnight failure into a script that finishes on a schedule you control.
