# How India Post article tracking works — and how this app does it

Research done 2026-10-11 against the live `indiapost.gov.in` site.

## 1. The article number (the only thing you need)

Every trackable India Post item — Speed Post, Registered Post, parcel, money order
reference — carries a **13-character article number** following the UPU **S10**
standard:

```
E  E  4  8  2  9  1  7  3  5  IN
│  │  └──── 8 digits ────┘│  └ country
│  │           9th digit = check digit, computed from the 8 before it
│  └ service hint
└ service hint
```

- **First letter → service:** `E` = Speed Post / EMS, `R` = Registered post,
  `C` = Registered parcel, `L` = air parcel, `V` = insured, `A` = bulk Speed Post,
  `P` = passport / Speed Post. (Second letter is a regional/series code.)
- **9th digit → check digit.** Weights `8,6,4,2,3,5,9,7` on the 8 digits, sum mod 11;
  `11 − remainder` (10→0, 11→5). A number with a wrong check digit was misread —
  this is why the app validates locally before any lookup.
- Find it on line 2 of the booking receipt / Speed Post slip.

## 2. The three official ways to track (no app needed)

1. **Website** — `indiapost.gov.in` → *Track & Trace* → enter the number and solve
   the **CAPTCHA**. (As of Oct 2026 the site is a Next.js app and the tracking call
   is a server action gated behind that CAPTCHA.)
2. **SMS** — text `POST TRACK <13-digit number>` (all caps) to **166** or **51969**.
   Works on all networks; standard SMS charges apply; history kept ~60 days.
3. **DakSewa app** — India Post's official mobile app (replaced the old Postinfo app).

## 3. Why there is no free official API — and how the working trackers do it

The Department of Posts publishes **no public tracking API**, and since the
October 2026 site revamp the website's own tracking call is gated behind a
CAPTCHA (the server literally returns `captcha_required`). That killed the
plain scraper sites: trackspeedpost/gospeedpost now show "servers
busy/under maintenance", speedposts.in is down.

The sites that still work fall into three groups:

| Who | How |
|---|---|
| **speedindiapost.com** | Runs its own backend that talks to India Post (handling the CAPTCHA server-side) and exposes a **public, no-key JSON API**: `GET speedindiapost.com/api/track?id=<number>` → full scan trail. This is what the app below uses. |
| **speedposttrack.io** | Same idea, different gate: Cloudflare Turnstile on their frontend → `/api/token` → websocket `track` event → their backend proxies India Post. |
| **myspeedpost.com** | Laravel + Livewire app — the tracking form posts to their Livewire backend, which proxies India Post. |
| **daakly** | Official business relationship — they sell Speed Post labels at contracted India Post rates, so they get data access. |
| **17track / Ship24 / AfterShip** | Commercial data partnerships. (Ship24 also has a free tier: 100 lookups first month, then 10/month, API included — the app's fallback.) |

All of them ultimately read the same India Post scan data.

## 4. How this app works

```
browser ──POST /api/track {numbers}──▶ Vercel serverless ──GET api/track?id=──▶ speedindiapost.com ──▶ India Post
   ▲                                          │
   └──────── normalized timeline ◀────────────┘
```

- **Frontend** (`index.html`): validates numbers with the S10 check digit,
  detects the service from the prefix, stores your list + notes in
  `localStorage`, renders status pills + checkpoint timelines (newest first),
  bulk import, CSV export, share links (`?add=`), PWA installable.
- **Backend** (`api/track.js`): validates, dedupes, and caches (10 minutes),
  then calls the public `speedindiapost.com/api/track` endpoint — **no API key
  needed anywhere**. Max 20 numbers / 3 parallel lookups per request.
- **No tracking backend? Assisted mode.** If the upstream feed is unreachable,
  the app still validates numbers and gives one-tap actions per article: copy
  number, open the official site, and the exact SMS text. A **Demo** toggle
  shows the full UI with sample journeys.

## 5. Honest limits

- Live data needs no key, but it comes from third-party feeds, not from India
  Post directly. The primary feed (speedindiapost.com's public API) is
  **intermittent**: it returned "no data" 3/3 times for JM248236995IN and then
  the full 15-event delivered trail 3/3 times minutes later. When the primary
  feed has no data, the app automatically retries through a second source —
  myspeedpost.com's tracking backend (their Livewire `submit` → `fetchStatus`
  flow, replicated server-side; no Turnstile needed on that path).
- If both sources are empty, the card offers one-tap fallbacks: 17track, the
  official India Post site, and SMS.
- Scans appear a few hours after the item is *handed in* at the post office,
  not at online booking time. "Not found" on day zero is normal.
- This project is not affiliated with the Department of Posts.
