Export Typeform to Power BI: CSV, Google Sheets, or the Responses API
← All posts
GuideOct 29, 2026· 9 min read

Export Typeform to Power BI: CSV, Google Sheets, or the Responses API

To export Typeform to Power BI, pick one of three routes. For a one-off report, download your responses as CSV or XLSX from Results and load the file in Power BI Desktop. For a feed that keeps itself current without code, turn on Typeform’s Google Sheets integration and point Power BI’s Google Sheets connector at that spreadsheet. For full control and scheduled refresh in the Power BI service, call the Typeform Responses API from Power Query. Power BI has no built-in Typeform connector, so every option goes through a file, a sheet, the API, or a paid third-party connector.

This guide walks through each route, the Power Query code for the API option, and how to fold scanned or PDF forms into the same report. If you need the raw data outside Power BI, the Export Typeform to JSON guide covers the API and downloads in more depth.

Route 1: Download a CSV or XLSX

This is the fastest way to get a chart in front of someone.

  1. In Typeform, open the form and go to Results, then Responses.
  2. Select all rows (or apply a filter), click the export icon, and choose CSV or XLSX. Typeform’s export help page notes that large exports arrive by email instead of downloading straight away.
  3. In Power BI Desktop, use Get data and pick Text/CSV or Excel workbook, then Transform Data to clean up types.

A few quirks of the export are worth knowing before you build measures on it:

  • Multi-select questions do not get one column. Each answer choice becomes its own column instead, so “Which features do you use?” with eight options becomes eight columns you will probably want to unpivot.
  • Yes/No questions export as 1 and 0.
  • Start Date and Submit Date are in UTC, while the Typeform UI shows your local time. Convert in Power Query if your report is local.
  • Statements and group titles are not included, because they collect no answers.

The weak point is freshness. The file is a snapshot, and refreshing the report means downloading again. If you save each new export over the old one in OneDrive or SharePoint and connect to it there, the Power BI service can refresh it without an on-premises gateway, but someone still has to do the download.

Route 2: Google Sheets as the bridge

If you want new responses to show up on their own and nobody on the team writes M code, use Google Sheets in the middle.

On the Typeform side, open the form, go to the Connect panel, and connect Google Sheets. You can create a new spreadsheet or reuse one, and Typeform adds one tab per form. According to Typeform’s setup guide, each submission becomes a row with a column per question plus Submitted At and Token, the unique response ID.

On the Power BI side, Microsoft’s Google Sheets connector is generally available. Choose Get data, search for Google Sheets, paste the spreadsheet URL, sign in with a Google account that can open the sheet, and select the tab.

Limits to plan for:

  • When you connect a form that already has responses, Typeform only backfills them for forms with up to 1,000 results, and it does not backfill partial responses. Older history needs a CSV import on top.
  • Typeform’s guide warns the integration is deactivated if the spreadsheet reaches 2 million cells, counting other tabs.
  • The connector supports Import only, not DirectQuery, so the report is as fresh as its last scheduled refresh.
  • If you send partial responses to integrations, a respondent can create two rows: one at the partial submit point and one on completion. Deduplicate on Token or filter partials in Power Query.

Route 3: The Responses API from Power Query

This route keeps Typeform as the only source of truth and gives you every field the API returns, including hidden fields and calculated scores. It takes a little more setup, mostly because of how the Power BI service handles web sources.

Before you start

  • Create a Typeform personal access token under Account, then Personal tokens, and give it read-only scopes for responses and forms. Typeform’s token docs are blunt that anyone holding a token can act on your data, and with this method the token sits in the query text, so anyone who can open the .pbix can read it.
  • Check where your account stores responses. Typeform’s EU data center page explains that querying responses on the wrong domain returns empty results. US accounts use api.typeform.com; EU accounts use api.eu.typeform.com, or api.typeform.eu if they were onboarded on the newer EU data center.

The query

In Power BI Desktop choose Get data, then Blank query, open the Advanced Editor, and paste this:

let
    BaseUrl = "https://api.typeform.com",
    FormId = "u6nXL7",
    Token = "tfp_your_token_here",

    GetPage = (before as nullable text) as list =>
        let
            Params = [page_size = "1000"]
                & (if before = null then [] else [before = before]),
            Body = Json.Document(
                Web.Contents(
                    BaseUrl,
                    [
                        RelativePath = "forms/" & FormId & "/responses",
                        Query = Params,
                        Headers = [Authorization = "Bearer " & Token]
                    ]
                )
            )
        in
            Body[items],

    Pages = List.Generate(
        () => GetPage(null),
        each List.Count(_) > 0,
        each GetPage(List.Last(_)[token])
    ),

    Responses = Table.FromRecords(
        List.Combine(Pages),
        {"response_id", "token", "landed_at", "submitted_at", "hidden", "calculated", "answers"},
        MissingField.UseNull
    )
in
    Responses

The Retrieve responses endpoint returns at most 1,000 items per page, newest first. Passing the last item’s token as before walks back through older pages until an empty page comes back. Only completed responses are returned unless you set response_type. Typeform also notes that submissions from roughly the last 30 minutes may not appear yet, which rarely matters for a daily refresh.

When Power BI asks how to connect to https://api.typeform.com, choose Anonymous. The token travels in the header you wrote, not in Power BI’s credential store.

Why the URL is split up

The base URL stays a fixed string and the form path goes in RelativePath. That is deliberate. Microsoft’s data refresh documentation explains that most dynamic data sources cannot be refreshed in the Power BI service, with an exception for Web.Contents calls that build the variable parts through RelativePath and Query. If you concatenate the full URL into the first argument instead, the report works in Desktop and then refuses to refresh after you publish. If the service fails while testing the connection on the bare base URL, tick Skip test connection when you set the data source credentials for the published semantic model.

One row per answer

Each response carries an answers list. Every answer has the question’s field.ref, a type, and the value stored under a key named after that type, such as text, email, number, boolean, choice, or choices. Create a second query that references Responses and turns the list into rows:

let
    Source = Table.SelectColumns(Responses, {"response_id", "submitted_at", "answers"}),
    Rows = Table.SelectRows(Table.ExpandListColumn(Source, "answers"), each [answers] <> null),
    WithRef = Table.AddColumn(Rows, "question_ref", each [answers][field][ref], type text),
    WithType = Table.AddColumn(WithRef, "answer_type", each [answers][type], type text),
    WithValue = Table.AddColumn(WithType, "answer_value", each
        let
            a = [answers],
            v = Record.Field(a, a[type])
        in
            try
                if a[type] = "choice" then (if v[label]? <> null then v[label] else v[other]?)
                else if a[type] = "choices" then Text.Combine(v[labels]? ?? {}, ", ")
                else Text.From(v)
            otherwise null,
        type text),
    Result = Table.RemoveColumns(WithValue, {"answers"})
in
    Result

Now you have a long table with response_id, question_ref, and answer_value. Keep it long for counts and distributions, or pivot question_ref into columns when you need one row per respondent. Types that are records, such as payments, come out as null with this version, so add a branch for them if your form uses one.

Question titles are not in the responses. To label visuals, call GET /forms/{form_id} the same way and expand its fields list into a small lookup table of ref and title, then relate it to the answers table on question_ref. Fields inside question groups are nested one level deeper, so expand those as well. Setting readable refs on each question in the Typeform builder before launch makes this table much easier to work with than the default random strings.

Keeping it light

The query above pulls the full history on every refresh. That is fine for a few thousand responses. For larger forms, add a since value to Params from a Power Query parameter rather than from DateTime.LocalNow(), since computing the date at runtime is one of the patterns that can block refresh in the service. If you outgrow that, land the data in a warehouse first and point Power BI at the tables; the Typeform to Redshift guide shows that pipeline.

Third-party connectors

Services such as Coupler.io and Windsor.ai sell Typeform to Power BI connectors that handle authentication, paging, and scheduling for you. They are worth a look if you report on many forms or want marketing data in the same model. Check how each one flattens answers, whether it can reach EU data center accounts, and how pricing scales with rows and refresh frequency before you commit.

Responses that never reached Typeform

Some answers will never be in the API: a printed version of the survey filled in at a trade show, a PDF a partner sends back, a photo of a sign-up sheet. If those should count in the same dashboard, read them into the same shape.

With ocrskill.com you list the fields you want and get JSON back:

curl "https://api.ocrskill.com/ocr.json?fields=last_name,first_name,email,company_name?" \
  -H "Authorization: Bearer sk-your-key-here" \
  -F "file=@tradeshow-survey-scan.jpg"

Required fields return a 400 when the page cannot support them, so a blurry scan fails loudly instead of becoming a half-empty row. Optional keys end in ?. Save the results as JSON or CSV in a folder, load them with Power Query’s folder connector, add a source column set to “paper”, and append them to the Typeform table with matching column names. The form data extraction API post covers form OCR in more detail.

Which route to pick

Situation Route
One report, one time CSV or XLSX download
Live feed, no code, under the sheet limits Typeform to Google Sheets to Power BI
Scheduled refresh in the service, hidden fields, full history Responses API in Power Query
Many forms or many SaaS sources Third-party connector or a warehouse
Paper, PDF, or photo forms OCR to JSON, then append

Conclusion

Power BI and Typeform do not talk to each other directly, but the gap is small. A CSV gets you a chart today, Google Sheets gives you a live feed without code, and the Responses API gives you every field Typeform exposes, with refresh handled by the service. The details that decide whether the report keeps working are the unglamorous ones: query the right regional domain, keep the base URL static so scheduled refresh is allowed, use a read-only token because it lives in the file, and join answers on question refs rather than column positions so editing the form does not break your visuals.