Zendesk to BigQuery
Explore answers support questions. Zendesk in BigQuery answers business questions, the ones that need tickets sitting next to revenue, product usage and churn.
Why bother at all
Be honest about the reason first, because the wrong reason leads to an expensive pipeline nobody queries.
Good reasons: joining ticket volume to account revenue, correlating support contacts with churn, feeding a company-wide dashboard that already lives in BigQuery, or retaining history longer than your helpdesk keeps it conveniently queryable.
Bad reason: a report Explore already produces. If the question is "what is our first response time by group", Explore answers it in five minutes and the warehouse answers it in three weeks.
Four routes from Zendesk into BigQuery
Availability and naming change over time, so check the current Zendesk documentation for what your plan includes.
| Route | Effort | Freshness | Best when |
|---|---|---|---|
| Managed data export from Zendesk | Low | Batch | It is on your plan and the schema fits |
| Third party ELT connector | Low to medium | Hourly or daily | You already run Fivetran, Airbyte or similar |
| Incremental export API plus your own loader | High | As often as you schedule | You need control, or the connector schema is wrong for you |
| Explore scheduled CSV, loaded manually | Very low | Poor | A one-off analysis, never a production pipeline |
Picking one
Start with whichever managed data export options your plan already includes. Free capability beats clever engineering.
If your company runs an ELT tool already, use the Zendesk connector. Fivetran, Airbyte, Stitch and the rest all have one, and they handle pagination, retries, schema drift and incremental state, which is genuinely most of the work. You pay per row or per connector and you skip a month of building.
Build it yourself when the connector schema does not carry the fields you need, when you want a specific refresh cadence, or when data residency rules mean the data can't pass through a third party. Building is a real project, not a weekend.
The incremental export pattern
If you do build it, the incremental export endpoints are the backbone. They exist precisely so you do not page through the entire ticket history every night.
# cursor based incremental ticket export
curl -s -u you@example.com/token:APITOKEN \
"https://acme.zendesk.com/api/v2/incremental/tickets/cursor.json?start_time=1735689600"The response carries a batch of tickets, an after_cursor and an end_of_stream flag. Loop until end_of_stream is true, persist the cursor, and start from that cursor on the next run. Store the cursor somewhere durable, because losing it means a full resync.
Two disciplines make the difference between a pipeline that runs for years and one that quietly drifts. Land the raw JSON responses in a staging table or a GCS bucket before you transform anything, so a schema surprise costs you a re-transform rather than a re-extract. And rerun a small overlap window each time, since exact boundary semantics are fiddly and a few duplicated rows are cheap when your merge key is the record id.
Rate limits are real. Check the current documented limits for the incremental endpoints and back off politely rather than retrying in a tight loop.
A schema that survives contact
The naive approach is one wide tickets table with every custom field as a column. It works for a month, then someone adds a field and your DDL is a migration.
ticket_id, field_id, value. New custom fields become rows, never schema changes. This one decision saves the most pain.Keep a field_definitions table alongside it, loaded from the ticket fields endpoint, so field_id resolves to a human title in your queries. See custom ticket fields for why ids and titles need keeping apart.
Partition the big tables by created or updated date and cluster on the ids you join. BigQuery bills by bytes scanned, and an unpartitioned audit table is the classic way to discover that.
Five things that will break it
field_definitions snapshot per load is your defence.closed_by_merge. Leave those in your volume counts and every ticket-per-customer figure is inflated by the duplicate rate, which typically runs 8% to 20% of a queue. Exclude the tag from volume metrics and keep the rows for auditing.Frequently asked questions
Is there a Zendesk BigQuery connector, or do you build it?
Both exist. A managed Zendesk BigQuery connector from an ELT vendor is the fast route into a Zendesk data warehouse, and building it yourself means the Zendesk incremental export API on a schedule.
What is the easiest way to get Zendesk data into BigQuery?
A third party ELT connector if your company already runs one. It handles pagination, retries and incremental state, which is most of the work.
Does Zendesk have a native BigQuery export?
There are managed export options on some plans, and the exact capability has changed over time. Check the current Zendesk documentation against your subscription before building anything.
What's the incremental export API?
A set of endpoints that return records changed since a cursor or timestamp, so you sync only what moved instead of paging the full history each run.
How should I model custom fields in BigQuery?
Long, not wide. One row per ticket per field, with a separate field definitions table for titles. New custom fields then become rows rather than schema migrations.
How do I handle merged tickets in warehouse reporting?
Merges tag the closed ticket closed_by_merge. Keep the rows for auditing but exclude the tag from volume and handle-time metrics, or those numbers read high.
Warehouse numbers only as clean as the queue
Duplicates inflate every count you load. Ticket Merger removes them at the source, before they reach BigQuery.
Start free trial14-day free trial. No credit card required.