Bulk Export Qualtrics Individual Response Reports to PDF

The Challenge

Qualtrics lets you export any individual response as a formatted PDF, but only one at a time. There is no native "export all as PDF" button. Every export requires navigating through the same four clicks: open the row's action menu, select Export to PDF, confirm the modal, then close the Manage Downloads dialog.

For surveys with dozens or hundreds of responses, this becomes a serious time problem:

This guide gives you a free three-stage browser script that handles the entire process automatically: queueing every export, downloading all the files, and cleaning up the jobs queue, without leaving the page.

The Solution

Three lightweight JavaScript scripts that run directly in your browser console. No installation, no API key, no external service. Your data never leaves your machine.

⚠️
This is a three-stage process. Run each script in order.

Because Qualtrics processes PDF exports asynchronously on its servers, queueing and downloading must happen in separate steps. Running Stage 2 immediately after Stage 1 without waiting may result in fewer files if Qualtrics has not yet finished generating all PDFs.

Stage What it does When to run
Stage 1: Queue Exports Loops every response row and triggers Export to PDF for each Immediately, on the Data & Analytics page
Stage 2: Download Files Opens Manage Downloads and clicks every available download button After a short wait for Qualtrics to process the exports
Stage 3: Clean Up (optional) Deletes all completed jobs from the Manage Downloads queue After confirming all PDFs are in your Downloads folder

How to Use

Watch the Demo

Stage 1: Queue PDF Exports

▶ Stage 1: Queue Exports
Stage 2: Download Files
Stage 3: Clean Up
1

Navigate to the Data & Analytics Page

Open your Qualtrics survey and click the Data & Analytics tab. Make sure the response table is fully loaded and all rows are visible before running the script.

The script targets the response rows on the current page. If your survey has more responses than fit on one page, see the Pagination note below.

2

Open the Browser Console

Right-click anywhere on the response table and select Inspect or Inspect Element from the context menu.

In the developer tools panel that opens, click the Console tab.

Why right-click on the table? This sets the correct page context in the inspector before the script runs, making DOM access more reliable.
3

Copy and Run the Stage 1 Script

Click the Copy Script button below, paste it into the console, and press Enter. The script runs automatically and you do not need to call any additional functions.

/* STAGE 1 SCRIPT PLACEHOLDER */
/**
 * Qualtrics Bulk Response PDF Exporter — STAGE 1: Queue Exports
 * Loops through every response row and queues a PDF export for each.
 * After this completes, run Stage 2 to download the queued files.
 *
 * Developed by Pirai AI — piraiai.com
 */

(async () => {

    // ============================================================================
    // HELPERS
    // ============================================================================

    function waitForElement(selector, timeoutMs = 10000) {
        return new Promise((resolve, reject) => {
            const el = document.querySelector(selector);
            if (el) { resolve(el); return; }

            const observer = new MutationObserver(() => {
                const found = document.querySelector(selector);
                if (found) {
                    observer.disconnect();
                    resolve(found);
                }
            });
            observer.observe(document.body, { childList: true, subtree: true });

            setTimeout(() => {
                observer.disconnect();
                reject(new Error(`Timed out waiting for: ${selector}`));
            }, timeoutMs);
        });
    }

    function findByText(text) {
        return Array.from(document.querySelectorAll('*')).find(el =>
            el.textContent.trim() === text && el.offsetParent !== null
        ) || null;
    }

    const sleep = ms => new Promise(r => setTimeout(r, ms));

    function getActionButtons() {
        return Array.from(
            document.querySelectorAll('td.actions-column button[aria-label="record actions"]')
        );
    }

    // ============================================================================
    // PREFLIGHT CHECK
    // ============================================================================

    const initialButtons = getActionButtons();
    if (initialButtons.length === 0) {
        console.error('❌ No response rows found.');
        console.error('   Make sure you are on the Data & Analytics page with the response table visible.');
        return;
    }

    const totalRows = initialButtons.length;
    console.log(`📊 Found ${totalRows} response rows. Queueing PDF exports...\n`);

    // ============================================================================
    // MAIN LOOP — Queue a PDF export for every row
    // ============================================================================

    let successCount = 0;
    let failCount = 0;

    for (let i = 0; i < totalRows; i++) {
        console.log(`--- Queueing Row ${i + 1} of ${totalRows} ---`);

        // Re-query each iteration — DOM re-renders after each export action.
        const buttons = getActionButtons();
        if (i >= buttons.length) {
            console.warn(`⚠️  Row ${i + 1}: button no longer in DOM, skipping.`);
            failCount++;
            continue;
        }

        const button = buttons[i];

        try {
            button.scrollIntoView({ behavior: 'smooth', block: 'center' });
            await sleep(300);
            button.click();
            console.log(`   🔘 Opened action menu`);
            await sleep(1000);

            // Find "Export to PDF" in the dropdown
            const allOptions = Array.from(
                document.querySelectorAll('[role="option"], [role="menuitem"]')
            ).filter(el => el.textContent.includes('PDF') && el.offsetParent !== null);

            const target = findByText('Export to PDF') || allOptions[0] || null;

            if (!target) {
                console.warn(`   ⚠️  "Export to PDF" option not found for row ${i + 1}`);
                document.body.click();
                await sleep(500);
                failCount++;
                continue;
            }

            target.click();
            console.log(`   ✅ Clicked "Export to PDF"`);

            // Wait for the export confirmation modal
            let exportBtn;
            try {
                exportBtn = await waitForElement('[data-testid="pdf-export-export-btn"]', 5000);
            } catch {
                console.warn(`   ⚠️  Export modal did not appear for row ${i + 1}`);
                const closeEl = document.querySelector('[data-testid="pdf-export-close-btn"], [aria-label="Close"]');
                if (closeEl) closeEl.click();
                failCount++;
                continue;
            }

            exportBtn.click();
            console.log(`   🚀 Export queued`);
            await sleep(3000);

            // Close the "Manage Downloads" dialog that appears after queuing
            try {
                const closeBtn = await waitForElement('[data-testid="pdf-export-close-btn"]', 5000);
                closeBtn.click();
                console.log(`   ✔️  Dismissed dialog`);
            } catch {
                // Dialog may not appear for every row
            }

            successCount++;
            await sleep(2000);

        } catch (err) {
            console.error(`   ❌ Error on row ${i + 1}: ${err.message}`);
            failCount++;
            try {
                const closeEl = document.querySelector('[data-testid="pdf-export-close-btn"], [aria-label="Close"]');
                if (closeEl) closeEl.click();
                else document.body.click();
            } catch { /* ignore */ }
            await sleep(1000);
        }
    }

    // ============================================================================
    // SUMMARY — prompt user to run Stage 2
    // ============================================================================

    console.log(`
╔════════════════════════════════════════════════════════════╗
║  STAGE 1 COMPLETE — PDF Exports Queued                     ║
╠════════════════════════════════════════════════════════════╣
║  Total rows:    ${String(totalRows).padEnd(42)}║
║  Queued:        ${String(successCount).padEnd(42)}║
║  Skipped/Error: ${String(failCount).padEnd(42)}║
╠════════════════════════════════════════════════════════════╣
║                                                            ║
║  ▶  NEXT STEP: Run the Stage 2 script to download all     ║
║     queued PDF files from the Manage Downloads modal.     ║
║                                                            ║
║  Paste the Stage 2 script into this console and press     ║
║  Enter. Stay on this Data & Analytics page.               ║
║                                                            ║
╚════════════════════════════════════════════════════════════╝
`);

})();
4

Wait for the Stage 1 Summary

The script loops through every row on the page. Watch the console for per-row progress logs. When it finishes you will see a summary box:

╔══════════════════════════════════════════════════════════╗
║ STAGE 1 COMPLETE — PDF Exports Queued ║
╠══════════════════════════════════════════════════════════╣
║ Total rows: 50 ║
║ Queued: 50 ║
║ Skipped/Error: 0 ║
╚══════════════════════════════════════════════════════════╝
Do not navigate away from the page. Stay on the Data & Analytics tab while Stage 1 runs and while you wait for Stage 2.
5

Wait for Qualtrics to Process the Exports

After Stage 1 completes, Qualtrics begins generating the PDF files on its servers. For simple surveys this typically takes a few seconds per response. For surveys with many questions or complex display logic, allow a minute or two before running Stage 2.

How to check if exports are ready: Open Export & Import → Manage Previous Downloads. When you see Download buttons next to each Response Report, they are ready.

Stage 2: Download the Files

✓ Stage 1: Queue Exports Done
▶ Stage 2: Download Files
Stage 3: Clean Up
6

Copy and Run the Stage 2 Script

Paste the Stage 2 script into the same console and press Enter. The script opens the Manage Downloads modal automatically and clicks every available download button.

/* STAGE 2 SCRIPT PLACEHOLDER */
/**
 * Qualtrics Bulk Response PDF Exporter — STAGE 2: Download Queued Files
 * Opens the Manage Previous Downloads modal and downloads every queued PDF.
 * Run this after Stage 1 has finished queueing all exports.
 *
 * Stay on the Data & Analytics page before running this script.
 *
 * Developed by Pirai AI — piraiai.com
 */

(async () => {

    // ============================================================================
    // HELPERS
    // ============================================================================

    function waitForElement(selector, timeoutMs = 10000) {
        return new Promise((resolve, reject) => {
            const el = document.querySelector(selector);
            if (el) { resolve(el); return; }

            const observer = new MutationObserver(() => {
                const found = document.querySelector(selector);
                if (found) {
                    observer.disconnect();
                    resolve(found);
                }
            });
            observer.observe(document.body, { childList: true, subtree: true });

            setTimeout(() => {
                observer.disconnect();
                reject(new Error(`Timed out waiting for: ${selector}`));
            }, timeoutMs);
        });
    }

    const sleep = ms => new Promise(r => setTimeout(r, ms));

    // ============================================================================
    // STEP 1: Open the Export & Import menu
    // ============================================================================

    console.log('📋 STAGE 2: Downloading queued PDF files...\n');

    const exportMenuBtn = document.querySelector('[data-testid="export-and-import-menu"]');
    if (!exportMenuBtn) {
        console.error('❌ Could not find the Export & Import menu button.');
        console.error('   Make sure you are on the Data & Analytics page.');
        return;
    }

    exportMenuBtn.click();
    console.log('✅ Opened Export & Import menu');
    await sleep(1000);

    // ============================================================================
    // STEP 2: Click "Manage Previous Downloads..."
    // ============================================================================

    const manageOption = Array.from(document.querySelectorAll('*')).find(el =>
        el.textContent.trim() === 'Manage Previous Downloads...' && el.offsetParent !== null
    );

    if (!manageOption) {
        console.error('❌ Could not find "Manage Previous Downloads..." option.');
        document.body.click(); // close menu
        return;
    }

    manageOption.click();
    console.log('✅ Clicked "Manage Previous Downloads..."');

    // ============================================================================
    // STEP 3: Wait for the Manage Downloads modal
    // ============================================================================

    try {
        await waitForElement('#pdf-export-modal', 15000);
        console.log('📊 Manage Downloads modal opened\n');
    } catch {
        console.error('❌ Manage Downloads modal did not appear within 15 seconds.');
        console.error('   The exports from Stage 1 may still be processing. Wait a moment and try again.');
        return;
    }

    await sleep(1000); // let the modal fully render

    // ============================================================================
    // STEP 4: Find all download buttons in the modal
    // ============================================================================

    const downloadButtons = Array.from(
        document.querySelectorAll('[data-testid^="pdf-export-download-button-"]')
    );

    if (downloadButtons.length === 0) {
        console.warn('⚠️  No download buttons found in the modal.');
        console.warn('   The exports may still be processing. Wait a few moments and run Stage 2 again.');
        return;
    }

    console.log(`📁 Found ${downloadButtons.length} files ready to download\n`);

    // ============================================================================
    // STEP 5: Click each download button
    // ============================================================================

    let successCount = 0;
    let failCount = 0;

    for (let i = 0; i < downloadButtons.length; i++) {
        const button = downloadButtons[i];
        console.log(`--- Downloading file ${i + 1} of ${downloadButtons.length} ---`);

        try {
            button.scrollIntoView({ behavior: 'smooth', block: 'center' });
            await sleep(300);
            button.click();
            console.log(`   ⬇️  Download triggered`);
            successCount++;
            await sleep(1000);
        } catch (err) {
            console.error(`   ❌ Error downloading file ${i + 1}: ${err.message}`);
            failCount++;
        }
    }

    // ============================================================================
    // STEP 6: Close the modal
    // ============================================================================

    await sleep(500);
    try {
        const closeBtn = document.querySelector('[data-testid="pdf-export-close-btn"]');
        if (closeBtn) {
            closeBtn.click();
            console.log('\n✅ Closed Manage Downloads modal');
        }
    } catch { /* ignore */ }

    // ============================================================================
    // SUMMARY
    // ============================================================================

    console.log(`
╔════════════════════════════════════════════════════════════╗
║  STAGE 2 COMPLETE — Downloads Triggered                    ║
╠════════════════════════════════════════════════════════════╣
║  Files found:   ${String(downloadButtons.length).padEnd(42)}║
║  Triggered:     ${String(successCount).padEnd(42)}║
║  Errors:        ${String(failCount).padEnd(42)}║
╠════════════════════════════════════════════════════════════╣
║                                                            ║
║  📁 Check your Downloads folder for the PDF files.        ║
║                                                            ║
║  If some exports were still processing when Stage 2 ran,  ║
║  wait a moment and run Stage 2 again to pick them up.     ║
║                                                            ║
╚════════════════════════════════════════════════════════════╝
`);

})();
7

Confirm Downloads

When Stage 2 finishes you will see a summary in the console. Open your browser's Downloads folder and confirm the PDF files are there.

Files are named by Qualtrics using the response ID, for example: Response_Report_R_d4HCfnbqgWogD54.pdf. Each filename maps directly back to a row in the Data & Analytics table.

Some exports not ready yet? If the Manage Downloads modal shows fewer files than expected, some exports may still be processing. Wait a moment and run Stage 2 again and it will pick up any newly completed files.

Stage 3: Clean Up Jobs (Optional)

✓ Stage 1: Queue Exports Done
✓ Stage 2: Download Files Done
▶ Stage 3: Clean Up
8

Run the Stage 3 Script

Stage 3 is optional but recommended. Qualtrics keeps completed export jobs in the Manage Downloads queue indefinitely. Running Stage 3 deletes all jobs, keeping the queue clean for next time.

Only run Stage 3 after you have confirmed all PDFs are in your Downloads folder. Deleted jobs cannot be recovered, but the underlying response data in Qualtrics is not affected.

If the Manage Downloads modal is still open from Stage 2, Stage 3 will use it directly. If you closed it, the script reopens it automatically.

/* STAGE 3 SCRIPT PLACEHOLDER */
/**
 * Qualtrics Bulk Response PDF Exporter — STAGE 3: Delete Queued Jobs
 * Deletes all Response Report jobs from the Manage Downloads modal.
 * Run this after Stage 2 has finished downloading all PDFs.
 *
 * Can be run with the modal already open (faster), or from scratch —
 * the script will open the modal automatically if it is not visible.
 *
 * Developed by Pirai AI — piraiai.com
 */

(async () => {

    // ============================================================================
    // HELPERS
    // ============================================================================

    function waitForElement(selector, timeoutMs = 10000) {
        return new Promise((resolve, reject) => {
            const el = document.querySelector(selector);
            if (el) { resolve(el); return; }

            const observer = new MutationObserver(() => {
                const found = document.querySelector(selector);
                if (found) {
                    observer.disconnect();
                    resolve(found);
                }
            });
            observer.observe(document.body, { childList: true, subtree: true });

            setTimeout(() => {
                observer.disconnect();
                reject(new Error(`Timed out waiting for: ${selector}`));
            }, timeoutMs);
        });
    }

    const sleep = ms => new Promise(r => setTimeout(r, ms));

    // ============================================================================
    // STEP 1: Ensure the Manage Downloads modal is open
    // ============================================================================

    console.log('🗑️  STAGE 3: Deleting all downloaded jobs...\n');

    const modalAlreadyOpen = !!document.querySelector('#pdf-export-modal');

    if (!modalAlreadyOpen) {
        console.log('   Modal not open — opening via Export & Import menu...');

        const exportMenuBtn = document.querySelector('[data-testid="export-and-import-menu"]');
        if (!exportMenuBtn) {
            console.error('❌ Could not find the Export & Import menu button.');
            console.error('   Make sure you are on the Data & Analytics page.');
            return;
        }

        exportMenuBtn.click();
        await sleep(1000);

        const manageOption = Array.from(document.querySelectorAll('*')).find(el =>
            el.textContent.trim() === 'Manage Previous Downloads...' && el.offsetParent !== null
        );

        if (!manageOption) {
            console.error('❌ Could not find "Manage Previous Downloads..." option.');
            document.body.click();
            return;
        }

        manageOption.click();

        try {
            await waitForElement('#pdf-export-modal', 15000);
            console.log('   ✅ Manage Downloads modal opened\n');
        } catch {
            console.error('❌ Manage Downloads modal did not appear.');
            return;
        }

        await sleep(800);

    } else {
        console.log('   ✅ Modal already open\n');
    }

    // ============================================================================
    // STEP 2: Count rows before starting
    // ============================================================================

    // Fresh count from live DOM — always re-query, never cache across deletions.
    function getDeleteButtons() {
        return Array.from(
            document.querySelectorAll('#pdf-export-modal tr.pdf-export-row button[aria-label="Delete Job"]')
        ).filter(btn => btn.offsetParent !== null);
    }

    const initialCount = getDeleteButtons().length;

    if (initialCount === 0) {
        console.log('ℹ️  No jobs found in the Manage Downloads modal. Nothing to delete.');
        return;
    }

    console.log(`📋 Found ${initialCount} job(s) to delete.\n`);

    // ============================================================================
    // STEP 3: Delete each job one at a time
    //
    // After each deletion the row is removed from the DOM, so the index always
    // stays at 0 — we always delete the first remaining button.
    // ============================================================================

    let deletedCount = 0;
    let failCount = 0;

    for (let i = 0; i < initialCount; i++) {
        // Always grab the first visible delete button — list shrinks after each click.
        const deleteButtons = getDeleteButtons();

        if (deleteButtons.length === 0) {
            console.log(`   ℹ️  No more delete buttons found after ${deletedCount} deletions.`);
            break;
        }

        const btn = deleteButtons[0];

        // Extract the Response ID from the sibling type cell for logging.
        const row = btn.closest('tr.pdf-export-row');
        const typeCell = row ? row.querySelector('td.pdf-export-job-type') : null;
        const jobLabel = typeCell ? typeCell.textContent.trim() : `Job ${i + 1}`;

        console.log(`--- Deleting ${i + 1} of ${initialCount}: ${jobLabel} ---`);

        try {
            btn.scrollIntoView({ behavior: 'smooth', block: 'center' });
            await sleep(200);
            btn.click();
            console.log(`   🗑️  Deleted`);
            deletedCount++;
            // Wait for the row to be removed from the DOM before proceeding.
            await sleep(600);
        } catch (err) {
            console.error(`   ❌ Error deleting job ${i + 1}: ${err.message}`);
            failCount++;
            await sleep(400);
        }
    }

    // ============================================================================
    // STEP 4: Verify and close
    // ============================================================================

    await sleep(500);

    const remaining = getDeleteButtons().length;

    if (remaining > 0) {
        console.warn(`\n⚠️  ${remaining} job(s) still remain in the modal. You may need to run Stage 3 again.`);
    }

    // Close the modal
    try {
        const closeBtn = document.querySelector('[data-testid="pdf-export-close-btn"]');
        if (closeBtn) {
            closeBtn.click();
            console.log('\n✅ Closed Manage Downloads modal');
        }
    } catch { /* ignore */ }

    // ============================================================================
    // SUMMARY
    // ============================================================================

    console.log(`
╔════════════════════════════════════════════════════════════╗
║  STAGE 3 COMPLETE — Jobs Deleted                           ║
╠════════════════════════════════════════════════════════════╣
║  Jobs found:    ${String(initialCount).padEnd(42)}║
║  Deleted:       ${String(deletedCount).padEnd(42)}║
║  Errors:        ${String(failCount).padEnd(42)}║
║  Remaining:     ${String(remaining).padEnd(42)}║
╚════════════════════════════════════════════════════════════╝
`);

})();
9

Confirm Deletion

The console will log each deleted job by its Response Report ID, then display a summary. The Manage Downloads modal will be empty and automatically closed.

Done. Your response PDFs are in your Downloads folder and the Manage Downloads queue is clear.

Understanding Your Output

Each downloaded file is a self-contained PDF of a single survey response, formatted exactly as you would see if you opened the response individually in Qualtrics.

Column Description
Filename Named by Qualtrics as Response_Report_R_XXXXXXXXXXXX.pdf where the suffix is the unique response ID
Contents All questions and answers for that response, formatted exactly as Qualtrics renders them in the individual response view
Traceability The response ID in the filename matches the Response ID column in the Data & Analytics table, making it easy to cross-reference

Troubleshooting

Stage 1: No response rows found

  • Confirm you are on the Data & Analytics tab with the response table fully loaded
  • Scroll through the table to ensure all rows have rendered before running the script
  • Refresh the page and try again

Stage 1: "Export to PDF" option not found for some rows

  • This can occur if Qualtrics's action menu closes before the script can click it. The row will be logged as skipped.
  • Rerun Stage 1; the script re-queries the DOM fresh each iteration and will attempt the skipped rows again
  • Avoid interacting with the browser while Stage 1 is running

Stage 2: No download buttons found in the modal

  • Qualtrics may still be processing the exports. Wait 30 to 60 seconds and run Stage 2 again.
  • For large or complex surveys, processing can take several minutes. Check the Manage Downloads modal manually to see if the Download buttons have appeared yet.

Stage 2: Downloads not saving to my folder

  • Check that your browser is not blocking downloads from qualtrics.com. Look for a blocked download notification in the browser's address bar.
  • Temporarily disable any pop-up or download blockers and run Stage 2 again
  • In Chrome, go to Settings → Privacy and Security → Site Settings → Additional permissions → Automatic downloads and allow qualtrics.com

Stage 3: Jobs not deleting or modal not opening

  • Make sure you are still on the Data & Analytics page — navigating away breaks the script's ability to find the Export & Import menu
  • If some jobs remain after Stage 3, run the script again — it always deletes from the top of the list and will pick up anything that was missed

Advanced Usage

Running Stage 2 multiple times for large batches +

If you queued exports for a large number of responses, Qualtrics may finish generating them in batches rather than all at once. Stage 2 only downloads files that have a Download button at the moment it runs. Files still processing are skipped.

You can safely run Stage 2 multiple times. Each run opens the Manage Downloads modal and downloads whatever is available. Run it once after a short wait, then again a minute later to catch any stragglers, until the summary shows no new files.

Handling multiple pages of responses +

By default, Qualtrics shows 50 responses per page. To bulk export more than 50 responses:

  • Run Stage 1 on page 1, wait for the summary, then navigate to page 2 and run Stage 1 again
  • Repeat for each page. Qualtrics queues all jobs centrally in Manage Downloads regardless of which page they came from.
  • Once all pages have been queued, run Stage 2 once (or multiple times) to download everything
  • Run Stage 3 once at the end to clean up all jobs

You can also increase the page size to 100 responses via the dropdown at the top of the Data & Analytics table, which reduces the number of Stage 1 runs needed.

Organising downloaded files by response ID +

Qualtrics names files using the response ID (e.g. Response_Report_R_d4HCfnbqgWogD54.pdf). To link each PDF back to respondent data:

  • Export your Data & Analytics data as CSV — it includes a Response ID column
  • Use the response ID to join the PDF filename to the corresponding row of survey data in Excel or any data tool
  • This is useful for attaching individual PDFs to CRM records, case management systems, or audit packages
Best practices before running on a large dataset +

A few habits worth following when exporting a large number of responses:

  • Do a test run on a single page with a small number of rows before committing to a full export
  • Check the Manage Downloads modal manually after Stage 1 to confirm jobs are appearing before running Stage 2
  • Avoid navigating away from the Data & Analytics page during any stage, as the scripts rely on remaining in this context
  • Run Stage 3 only after you have verified all PDFs are saved. Deleted jobs cannot be recovered, though the underlying response data is unaffected.
  • Use the browser's Downloads panel (Ctrl+J in Chrome) to monitor files as they arrive during Stage 2

Need to convert surveys into Qualtrics?

This tool automates your Qualtrics response PDF exports. If you also need to convert Word, PDF or Excel questionnaires into QSF files for Qualtrics import, Pirai AI handles that automatically across 20+ survey platforms with no manual scripting required.