How a Waitlisted Train Ticket Turned Into a Cloud Hack
IRCTC goes silent on waitlisted tickets booked in advance. So I built a serverless PNR notifier on AWS Lambda — after captchas and cloud-IP blocking killed two other approaches.

The Spark: A Waitlisted Ticket
It began with a booking that should have been routine. I logged into IRCTC, filled in my details for the Duronto Express, and hit "Book Now." When the screen refreshed, my heart sank a little: WL (waitlisted).
At first, I wasn't worried. Surely IRCTC would keep me posted. After all, in 2025, how hard could it be to send an SMS when your waitlist number changes? But as I looked closer, the truth unfolded. IRCTC alerts are sparse: a booking confirmation, maybe a cancellation, and finally a message when the reservation chart is prepared. That's it. The much-promoted "Get SMS" option works only within three days of the journey. For tickets booked weeks or months in advance, the system goes silent.
So there I was — money deducted, journey uncertain, with no clue if things were moving in my favour. My only choice? Refresh the website again and again, or fire off curl commands just to see the same line of text. It felt ridiculous, like being stuck in a loop from a decade ago.
The frustration wasn't just about convenience; it was about control. Every traveller with a waitlisted ticket knows the anxiety: Should I wait? Should I cancel? Should I book a backup? A single timely update could have answered those questions. Instead, the official system left me in the dark.
"Necessity is the mother of invention." — Plato
And that necessity, born out of one small but persistent irritation, lit the spark for what came next.
Why Not Build It Myself?
Once I accepted that IRCTC wasn't going to do me any favours, the question almost asked itself: why not build it myself?
I wasn't looking to reinvent the wheel. All I needed was something small, something practical: a way to check the PNR at intervals, remember the last result, and ping me when something changed. That's the kind of problem backend engineers face every day, just wrapped in a different context.
And I had another advantage: this wasn't 2015 anymore. With tools like GitHub Copilot and AI assistants, writing boilerplate has become less of a chore and more of a collaboration. What might have once taken me half a day could now be done in just over an hour. I'd focus on the logic, and let AI handle the scaffolding.
The idea was simple, but the motivation was bigger. It wasn't just about getting my ticket confirmed — it was about the satisfaction of turning frustration into creation.
"Simplicity is the soul of efficiency." — Austin Freeman
The Roadblocks Along the Way
No good story unfolds without a few bumps, and mine came early.
Attempt 1 — IRCTC itself: captchas and session walls
Surely the source of truth would give me what I needed. But reality hit fast. IRCTC wraps its APIs in a thick layer of session state and captcha verification. Even a single request meant juggling cookies, headers, and a captcha image. It wasn't just inconvenient — it was anti-automation by default. The curl alone looked like a tangle of cookies and random tokens:
curl 'https://indianrail.gov.in/enquiry/CommonCaptcha?inputCaptcha=88&inputPnrNo=4224374267&inputPage=PNR&language=en2%2C' \
-H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)...' \
-b 'JSESSIONID=...; f5avraaaaaaaaaaaaaaaa_session_=...'
Unless I wanted to start solving captchas with OCR (which I didn't), this route was a dead end.
Attempt 2 — Redbus: works locally, dies in the cloud
This one was exciting. Their API returned exactly the data structure I wanted — a neat JSON payload with fields like pnrNo, passengers, and currentStatus. From my Mac, it was smooth and predictable:
curl --location 'https://www.redbus.in/rails/api/getPnrToolKitData' \
--header 'Content-Type: application/json' \
--data '{"mobile": "", "pnr": "4224374267"}'
For a brief moment, I thought I had cracked it. But when I deployed the exact same request on AWS Lambda, everything broke. Calls that worked locally failed in the cloud.
The reason? Redbus applies IP range filtering to block traffic from known cloud providers. My Lambda's IP was automatically blacklisted. Another wall.
Attempt 3 — ConfirmTkt: the breakthrough
Their endpoint was refreshingly simple: a POST with a minimal payload that returned exactly what I needed — passengerStatus, currentStatus, and even a prediction percentage.
curl --location 'https://cttrainsapi.confirmtkt.com/api/v2/ctpro/mweb/4224374267?querysource=ct-mweb&locale=en&getHighChanceText=true&livePnr=true' \
--header 'Content-Type: application/json' \
--data '{"proPlanName": "CP7","emailId": "","tempToken": ""}'
No captcha, no session, no IP filtering. For the first time, Lambda executed successfully, and I had reliable data in hand.
Every roadblock had been frustrating, but also enlightening. Each one showed me a different defence mechanism, and reminded me of something I've learned repeatedly in engineering: persistence and patience usually reveal the door that finally opens.
"Obstacles are those frightful things you see when you take your eyes off your goal." — Henry Ford
Designing the Solution
The goal was simple: poll → diff → notify. The design stayed ruthlessly minimal so I could move fast and avoid operational overhead.
The architecture
At the centre is a tiny AWS Lambda (Python 3.11). It POSTs to the ConfirmTkt endpoint, parses out passengerStatus[] (notably currentStatus and prediction), then compares that snapshot to what it saw last time.
To remember "last time," I didn't spin up a database. AWS Systems Manager (SSM) Parameter Store is perfect for small state — I store a compact JSON at a key like /pnr/<PNR>/last_statuses. Reads and writes are fast, cheap, and need no extra infra.
Scheduling is handled by EventBridge Scheduler. I set a cron like cron(0 * * * ? *) with timezone = Asia/Kolkata, so it runs at the top of every hour IST. The nice part is that Scheduler can pass target input, so you can track multiple PNRs by creating multiple schedules with different JSON inputs (e.g. {"PNR":"4224374267"}). Closer to the journey date, either add a second schedule with rate(10 minutes) or just update the existing one.
What happens on each run
- Fetch the latest PNR JSON via
urllibwith a small payload ({"proPlanName":"CP7",...}) and a sensible timeout. - Extract statuses per passenger into a dict:
{ "1": { "status": "CNF", "prediction": "..." }, "2": { "...": "..." } } - Diff against SSM's previous snapshot to detect meaningful changes — WL→CNF, WL position shifts, prediction deltas.
- Format a crisp message (train, DOJ, class, each passenger line) so it's readable at a glance on a phone.
- Publish to Amazon SNS using a topic you own — subscribe via SMS, email, or both. For testing, a
TEST_SMS=1env flag forces a snapshot every run; set it back to0for only-on-change behaviour.
Configuration is purely environment variables: PNR, SNS_TOPIC_ARN, SSM_PARAM_NAME (optional), HTTP_TIMEOUT, and the logging knobs. No extra libraries — urllib + boto3 only — so the deployment artifact stays a tiny single-file zip.
Small, boring, reliable. Exactly what this problem needed.
The Cost of Simplicity
When I first imagined building this, part of me wondered if I was over-engineering it. Would it be worth the effort and the cloud bill? Once it was running, the answer was obvious: the system is practically free.
| Service | Usage | Cost |
|---|---|---|
| AWS Lambda | ~4,300 invocations/month (hourly) | Free tier (1M/month) |
| EventBridge Scheduler | 1–2 schedules | Free tier |
| SNS — Email | Per notification | ₹0 |
| SNS — SMS (India) | Per notification | ~₹1–1.5 |
| Total | Per journey | ~₹2–3 |
Less than a cup of tea at the station. For the peace of mind it brings, that's a trade I'll happily make.
The Lessons Along the Way
Real-world APIs don't always serve user needs. Official ones may exist, but captchas, session walls, and IP blocking aren't technical quirks — they're deliberate defences.
Cloud design doesn't need to be complicated. No database, no container, no pipeline. A few lightweight AWS services, stitched together thoughtfully, solved the problem with elegance.
AI accelerates creativity. What might have taken half a day was done in just over an hour, with Copilot filling in boilerplate and me steering the logic. The human spark was frustration; the machine's role was speed. Together they made something useful out of what was once just irritation.
"First, solve the problem. Then, write the code." — John Johnson
In the end, this wasn't about trains or tickets. It was about seeing a small gap in the system and proving to myself that I could fill it. And maybe that's what engineering really is — finding better ways to travel through life, one solved annoyance at a time.
🚀 Explore the Code
This was a weekend hack turned into a handy little tool. If you'd like to dive in, experiment, or extend it for your own use case, the full project is open source:
👉 GitHub — IRCTC PNR Status Notifier
Feel free to explore, fork, or adapt it. Contributions, ideas, or even stories of how you used it are always welcome.
Leave a comment
No account needed. Leave the name blank and you'll get a random one.