Getting started¶
Running it¶
git clone https://github.com/Udhay-Adithya/vit_ap_vtop_api.git
cd vit_ap_vtop_api
poetry install
cp .env.example .env # then set API_KEY
poetry run fastapi dev src/main.py
The service listens on http://127.0.0.1:8000. Interactive OpenAPI docs are
at /docs.
Requires Python 3.13 or newer. There is nothing else to configure: the captcha
is solved locally by the client library, so there is no external service and no
key to obtain beyond your own API_KEY.
The API key¶
Every endpoint requires an X-API-Key header matching the API_KEY in your
environment. It gates access to the service; it has nothing to do with VTOP
credentials.
curl -X POST http://127.0.0.1:8000/student/semesters \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{"session": {...}}'
A missing or wrong key is a 401.
The shape of a request¶
Two steps, always:
Log in once at /auth/login. You get back a session.
Send that session with every data request afterwards.
import requests
BASE = "http://127.0.0.1:8000"
HEAD = {"X-API-Key": "your-api-key"}
login = requests.post(f"{BASE}/auth/login", headers=HEAD, json={
"registration_number": "REGNO",
"password": "your-vtop-password",
}).json()
if login["status"] == "otp_required":
otp = input("OTP sent to your registered email: ")
login = requests.post(f"{BASE}/auth/verify_otp", headers=HEAD, json={
"otp_challenge": login["otp_challenge"],
"otp": otp,
}).json()
session = login["session"]
semesters = requests.post(f"{BASE}/student/semesters", headers=HEAD,
json={"session": session}).json()
sem_id = semesters["semesters"][0]["id"]
attendance = requests.post(f"{BASE}/student/attendance", headers=HEAD,
json={"session": session, "sem_sub_id": sem_id}).json()
Credentials are sent exactly once. Everything after that carries the session, which is both much faster and the only way the OTP flow can work at all — see Authentication.
Semester ids¶
Most endpoints take a sem_sub_id. Do not hardcode one — ask for it:
requests.post(f"{BASE}/student/semesters", headers=HEAD,
json={"session": session}).json()
A malformed id is rejected with a 400 before anything is sent to VTOP. An id
that is well formed but wrong is not, and cannot be: VTOP answers an unknown
semester with an empty result rather than an error, so it is indistinguishable
from a semester with no data. Take ids from /student/semesters at the point
of use.
Being a good citizen¶
Every call is a real request against a university server that is not built for automation.
Reuse one session for many calls rather than logging in per request.
Never loop on a failed login. Repeated failures can lock a VTOP account.
Treat an empty result as legitimate. Marks may be unpublished, biometric can be genuinely empty for a day, and grades do not exist until a semester ends.