<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[gstinapi.in]]></title><description><![CDATA[gstinapi.in]]></description><link>https://gstinapi.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Sat, 07 Nov 2026 11:02:59 GMT</lastBuildDate><atom:link href="https://gstinapi.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[GSTR-2A vs GSTR-2B for developers: which one to fetch, and the mistake that cost us a day]]></title><description><![CDATA[GSTR-2A is live and split into 12 tables, GSTR-2B is one monthly ITC statement. How to fetch both as JSON, and the mistake that cost us a day.]]></description><link>https://gstinapi.hashnode.dev/gstr-2a-vs-gstr-2b-for-developers-which-one-to-fetch-and-the-mistake-that-cost-us-a-day</link><guid isPermaLink="true">https://gstinapi.hashnode.dev/gstr-2a-vs-gstr-2b-for-developers-which-one-to-fetch-and-the-mistake-that-cost-us-a-day</guid><category><![CDATA[api]]></category><category><![CDATA[india]]></category><category><![CDATA[fintech]]></category><category><![CDATA[Tutorial]]></category><dc:creator><![CDATA[tarun Vaghasia]]></dc:creator><pubDate>Tue, 27 Oct 2026 07:30:05 GMT</pubDate><content:encoded><![CDATA[<p>If you are building anything on top of GST purchase data (vendor reconciliation, ITC checks, a close-the-books dashboard), you will be asked for "GSTR-2A" and "GSTR-2B", usually as if they were the same thing. They are not, and picking the wrong one costs you either accuracy or money.</p>
<p>Here is how they differ from a developer's side, how to fetch both as JSON, and the mistake that cost us a day.</p>
<h2>The short version</h2>
<table>
<thead>
<tr>
<th></th>
<th>GSTR-2A</th>
<th>GSTR-2B</th>
</tr>
</thead>
<tbody><tr>
<td>Behaviour</td>
<td>Live: updates as suppliers file</td>
<td>A statement generated for a month, then it stays as it is</td>
</tr>
<tr>
<td>Shape</td>
<td><strong>12 separate tables</strong>, one call each</td>
<td>One consolidated statement, one call</td>
</tr>
<tr>
<td>Good for</td>
<td>Latest supplier filings; TDS, TCS, imports, ISD credits</td>
<td>The monthly input tax credit picture</td>
</tr>
</tbody></table>
<p>If you only need "what ITC do I have for April", fetch 2B. If you need "what has this supplier filed so far", or the TDS or TCS entries, fetch 2A.</p>
<h2>GSTR-2A: twelve tables, not one document</h2>
<p>The network serves 2A as 12 tables: <code>b2b</code>, <code>b2ba</code> (amended), <code>cdn</code> (credit and debit notes), <code>cdna</code>, <code>impg</code> (import of goods), <code>impgsez</code>, <code>tcs</code>, <code>tds</code>, <code>amdhist</code> (bill-of-entry amendment history), <code>ecom</code>, <code>ecoma</code> and <code>isd</code>.</p>
<p>A full 2A for one GSTIN and one period is therefore up to 12 calls. TDS and TCS are not separate endpoints: they are two values of the table field.</p>
<pre><code class="language-bash">curl -X POST "https://www.gstinapi.in/v1/gstr2a/fetch" \
  -H "x-api-key: YOUR_API_KEY" -H "Content-Type: application/json" \
  -d '{ "table": "b2b", "gstin": "27ABCDE1234F1Z5", "ret_period": "042026" }'
</code></pre>
<p><code>ret_period</code> is <code>MMYYYY</code>. Most tables also take <code>ctin</code> (one supplier's GSTIN) as a filter. <code>amdhist</code> is the odd one: it also needs <code>portcode</code>, <code>benumber</code> and <code>bedate</code>.</p>
<h3>Empty is normal</h3>
<p>Most GSTINs have nothing in several tables every month: no TCS, no ISD credit, no imports. The network answers those with "No document found for the provided Inputs" (error code <code>RET13509</code>). Through the API used here that comes back as a 400 with that <code>error_code</code>, and it is not charged. It means "this table is empty", not that the call failed, so map it to an empty result in your code. If you treat it as a hard failure, you will page yourself every month-end.</p>
<h2>GSTR-2B: one statement</h2>
<pre><code class="language-bash">curl -X POST "https://www.gstinapi.in/v1/gstr2b/fetch" \
  -H "x-api-key: YOUR_API_KEY" -H "Content-Type: application/json" \
  -d '{ "gstin": "27ABCDE1234F1Z5", "ret_period": "042026" }'
</code></pre>
<p>The statement comes back under <code>data</code>, with the ITC summary, the supplier-wise summary and the document data (each supplier's GSTIN as <code>ctin</code>, its trade name as <code>trdnm</code>, and a checksum). If suppliers filed late and you want a statement that reflects them, you can ask for it to be regenerated on demand and poll until it finishes. Generating and polling cost nothing; the fetch is the only billed step.</p>
<p>Field shapes can still shift as more periods are seen, so read the statement defensively: look fields up by name rather than by position, and do not assume every optional field is present.</p>
<h2>Both need a connection first</h2>
<p>These calls act on a taxpayer's own data, so the GSTIN has to be connected once with an OTP that the GST portal sends to its registered mobile or email. The connection lasts about six hours, after which the API answers <code>NOT_CONNECTED</code> and you reconnect. One connection covers 2A, 2B and the other return calls. There is also a <code>test_mode</code> on every call that returns a sample in the live shape, so you can build the parsing before connecting anything.</p>
<h2>The mistake that cost us a day</h2>
<p>On the first live test, every data call failed. 2A returned "API Authorization Failed". 2B returned "GSTIN in request doesnt match with session". Even a call for a completely different return failed with the same sentence. The OTP step had succeeded, which made it more confusing, not less.</p>
<p>We assumed the feature was not available to us and started chasing settings: a second fresh OTP session to rule out a stale token, an "enable API access" toggle on the GST portal. Neither changed anything.</p>
<p>The turning point was that a <strong>different return failed with the same message</strong>. That meant the problem was not 2A or 2B at all. The username we had connected was the API user of a <em>different</em> GSTIN than the one we were querying. The OTP validates the username on its own, so it passed. Every data call then checks that the username belongs to the GSTIN in the request, so every one failed.</p>
<p>Once we paired the right username with the right GSTIN, everything worked: real invoices from 2A <code>b2b</code>, a real ITC statement from 2B, and a clean "no document found" from 2A <code>tcs</code>.</p>
<p>If you see "GSTIN in request doesnt match with session" or "API Authorization Failed", check the pairing first. The GST portal's Manage API Access page also states that only a selected GSP-ASP combination is permitted API access, so check that selection as well.</p>
<h2>Which one should you build on?</h2>
<ul>
<li>Monthly ITC reconciliation: <strong>GSTR-2B</strong>, one call per GSTIN per month.</li>
<li>Continuous supplier tracking, or anything involving TDS, TCS, imports or ISD: <strong>GSTR-2A</strong>, only the tables you need.</li>
<li>Both, if you want a monthly statement and a running view: they do not overlap enough to cancel each other out.</li>
</ul>
<p>The reference for both, with fields, prices and error cases, is here: <a href="https://www.gstinapi.in/gst-return-api">GSTR-2B and GSTR-2A API</a>, including a <a href="https://www.gstinapi.in/gst-return-api/gstr-2a-vs-gstr-2b-api">2A vs 2B page</a> written for people calling the API.</p>
]]></content:encoded></item></channel></rss>