CoWIN - Exploring the API 💉

Just to be fair this has not been tested. This is not the way I booked my slot for the vaccination.

TL;DR: You can probably automate vaccination slot booking

It is common knowledge that CoWIN has opened up its APIs. You can find more details about version 2 of their APIs on the API Setu website (I'm yet to meet someone who gets their APIs right in v1 😛).

Most of the writeups which I found online were of people using the API for notifying them of new slots opening up. Some have already created notification services around it like getjab and VaccinateMe along with a few Telegram groups popping up to serve the same purpose. Even PayTM has launched a Vaccine Slot Finder tool.

But all these tools were missing a critical component, which was to actually book the slot. Due to shortage of vaccine availability these slots get filled up before anyone has time to react to the notifications. I wanted to go 1 step further and automate the booking process on availability of the slot.

The APIs are divided into 2 parts Public APIs and Protected APIs. The most important are the Appointment Availability APIs which are essentially what are used for the notifications feature. They have both a public and a private endpoint. The public endpoint does not need any form of auth, however it may return relatively older data (upto 30 mins old) due to returning from cache. The private endpoint requires auth (although I've hit it numerous times without auth, and it has returned the available slots successfully).

But for booking a slot using /v2/appointment/schedule I believe auth is critical.

A POST request needs to be made to the above mentioned endpoint, with the following data

data = {
    "center_id": center_id,
    "session_id": session_id,
    "beneficiaries": [beneficiary],
    "slot": slot,
    "dose": 1
}

The center_id, session_id and slot parameters are obtained from the response of the Appointment Availability APIs. The dose parameter is either 1 or 2 based on which vaccination shot you're going in for. And the beneficiary is obtained from decoding your JWT Access Token.

While testing this out I faced numerous 403 Forbidden responses, reasons for which I've mentioned at the end of the post. A 403 potentially indicates a ban, and given I didn't have enough data to infer the cause of the ban, I chose to spoof the headers on my request, so as to make it appear to be coming from a browser. You can find the headers used in my request below.

headers = {
    'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:87.0) Gecko/20100101 Firefox/87.0',
    'Accept': 'application/json, text/plain, */*',
    'Accept-Language': 'en-US,en;q=0.5',
    'Origin': 'https://selfregistration.cowin.gov.in',
    'Authorization': f'Bearer {access_token}',
    'DNT': '1',
    'Connection': 'keep-alive',
    'Referer': 'https://selfregistration.cowin.gov.in/',
    'Sec-GPC': '1',
    'TE': 'Trailers',
}

The access_token mentioned above is used for the authentication of your request. You may read more about the same at jwt.io. They also have an in browser JWT Token decoder on their homepage. To get your personal JWT access token you'll have to login to the self registration cowin portal (use a PC). Once logged in, check the Session Storage of your Browser (told you to use a PC) and your JWT Access Token will pe present in the userToken variable. You may use decode this on jwt.io to fetch more details.

{
  "user_name": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "user_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "user_type": "BENEFICIARY",
  "mobile_number": xxxxxxxxxx,
  "beneficiary_reference_id": xxxxxxxxxxxxxx,
  "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:87.0) Gecko/20100101 Firefox/87.0",
  "date_modified": "2021-05-05T14:32:15.194Z",
  "iat": xxxxxxxxxx,
  "exp": xxxxxxxxxx
}

For obvious reasons I've censored out the sensitive information. You may use this decoded JSON to get your beneficiary ID since it is a required data argument in the POST request. And the entire encoded userToken can be used as is, in the headers for auth.

I've a added a simple python script to automate the entire process from finding a slot to booking a appointment in a GitHub Gist. The code certainly could use a lot of cleanup. It is just intended to be a proof of concept. Feel free to use / modify it as required.

Personal Observations and Speculative Notes

  • Frequetly hitting the API will get you banned. I'm not sure if this is purely an IP level ban or a ban on unauthenticated requests from your IP. I've had a server IP of mine banned. Even authenticated requests are getting 403 responses. This could also be a location (country) based ban.
  • User tokens are valid for around 30 mins. After that you may need to login again via OTP and update your access token in the script.
  • The CoWIN API website claims that one can make upto 100 requests in 5 minutes. However I believe the threshod is much lower than this. Maybe something like 10 requests per min. Again, more data is needed to infer this. I din't want to risk banning my Home IP, and didn't experiment further.
  • The Vaccination Appointment APIs accessible via the GET HTTP method can be used just the same with or without auth. I'm not sure if this is intended since the data is anyways accessible openly or just a misconfiguration. The POST methods however require auth, else they return a 401 Unauthorized.
  • Using all the headers mentioned is obviouly not necessary. But I have faced 403's for not having spoofed atleast the "User-Agent" header. I presume this is checked at the backend to verify the request is coming in from a browser and not something like python requests library or curl. Since they would use their own headers.