Dev.to WebDev 🛠 Dev 👁 0 📖 2 min read

How to Check Indian Railway PNR Status Programmatically (Node.js & Python Guide)

Original Guide: This article was originally published on IRCTC Developer Platform. Checking the status of an Indian Railways Passenger Name Record (PNR) programmatically has historically been frustrating: brittle web s

Original Guide: This article was originally published on IRCTC Developer Platform.

Checking the status of an Indian Railways Passenger Name Record (PNR) programmatically has historically been frustrating: brittle web scrapers, OCR-based captcha captchas, and random IP bans.

In this guide, we'll cover how to query official CRIS PRS live PNR records in under 100ms using Node.js and Python with zero scraping maintenance.

Anatomy of a 10-Digit PNR

A PNR number consists of:

  • First 3 Digits: PRS Reservation Zone (e.g., 226 for Northern Railway CRIS PRS).
  • Last 7 Digits: Unique transaction serial number.

Method 1: Node.js & TypeScript with irctc-api

The official client SDK handles authentication, keep-alive connections, and TypeScript types out of the box.

Installation

npm install irctc-api

Code

import { IrctcApi } from 'irctc-api';

const client = new IrctcApi({
  apiKey: process.env.IRCTC_API_KEY || 'irctc_live_9a8f2c7e01d4'
});

async function verifyBooking(pnrNumber: string) {
  try {
    const pnr = await client.pnr.check(pnrNumber);

    console.log(`🚆 Train: ${pnr.trainNumber} - ${pnr.trainName}`);
    console.log(`🛣️ Route: ${pnr.from} -> ${pnr.to} | Date: ${pnr.doj}`);
    console.log(`📊 Chart Status: ${pnr.chartStatus}`);

    pnr.passengers.forEach((p, idx) => {
      console.log(`  • Passenger ${idx + 1}: ${p.currentStatus} | Coach: ${p.coach || 'N.A'} | Berth: ${p.berthNumber || 'N.A'} [${p.berth || 'N.A'}]`);
    });
  } catch (error: any) {
    console.error('PNR verification error:', error.message);
  }
}

verifyBooking('2265248473');

Method 2: Python with requests

If you are building in Python (Django, Flask, FastAPI, Celery):

Installation

pip install requests

Code

import os
import requests

API_KEY = os.getenv("IRCTC_API_KEY", "irctc_live_9a8f2c7e01d4")
PNR = "2265248473"

url = f"https://api.irctc.dev/api/v1/pnr/{PNR}"
headers = {
    "x-api-key": API_KEY,
    "accept": "application/json",
    "User-Agent": "pnr-client/v1.0"
}

response = requests.get(url, headers=headers, timeout=5.0)

if response.status_code == 200:
    data = response.json().get("data", {})
    print(f"Train: {data.get('trainNumber')} - {data.get('trainName')}")
    print(f"Date: {data.get('doj')} ({data.get('from')} -> {data.get('to')})")
    print(f"Chart: {data.get('chartStatus')}")

    for idx, p in enumerate(data.get("passengers", []), 1):
        print(f"  P{idx}: {p.get('currentStatus')} | Coach: {p.get('coach', 'N.A')} | Seat: {p.get('berthNumber', 'N.A')} [{p.get('berth', 'N.A')}]")
else:
    print(f"Error {response.status_code}: {response.text}")

Understanding Passenger Fields

Field Description Values
currentStatus Current booking status CNF, RAC 12, WL 45
coach Reserved coach B2, S4, M1, N.A
berthNumber Specific berth number 18, 45, N.A
berth Clean normalized berth type LOWER BERTH, MIDDLE BERTH, UPPER BERTH, SIDE LOWER, SIDE UPPER
chartStatus PRS charting status CHART NOT PREPARED, CHART PREPARED

Recommended Polling Schedule (Smart Backoff)

Do NOT poll PNR every few seconds. PNR statuses only update upon ticket cancellation or chart preparation.

  • > 48 hours to departure: Poll once every 24 hours.
  • 12 – 48 hours to departure: Poll every 4 hours.
  • < 4 hours to departure: Poll every 15 minutes to catch chart preparation.

Resources

📰 Read the original article on Dev.to WebDev

Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.