Skip to main content

neptun-api

Python License Endpoints

The most complete Python API wrapper for the Hungarian Neptun university system. Reverse-engineered from the Angular web client, covering 1,100+ endpoints across 75+ controllers.

Built and tested against Obuda University, but should work with any Neptun instance by changing the base URL. Instances that require two-factor login (e.g. University of Debrecen) are supported, see Two-factor authentication.

Installation

pip install -e .                   # core API wrapper
pip install -e ".[survey]"         # + automated survey filler
pip install -e ".[all]"            # + survey + dev/test tools

For survey auto-fill, also run after install:

python -m playwright install chromium

Requirements: Python 3.11+, requests

Quick Start

from neptun_api import NeptunAPI

api = NeptunAPI("YOUR_NEPTUN_CODE", "YOUR_PASSWORD",
                base_url="https://neptun.uni-obuda.hu/ujhallgato/api/")
api.authenticate()

# Get your GPA
averages = api.get_term_averages()
for term in averages["termAveragesByTrainings"]:
    print(f'{term["average"]} avg, {term["creditIndex"]} credit index')

# Get the grade for every subject across all terms (full transcript).
# This is the reliable way to read grades — including midterm marks and
# transfer/recognised grades — which get_subject_results_list does not return
# on many instances. (get_subject_result / get_subject_results_list are kept
# only as raw endpoint wrappers; prefer these.)
for g in api.get_all_grades():
    print(f'{g["term"]} [{g["subjectCode"]}] {g["subjectName"]}: {g["grade"]} ({g["gradeName"]})')

# Or one subject's result by its subject id
result = api.get_subject_grade(subject_id)   # {"resultValue": 4, "resultName": "Jó", "passed": True}

# Get current courses
terms = api.get_taken_subjects_terms()
subjects = api.get_taken_subjects(terms[1]["value"])  # [1] skips "0. felev"
for s in subjects:
    print(f'[{s["subjectCode"]}] {s["subjectName"]} - {s["subjectCredit"]} credits')

# Get unread messages
count = api.get_unread_message_count()
messages = api.get_received_messages(0, 20)

# Calendar
from datetime import datetime
events = api.get_calendar_events(datetime(2025, 9, 1), datetime(2026, 1, 31))

# Finances
impositions = api.get_financial_impositions()

# Auto-fill all pending semester surveys (rating 1-5)
from neptun_api import fill_all_surveys
results = fill_all_surveys(api, rating=5)

What's Covered

Area Methods Examples
Dashboard 6 GPA, credit progress, exam entries
Messages 18+ Inbox, sent, deleted, compose, recipients
Calendar 29 Events, export, subscriptions, filters
Subjects & Courses 60+ Taken subjects, registration, course changes
Exams 30+ Registration, results, remaining exams
Grades & Advancement 21 Term averages, credit index, curriculum progress
Financial 50+ Impositions, payments, invoices, scholarships
Personal Data 113 Addresses, emails, phones, documents, language exams
Documents 49 Upload, download, containers, folders
Timetable & Rooms 38+ Room schedule, booking, institutional timetable
Curriculum 12 Templates, subject groups, completable subjects
Request Forms 62 Submit, track, attachments, judgements
Student Card 12 Claims, address, status
Tasks 28 Assignments, documentation, deadlines
Questionnaires 17 + auto-fill List, fill out via browser automation, view results
Consultation 12 Appointments, sign up, drop
E-Materials 28 Course materials, downloads
Online Occasions 24 Virtual classes, Webex/Teams links
Thesis 46+ Application, upload, published theses
Practice 26 External practice, documentation
Erasmus 15 Applications, learning contracts
Dormitory 16 Registration, periods, applications
Final Exams 15 Periods, topics, applications
MeetStreet 70+ Forums, news, events, votes, e-learning
Bank Account 17 Manage payment accounts
Publications 18 Academic publications
User Profile 29 Settings, avatars, onboarding
And more... Specializations, modules, registry sheets, legal remedies

Total: 1,100+ methods

Auto-Fill Semester Surveys

Neptun's end-of-semester opinion surveys (Unipoll) can be filled automatically using browser automation. The filler handles both English and Hungarian survey interfaces.

from neptun_api import NeptunAPI, fill_all_surveys

api = NeptunAPI("code", "password", base_url="https://neptun.uni-obuda.hu/ujhallgato/api/")
api.authenticate()

# Fill all pending surveys with 5/5 rating
results = fill_all_surveys(api, rating=5)

# Custom rating (1-5) and optional text comment
results = fill_all_surveys(api, rating=4, text_answer="Great course!")

# Dry run — fills forms but doesn't submit
results = fill_all_surveys(api, rating=5, dry_run=True)

# Show browser window (useful for debugging)
results = fill_all_surveys(api, rating=5, headless=False)

Each result dict contains subject, course, and status ("submitted", "failed", or "error").

Requires: pip install playwright && python -m playwright install chromium

MCP Server

Run Neptun as an MCP server so any MCP-compatible AI (Claude Desktop, Claude Code, Cursor, etc.) can access your university data.

Stdio (local — Claude Desktop / Claude Code):

pip install -e ".[mcp]"
python -m neptun_api --username YOUR_CODE --password YOUR_PASS

SSE (remote / port-forwarded):

python -m neptun_api --username YOUR_CODE --password YOUR_PASS --transport sse --port 8000

Streamable HTTP:

python -m neptun_api --username YOUR_CODE --password YOUR_PASS --transport streamable-http --port 8000

Or use environment variables:

export NEPTUN_USERNAME=YOUR_CODE
export NEPTUN_PASSWORD=YOUR_PASS
export NEPTUN_BASE_URL=https://neptun.uni-obuda.hu/ujhallgato/api/
export NEPTUN_TOTP_SECRET=BASE32SECRET   # only for two-factor instances (see below)
python -m neptun_api --transport sse --port 8000

Claude Desktop config

Add to claude_desktop_config.json:

{
  "mcpServers": {
    "neptun": {
      "command": "python",
      "args": ["-m", "neptun_api"],
      "env": {
        "NEPTUN_USERNAME": "YOUR_CODE",
        "NEPTUN_PASSWORD": "YOUR_PASS"
      }
    }
  }
}

There is also an installed console command equivalent to python -m neptun_api:

neptun-mcp --username YOUR_CODE --password YOUR_PASS

Available MCP Tools (23)

Tool Description
get_student_info Student profile, neptun code, training
get_dashboard_data GPA, credit progress, unread messages
get_term_averages GPA and credit index per semester
get_all_grades Grade for every subject across all terms (full transcript)
get_subject_grade Result/grade for one subject by ID
get_taken_subjects Subjects taken in a term
get_exam_results / get_exam_results_terms Exam results per term
get_exams_list Available exams for a subject in a term
get_received_messages / get_message_detail Read inbox / a thread
get_calendar_events Calendar events by date range
get_financial_impositions / get_financial_payments Fees and transactions
get_pending_surveys / fill_all_surveys Surveys (Playwright auto-fill)
call_method / list_methods Call/discover any of the 1,100+ API methods
...and more Curriculum, documents, surveys

Authentication

The wrapper uses JWT Bearer tokens. Authentication is automatic — if your token expires mid-session, the client re-authenticates and retries transparently.

api = NeptunAPI("code", "password", base_url="https://your-university.hu/ujhallgato/api/")
api.authenticate()

# Token refresh
api.refresh_token()

# Auto-retry on 401 is built in — you don't need to handle token expiry

Two-factor authentication

Some universities (for example the University of Debrecen) require an authenticator-app code on every login. Their Neptun answers the first login with HTTP 202 and "isTwoFactorRequired": true instead of a token; the official web client then re-sends the login with the 6-digit code in a token field. authenticate() does the same, and you have three ways to supply the code:

from neptun_api import NeptunAPI, NeptunTwoFactorRequired

DEBRECEN = "https://www-h-ng.neptun.unideb.hu/hallgato_ng/api/"

# 1. Pass the code from your authenticator app for this login only
api = NeptunAPI("code", "password", base_url=DEBRECEN)
api.authenticate(totp_code=input("Authenticator code: "))

# 2. Ask for it whenever the client needs to (re-)authenticate
api = NeptunAPI("code", "password", base_url=DEBRECEN,
                two_factor_callback=lambda: input("Authenticator code: "))
api.authenticate()

# 3. Fully unattended: give the TOTP secret and codes are generated for you
#    (this is the base32 key behind the QR code shown when you registered the
#    authenticator — most apps let you view or export it; otherwise delete the
#    key in Neptun and register it again, noting the key this time).
api = NeptunAPI("code", "password", base_url=DEBRECEN, totp_secret="JBSWY3DPEHPK3PXP")
api.authenticate()

If none of these is provided and the instance asks for a second factor, authenticate() raises NeptunTwoFactorRequired (a NeptunAuthError). A wrong or expired code raises NeptunAuthError; after repeated failed logins Neptun may also demand a captcha, which surfaces as NeptunCaptchaRequired — log in once through the website to clear it.

Option 1 only covers the initial login: if the session later expires (401) the client cannot re-authenticate without a fresh code, so long-running scripts should use option 2 or 3. generate_totp(secret) is exported if you need the current code yourself; it is a dependency-free RFC 6238 implementation (6 digits, 30 s, SHA-1, the Google Authenticator defaults Neptun uses).

Raw Requests

For endpoints not yet wrapped, use the raw methods:

# GET
data = api.raw_get("SomeController/SomeAction", params={"key": "value"})

# POST
data = api.raw_post("SomeController/SomeAction", data={"key": "value"})

# PUT / DELETE
data = api.raw_put("SomeController/SomeAction", data={"key": "value"})
data = api.raw_delete("SomeController/SomeAction", params={"key": "value"})

Other Universities

Change the base_url to point to your university's Neptun instance:

# Obuda University
api = NeptunAPI("code", "pass", base_url="https://neptun.uni-obuda.hu/ujhallgato/api/")

# BME
api = NeptunAPI("code", "pass", base_url="https://neptun.bme.hu/ujhallgato/api/")

# ELTE
api = NeptunAPI("code", "pass", base_url="https://neptun.elte.hu/ujhallgato/api/")

# University of Debrecen (two-factor login, see Authentication above)
api = NeptunAPI("code", "pass", base_url="https://www-h-ng.neptun.unideb.hu/hallgato_ng/api/",
                two_factor_callback=lambda: input("Authenticator code: "))

# Any Neptun instance — take the URL of the new student web (it ends in
# /ujhallgato/ or /hallgato_ng/ depending on the university) and append api/

Note: Not all universities may have the same endpoints enabled. Some features (booking, theses, practice) may return 403 depending on your institution's configuration.

Running Tests

# Unit tests
pip install -e ".[dev]"
pytest

# Live integration test (requires NEPTUN_USERNAME / NEPTUN_PASSWORD env vars)
NEPTUN_USERNAME=YOUR_CODE NEPTUN_PASSWORD=YOUR_PASS python tests/test_live_comprehensive.py

Project Structure

neptun_api/
  __init__.py        # Public exports
  __main__.py        # Entry point for python -m neptun_api
  client.py          # API wrapper (1,100+ methods)
  mcp_server.py      # MCP server (stdio / SSE / streamable-http)
  survey_filler.py   # Automated Unipoll survey filler (Playwright)
  models.py          # Dataclasses for common response types
  exceptions.py      # NeptunAuthError, NeptunRequestError
tests/
  test_client.py             # client unit tests (incl. grade API)
  test_models.py             # dataclass model tests
  test_mcp_server.py         # guards MCP tools against client drift
  test_live_comprehensive.py # live smoke test (run directly, needs creds)
docs/
  request_forms.md           # guide to the request-form (petition) endpoints

License

MIT

Metadata

Release files for neptun-api 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for neptun-api 0.1.1
File Size Uploaded
neptun_api-0.1.1.tar.gz 60.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for neptun-api 0.1.1
File Interpreter ABI Platform
neptun_api-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 108.3 kB

Release files / neptun_api-0.1.1.tar.gz

Download URL neptun_api-0.1.1.tar.gz
Size 60.9 kB
Tags Source
SHA-256 checksum
How to use checksums
a1d67d9ffbf6629039cacbbaa1f333421e9d163f0683c727027d9e595e67b20c
BLAKE2b-256 checksum
How to use checksums
28891727b6965a6259792832c1984b78082d063b37ec166871b218fb86cd7585
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.2

Release files / neptun_api-0.1.1-py3-none-any.whl

Download URL neptun_api-0.1.1-py3-none-any.whl
Size 47.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
541da48867cc8f39b6fda2bc7d273ca796842750498dafdc57e73febf2c7b438
BLAKE2b-256 checksum
How to use checksums
9dae3251743cd8c134caf0ce1cb164ded9f0b8cf58163c82d998f5f2c11fa719
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.2

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page