Working with unofficial transcript data at Rutgers
I spent a bunch of time two years ago helping a small research group piece together student records that weren't available through official channels. What we ended up building isn't tied to the university directly, and it became a reference point for anyone trying to do similar work. People keep asking about it now and then, so here's how it actually works and what you need to watch out for. The project is exactly what the name suggests: a compilation of student academic records gathered from sources that aren't part of the registrar's official reporting system. Transfer credits, course substitutions, independent study notations, pass/fail choices, and the occasional grade change that never made it into the public dashboards. The goal was to surface patterns that standard data releases simply don't show. Most people assume this kind of data pulls from the university directly. It doesn't. The records come from student-submitted documents, public FERPA-adjacent filings, alumni-shared copies, and a few scraped institutional pages that weren't meant to function as data sources. That matters because the ingestion pipeline has to handle messy input formats from day one.
Here's the practical breakdown of how the whole process runs. Students submit their unofficial transcripts through a hosted form. Each submission gets parsed with a custom script that looks for course codes, credit hours, term dates, and final grades. The parser runs a validation pass against the known Rutgers course catalog to flag anything that doesn't match existing sections. From there, the records go into a structured database where deduplication happens by student ID, term, and course number. A separate normalization layer standardizes the grading scale since some submissions come from study abroad programs with entirely different grade conventions. I learned the hard way that grade normalization is the part that breaks most people. You'd think converting a foreign scale to a 4.0 is straightforward. It isn't. One cohort of students returned from a program in Germany where a 1.0 was the highest grade and a 4.0 was the lowest. Another group came back from a partner school in Japan using a different credit-hour system entirely. I spent about three weeks building conversion tables and cross-referencing them against each institution's published standards before the data stopped looking garbage-y. If you're starting this from scratch, don't skip the normalization step. Build it first. The rest of the pipeline is easier than that. One edge case that took me by surprise involved summer session courses. Rutgers runs multiple summer terms, and some of them share the same course code with different section numbers. When students submitted unofficial transcripts, the section numbers were often handwritten or scanned in low resolution. The OCR kept misreading section 0101 as 0110, which meant the parser was assigning summer courses to the wrong term entirely. That threw off term GPA calculations and made it look like students were repeating courses they hadn't actually repeated. The fix was to add a confidence threshold to the OCR output and flag anything below 85 percent accuracy for manual review. It added maybe two hours per batch of submissions, but it saved the dataset from being quietly wrong across thousands of rows.
The database itself is stored in PostgreSQL with a schema designed around the Rutgers academic calendar. There are tables for students, terms, courses, enrolled_courses, grades, and a notes table for anything the automated system couldn't resolve. A simple API sits on top that lets researchers query by major, term, or specific course sequences. It's not built for high concurrency. The current setup handles maybe fifty simultaneous queries before response times start degrading. If you're planning to pull large datasets for analysis, batch the requests and throttle between calls. That alone will prevent most connection errors. There are some real limitations here that the project documentation doesn't always highlight. The biggest one is selection bias. The students who voluntarily submit unofficial transcripts tend to be more engaged with the research process than the general population. That skews the data toward upperclassmen and students in majors that require more transfer credit documentation. First-year students and those in professional programs like nursing or engineering show up less frequently because their course sequences are more rigid and leave less room for the kind of record variations this project captures. You're not getting a representative picture of the whole student body. You're getting a picture of the students who had something worth sharing in their transcripts. Another issue is data decay. Course catalogs change every year. Prerequisites shift. Course codes get retired or merged. A transcript from 2019 might list a course that no longer exists under that exact title. The parser tries to map old codes to current equivalents using an archival lookup table, but that table has gaps. Some departments stopped maintaining archived code references after 2020. When I hit those gaps, I had to manually cross-reference old syllabi and bulletin archives to verify course equivalencies. It's slow work and it doesn't scale well.
Get the Full Details

If you want access, the data is available through the project's public interface. There's no formal download link you can just grab and run with. You need to register, state your intended use, and agree to a data use restriction that prohibits re-identification of individual students. The approval process usually takes about three business days if your request is clear. Vague research proposals get sent back for clarification, which adds another day or two. Don't waste time on a half-formed request. For most people who just need to explore the data, I'd suggest starting with a focused query rather than a broad pull. Ask for a specific term range, a particular college or department, and a defined set of variables. That keeps the response manageable and reduces the chance of hitting rate limits. The API documentation is minimal but functional. It covers the main endpoints and shows example requests in cURL format. Also worth noting: this isn't an official Rutgers initiative. The university hasn't endorsed it or provided infrastructure support. Everything runs on independently hosted servers. If the funding for the hosting runs out, the data goes offline. That's not a hypothetical concern. The project has gone dark twice in its history for brief periods when server payments slipped. Most of the data survived because it was backed up, but users who didn't keep their own copies lost access temporarily. Make sure you're saving snapshots of anything you pull.
There are alternative approaches if this project isn't suitable for what you need. The Open Educational Data Initiative maintains a broader set of university transcripts that includes some Rutgers records but isn't as granular. State-level education departments sometimes release anonymized aggregate data that can approximate what this project offers, though usually with enough lag to make it less useful for current research. If your question is about longitudinal patterns within a single institution, this dataset is still one of the more complete unofficial sources available. If you need officially verified records, you'd have to go through FERPA-compliant channels with the registrar, which is a much slower and more restrictive path. The raw schema is available if you want to build something similar from scratch. The parsing scripts are open source and documented, though they're written in Python 3.10 and depend on a few libraries that haven't been updated recently. The main dependencies are pandas for data handling, pytesseract for the OCR fallback, and SQLAlchemy for the database layer. Nothing exotic. If you're comfortable with that stack, you can replicate the ingestion pipeline in a weekend. The normalization tables are the part that takes real time to build and verify. I've seen a lot of people try to reuse this data without accounting for the selection bias I mentioned earlier. They publish findings that sound impressive and then miss the fact that their sample is heavily weighted toward students who already had unusual academic paths. The data doesn't lie. It's just not as generalizable as people want it to be. Keep that in mind before you treat it as representative of the whole campus population.