Every month, the same chore lands on someone’s desk: a folder of invoice PDFs — some from an e-invoice/IRP portal, some from a marketplace’s monthly statement, some a small vendor’s own letterhead — that all need to become one clean row each in a GST working file before they can go anywhere near a GSTR-1 or a purchase register. Copying GSTIN, trade name, invoice number, date, taxable value, and three tax columns out of a PDF by hand is slow and it’s exactly the kind of repetitive, detail-heavy work a typo slips into unnoticed.
PDF Invoices to GST Data does that extraction and the accounting sense-checking around it — which invoice is yours to report as a sale versus a purchase, whether reverse charge applies, whether a document number is missing from the sequence — and hands back a working file laid out the way a GST return period actually is, with a review table in between where every field can be corrected by hand before it’s exported.
The problem with “just copy it into Excel”
A real batch of invoice PDFs isn’t uniform. In practice you get:
- Standard e-invoice/IRP-format bills — the government schema, with a QR code and an IRN, where the HSN code sits in a per-line-item table far from any single “HSN” label you could search for.
- Marketplace tax invoices — Amazon, Flipkart, and similar sellers each lay out GSTIN, buyer, and seller blocks slightly differently.
- A small vendor’s own letterhead — hand-formatted, no fixed template, sometimes missing a field entirely.
- A single PDF bundling several invoices — a marketplace’s monthly statement, or a batch someone scanned at a CA’s office, where each invoice is its own page or page range inside one file.
A rigid, position-based template that expects “GSTIN always at (120, 480)” breaks on the second invoice template it meets. So extraction here is label-proximity-based instead — “find where ‘Invoice No’ or ‘Invoice Number’ appears, read what follows it” — the same approach across all three shapes above, rather than one universal layout assumption. It’s a strong first draft, not a claim of perfect accuracy: every field lands in an editable review table, and a row with something the tool genuinely couldn’t read is flagged rather than silently left wrong.
How a batch turns into a working file
1. Upload one or more PDFs. One invoice per file is the simplest case, but a single upload commonly bundles several invoices or credit notes together — a marketplace’s monthly statement, a multi-invoice batch. The tool looks for where a new invoice/document number starts in the page-by-page text and splits the file into separate rows at each boundary, while a genuinely multi-page single invoice (no new number on page 2) correctly stays as one row.
2. Each page is read positionally, not by raw draw order. A PDF’s internal content stream frequently doesn’t emit text in the order it appears on the page — a real invoice generator commonly draws an item table, then a two-column seller/buyer block, then an IRN box, in whatever order was convenient to generate, not top-to-bottom reading order. Each page’s text is reconstructed sorted top-to-bottom, left-to-right, with real line breaks, before any field extraction runs against it.
3. Review and correct. GSTIN, trade name, invoice number, date, HSN/SAC, taxable value, the three tax heads, and reverse charge are all populated automatically. Every field is a normal editable cell — invoice layouts vary enough, especially for smaller vendors, that hand-correction is a routine, expected step, not an edge case.
4. Download the GST working file. One sheet per invoice month, named the way a GST return period usually is (“GSTR April 2026”), invoices and credit notes in clearly separated sections, each with its own total row so a credit note never quietly nets into the regular invoice total by accident.
Sales versus purchase: why the same GSTIN field can mean two different things
This is the part that’s easy to get backwards, and it’s a real bug this tool went through a round of fixing: a GST working file doesn’t always report the document’s own printed supplier. If you’re filing your GSTR-1 for your own sales, what you report is your recipient — your own GSTIN is implicit, since every row is one of your outward supplies, so listing it again on every line would be redundant. If you’re building a purchase register for input tax credit, what you report is the document’s supplier — the party you paid.
The tool resolves this by detecting your own company’s GSTIN across the batch (the most common GSTIN across every uploaded document, shown above the review table, with a one-click way to fix it if it guessed wrong) and comparing it against each document’s supplier and counterparty GSTIN. If your GSTIN is the supplier on a document, that document is a sale, and the row reports the counterparty (your customer). If your GSTIN is the counterparty, it’s a purchase, and the row reports the supplier (your vendor). A batch that’s ambiguous — your company’s GSTIN doesn’t clearly match either side — gets flagged for a manual check rather than guessed.
This matters concretely: for a tax invoice where your company issued the bill to “Example Buyer Pvt Ltd,” the working file’s “Trade/Legal name” and “GSTIN of supplier” columns should show Example Buyer Pvt Ltd and its GSTIN — not your own company’s letterhead details, even though your own name and GSTIN are what’s printed largest on the page. Getting this backwards is the single most confusing way a GST working file can go wrong, because every other field on the row still looks correct.
Sales, Purchase, and Credit Note — kept apart, not just labeled
Sales and purchase invoices are never candidates for the same GST return anyway — a GSTR-1 reports outward supplies, a purchase register is entirely separate input-tax-credit data — so mixing them into one table would misrepresent the data even with a “Type” column to tell them apart by eye. When a single upload contains both directions, the export automatically splits into top-level Sales Invoices, Purchase Invoices, and Credit Note Invoices tabs, each still broken into its own “GSTR <Month> <Year>” sections internally. A batch that’s cleanly one direction (the common case, since sales and purchase runs are usually uploaded separately) skips the extra tabs and instead labels the main table itself — bold, capitalized, centered, merged across every column, the same treatment every section heading gets — so a reader always knows which direction they’re looking at without inspecting the data row by row.
How reverse charge gets determined
Reverse charge status is read primarily from the numbers, not the label text: if real CGST/SGST/IGST was actually collected on the invoice, it’s never reverse charge, regardless of what any printed note says. When nothing was collected on a genuine taxable supply, an explicit declaration on the document itself (something like “With Reverse Charge”) is trusted — this specifically covers the real-world case where the printed line-item rate is itself shown as 0%, which would otherwise be indistinguishable from an exempt or nil-rated supply using the rate alone. Without an explicit declaration, a GST rate printed anywhere on the document with ₹0 actually charged is still taken as reverse-charge Yes; no rate information at all defaults to No.
HSN/SAC: two different extraction problems, one column
HSN/SAC turns out to need two different strategies depending on the template:
- On most invoices, a simple label search (“HSN” or “SAC,” read what follows) finds it directly.
- On the government e-invoice/IRP template, HSN codes sit in a per-line-item table where the column header is far from any individual row’s actual code — a proximity search alone misreads the table. For this shape specifically, the tool walks each line item positionally and validates what it finds against the real GST Unit Quantity Code list (NOS, KGS, PCS, BOX, SET, and so on) before accepting a match, which is what stops a row’s own trailing total plus the next row’s serial number from occasionally getting misread as a fake HSN-quantity-unit triple.
Every unique HSN/SAC code found across a document’s line items is comma-joined into the one HSN/SAC column.
The invoice date: Ack Date first, then a fallback chain
GST paperwork carries more than one date-looking field — an Ack Date (from e-invoice acknowledgment), a Tax Invoice Date, a plain Invoice Date, a Bill Date, a Document Date — and they don’t always agree on the same document. The extractor checks them in a fixed priority order, Ack Date first, falling through Tax Invoice Date, Invoice Date, Bill Date, “Date of Invoice,” then Document Date, with a bare “Date” label as a last resort. The exported Invoice Date column displays as DD-MM-YYYY, the convention GST paperwork itself uses, even though every date is kept internally as ISO (for correct sorting and month-grouping) until the final export step reformats just the display cell.
Catching a missing invoice number
Rule 46(b) of the CGST Rules requires a consecutive serial number per invoice series, and it explicitly allows multiple series — one per branch or location is completely normal. So the check groups invoice numbers by their non-numeric prefix (the “series”) and only looks for gaps within a series, rather than assuming an entire upload is one flat sequence. Where it finds a gap, it inserts a bold, centered Missing/Cancelled row directly into the exported sheet at the right spot in the sequence, with every other field left blank — a genuinely useful flag, since a gap almost always means a cancelled invoice that was never issued, or one the user simply didn’t upload, either way worth a closer look.
One thing worth knowing about this check: it only flags a gap up to a reasonable size (500 numbers). Two invoice numbers that happen to share a prefix but are hundreds of thousands apart — a different branch’s numbering, a different document type, or occasionally a misread digit — aren’t a real gap in one series, and trying to enumerate every number in between would be neither useful nor safe to render. Past that size, the pair is simply left unflagged rather than guessed at.
Credit notes: negative, and clearly separated
A credit note reduces what was originally invoiced, so its Taxable Value, tax columns, and Total Value are all exported as negative numbers — the sign convention that lets a credit note net correctly against the original invoice when a column is totaled, and the sign a GST working file is expected to use. A debit note, the opposite kind of adjustment, keeps its amounts positive, same as a regular invoice. Credit and debit notes always get their own labeled section with their own total row — never folded into the regular invoice total for that month, so the two never accidentally net against each other silently.
When a file can’t be read
Two different kinds of failure are handled differently, deliberately:
- A scanned or printed copy (a page with no real text layer at all) is rejected immediately, with a clear message pointing at the OCR PDF tool to run first. This tool doesn’t attempt OCR on a scanned invoice itself — browser-side text recognition is a genuinely slow process, and making a whole batch upload wait through it, file after file, is a worse experience than telling the user plainly and instantly that this particular file needs a different first step. The rest of the batch keeps processing normally; only the affected file is skipped.
- A digital PDF that’s missing a specific field (no HSN found, no readable trade name, an unclear direction) still produces a row with everything it could read, flagged with a warning rather than discarded.
Either way, a red Error in Files banner appears above the review table listing exactly which files had a problem and why, and the exported working file carries the same information: an Error in Files tab with the file name and the same warning message in place of a blank Trade/Legal name column, so the file is self-documenting even once it’s left the browser and is sitting in someone else’s inbox.
A worked example
Say a batch mixes two sales invoices and a purchase invoice from the same company:
| File | Direction | GSTIN reported | Trade/Legal name | Invoice No. | Taxable Value | Total Value |
|---|---|---|---|---|---|---|
| Sale to Example Buyers Pvt Ltd | Sales | 29AAAAA0000A1Z5 | Example Buyers Pvt Ltd | INV-2026-0041 | 2,50,000 | 2,95,000 |
| Sale to Example Buyers Pvt Ltd | Sales | 29AAAAA0000A1Z5 | Example Buyers Pvt Ltd | INV-2026-0043 | 1,10,000 | 1,29,800 |
| Purchase from Example Suppliers | Purchase | 07BBBBB1111B2Z6 | Example Suppliers LLP | SUP-778 | 60,000 | 70,800 |
Run through the tool, this becomes a workbook with a Sales Invoices tab and a Purchase Invoices tab (since both directions are present). Inside the Sales Invoices tab, a “GSTR <Month> <Year>” section holds both sale rows — with INV-2026-0042 flagged as a Missing/Cancelled row between them, since the series jumped from 0041 to 0043 — followed by a Total row summing only the two real invoices. The Purchase Invoices tab holds the one purchase row in its own monthly section. Neither tab’s total ever includes the other direction’s numbers, and the GSTIN/name columns correctly show the counterparty on the sales rows (the customer) and the supplier on the purchase row (the vendor) — never the company’s own letterhead details on either.
What stays entirely in your browser
Every PDF dropped into this tool is read and parsed entirely client-side, in JavaScript running in your own browser. Nothing about your invoices, your suppliers, or the GSTINs on them is ever sent anywhere. That matters here specifically, since a folder of GST invoices routinely contains exactly the kind of business detail — customer names, supplier relationships, transaction values — that shouldn’t need to leave your machine just to get reformatted into a spreadsheet.
Try it
If you’re staring down a folder of invoice PDFs that need to become one clean GSTR working file, PDF Invoices to GST Data does the extraction, the sales/purchase classification, the reverse-charge determination, and the missing-invoice-number check for you — with every field correctable by hand before you export, and a clear flag on anything it genuinely couldn’t read. It’s free, runs in your browser, and nothing needs an account.