# How to download invoices from Stripe

- Tool: Stripe
- Difficulty: Beginner
- Time required: 15 minutes
- Compatibility: Stripe API v2024-12+, Node.js 18+
- Last updated: March 2026

## TL;DR

Download Stripe invoices from the Dashboard by going to Billing → Invoices, clicking an invoice, and clicking the PDF download button. For bulk or automated downloads, use the Stripe API to retrieve the invoice_pdf URL on each invoice object. You can also build a script to download all invoices for a customer or date range programmatically using Node.js.

## How to Access and Download Stripe Invoices

Stripe automatically generates invoices for subscription payments and one-time invoice charges. Each invoice has a hosted URL (viewable in browser) and a PDF URL (downloadable). You can access these through the Dashboard for one-off downloads, or use the API to automate bulk downloads for accounting, tax filing, or customer self-service portals.

## Before you start

- A Stripe account with at least one finalized invoice
- Node.js 18+ for API-based downloads
- The Stripe npm package installed

## Step-by-step guide

### 1. Download from the Stripe Dashboard

Go to Stripe Dashboard → Billing → Invoices. Find the invoice you want and click on it. In the invoice detail view, click the download icon or 'Download PDF' button in the top-right area. You can also click 'View invoice' to see the hosted version in your browser.

**Expected result:** A PDF file is downloaded to your computer containing the full invoice details.

### 2. Get the invoice PDF URL via API

Each Stripe invoice object has an invoice_pdf field containing a direct URL to the PDF. Retrieve the invoice and access this URL to download programmatically.

```
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);

const invoice = await stripe.invoices.retrieve('in_abc123');

console.log('PDF URL:', invoice.invoice_pdf);
console.log('Hosted URL:', invoice.hosted_invoice_url);
// PDF URL is a direct download link — valid without authentication
```

**Expected result:** You have the direct PDF download URL and the hosted browser-viewable URL for the invoice.

### 3. List all invoices for a customer

Use the API to retrieve all invoices for a specific customer. Filter by status and date range to find exactly what you need.

```
const invoices = await stripe.invoices.list({
  customer: 'cus_abc123',
  status: 'paid',
  limit: 100,
});

invoices.data.forEach(inv => {
  console.log(`${inv.number} | ${inv.amount_paid / 100} ${inv.currency.toUpperCase()} | ${inv.invoice_pdf}`);
});
```

**Expected result:** A list of all paid invoices for the customer with their PDF download URLs.

### 4. Build a bulk download script

Download all invoices for a customer (or all customers) as PDF files to a local directory. This is useful for year-end accounting or tax preparation.

```
const fs = require('fs');
const https = require('https');
const path = require('path');

async function downloadInvoicePDF(url, filename) {
  return new Promise((resolve, reject) => {
    const file = fs.createWriteStream(filename);
    https.get(url, (response) => {
      response.pipe(file);
      file.on('finish', () => { file.close(); resolve(); });
    }).on('error', reject);
  });
}

async function downloadAllInvoices(customerId, outputDir) {
  fs.mkdirSync(outputDir, { recursive: true });

  let hasMore = true;
  let startingAfter = undefined;

  while (hasMore) {
    const params = { customer: customerId, status: 'paid', limit: 100 };
    if (startingAfter) params.starting_after = startingAfter;

    const invoices = await stripe.invoices.list(params);

    for (const inv of invoices.data) {
      if (inv.invoice_pdf) {
        const filename = path.join(outputDir, `${inv.number || inv.id}.pdf`);
        await downloadInvoicePDF(inv.invoice_pdf, filename);
        console.log(`Downloaded: ${filename}`);
      }
    }

    hasMore = invoices.has_more;
    if (invoices.data.length > 0) {
      startingAfter = invoices.data[invoices.data.length - 1].id;
    }
  }
}
```

**Expected result:** All paid invoices for the customer are downloaded as PDF files to the specified directory.

## Complete code example

File: `download-invoices.js`

```javascript
require('dotenv').config();
const fs = require('fs');
const https = require('https');
const path = require('path');
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);

function downloadFile(url, filepath) {
  return new Promise((resolve, reject) => {
    const file = fs.createWriteStream(filepath);
    https.get(url, (res) => {
      if (res.statusCode === 302 || res.statusCode === 301) {
        // Follow redirect
        https.get(res.headers.location, (redirectRes) => {
          redirectRes.pipe(file);
          file.on('finish', () => { file.close(resolve); });
        }).on('error', reject);
      } else {
        res.pipe(file);
        file.on('finish', () => { file.close(resolve); });
      }
    }).on('error', reject);
  });
}

async function downloadCustomerInvoices(customerId, outputDir = './invoices') {
  fs.mkdirSync(outputDir, { recursive: true });

  let downloaded = 0;
  let hasMore = true;
  let startingAfter;

  while (hasMore) {
    const params = { customer: customerId, status: 'paid', limit: 100 };
    if (startingAfter) params.starting_after = startingAfter;

    const invoices = await stripe.invoices.list(params);

    for (const inv of invoices.data) {
      if (!inv.invoice_pdf) continue;
      const name = `${inv.number || inv.id}.pdf`;
      const filepath = path.join(outputDir, name);
      await downloadFile(inv.invoice_pdf, filepath);
      downloaded++;
      console.log(`[${downloaded}] ${name}`);
    }

    hasMore = invoices.has_more;
    if (invoices.data.length) {
      startingAfter = invoices.data[invoices.data.length - 1].id;
    }
  }

  console.log(`\nDone. Downloaded ${downloaded} invoices to ${outputDir}`);
}

// Usage: pass customer ID as argument
const customerId = process.argv[2];
if (!customerId) {
  console.error('Usage: node download-invoices.js cus_xxx');
  process.exit(1);
}

downloadCustomerInvoices(customerId).catch(console.error);
```

## Common mistakes

- **Trying to download invoice PDFs for draft invoices** — undefined Fix: Only finalized invoices (status: 'paid', 'open', or 'void') have PDF URLs. Draft invoices don't have invoice_pdf set.
- **Storing invoice PDF URLs permanently** — undefined Fix: Invoice PDF URLs are temporary. Always retrieve a fresh URL from the API when you need to download. Don't cache URLs for long-term use.
- **Not handling pagination when listing invoices** — undefined Fix: The API returns a maximum of 100 invoices per request. Use has_more and starting_after to paginate through all results.
- **Not following redirects when downloading PDFs** — undefined Fix: Stripe PDF URLs may redirect. Your download function should follow 301/302 redirects to reach the actual PDF file.

## Best practices

- Use the Stripe Dashboard for quick one-off invoice downloads
- Use the API and invoice_pdf URL for automated or bulk downloads
- Always paginate when listing invoices — don't assume all results fit in one request
- Handle HTTP redirects in your download function
- Create a customer self-service portal using hosted_invoice_url for browser-viewable invoices
- Filter by status and date range to download only the invoices you need
- Store downloaded PDFs with the invoice number as filename for easy identification

## Frequently asked questions

### Where do I find invoices in the Stripe Dashboard?

Go to Billing → Invoices. You can filter by status (paid, open, draft, void) and search by customer name or invoice number.

### Can customers download their own invoices?

Yes. Share the hosted_invoice_url with customers — it opens the invoice in a browser where they can view and download it. You can also build a customer portal using these URLs.

### Are invoice PDF URLs permanent?

No. Invoice PDF URLs are temporary. Always retrieve a fresh URL from the API when you need to download. Don't store or cache URLs for long-term use.

### Can I download invoices for all customers at once?

Yes. Use stripe.invoices.list() without a customer filter and paginate through all results. This works for year-end bulk downloads or accounting exports.

### Do draft invoices have PDF URLs?

No. Only finalized invoices have invoice_pdf URLs. You must finalize a draft invoice (stripe.invoices.finalizeInvoice) before a PDF is generated.

---

Source: https://www.rapidevelopers.com/stripe-guide/how-to-download-invoices-from-stripe
© RapidDev — https://www.rapidevelopers.com/stripe-guide/how-to-download-invoices-from-stripe
