Scoring API
Score many transcripts in one request, from a script, a notebook, or an agent.
Request
Send POST /api/v1/score with a JSON body holding transcripts: up to 500 of them, each with up to 100 courses and an optional id. The id can be any string up to 200 characters. It comes back with the result so you can match results to your own records.
Each course can be:
- a code, like
CS 124; - a code with a section title, like
CS 199: Python for Data, for courses whose sections cover different topics; - an object that says when the course was taken, like
{"id": "CS 124", "year": 2025, "term": "Fall"}, so the score uses that semester's course description. Without bothyearandterm, it uses the most recent offering. AddsectionHint(a section code likeUAI) to pick a section without its title.
Course codes, with or without a section title, are at most 200 characters. A course listed twice in one transcript counts once.
Response
catalog.exportedAt says when the course catalog behind the scores was built. It's refreshed each semester, and scores can shift slightly when it is, so compare scores computed against the same export. results has one entry per transcript, in request order:
id: yours, ornullif you didn't send one.score: 0 to 100, the same number the site shows for those courses.k: how many courses matched. A transcript needs at least 2 to score above 0.matchedandunmatched: normalized codes found and not found in the catalog. Transfer credit and courses not offered since 2004 end up inunmatched.ambiguous: codes offered only as titled sections, such asCS 199, which need a title or section hint to score.
Example
curl -s https://www.ratemytranscript.com/api/v1/score \
-H 'Content-Type: application/json' \
-d '{
"transcripts": [
{
"id": "student-1",
"courses": [
"CS 124",
"MATH 241",
"PHYS 211",
"MUS 130"
]
},
{
"id": "student-2",
"courses": [
{
"id": "CS 124",
"year": 2025,
"term": "Fall"
},
"CS 128",
"CS 225",
"CS 199"
]
}
]
}'returns (scores rounded here; the API returns full precision):
{
"catalog": {
"exportedAt": "2026-09-10T20:28:15.221Z"
},
"results": [
{
"id": "student-1",
"score": 73,
"k": 4,
"matched": [
"CS 124",
"MATH 241",
"PHYS 211",
"MUS 130"
],
"unmatched": [],
"ambiguous": []
},
{
"id": "student-2",
"score": 37,
"k": 3,
"matched": [
"CS 124",
"CS 128",
"CS 225"
],
"unmatched": [],
"ambiguous": [
"CS 199"
]
}
]
}The same request from Python:
import requests
transcripts = [
{"id": "student-1", "courses": ["CS 124", "MATH 241", "PHYS 211", "MUS 130"]},
# ...one entry per student, up to 500 per request
]
response = requests.post(
"https://www.ratemytranscript.com/api/v1/score",
json={"transcripts": transcripts},
timeout=120,
)
response.raise_for_status()
for result in response.json()["results"]:
print(result["id"], round(result["score"], 1), result["unmatched"])Limits and Privacy
- No key is needed. A full batch of 500 transcripts takes several seconds, so send students together rather than one request each.
- A malformed request gets HTTP 400 with an error naming the entry, such as
transcripts[0].courses[1] must be a course code or {id, year?, term?, sectionHint?}. - Nothing in a request is logged or stored. The API only needs course codes and semesters, never names, grades, or student numbers, so use an id that means nothing outside your records.
For Agents and Tools
The same reference, in forms programs read:
/openapi.json: an OpenAPI 3.1 specification of the endpoint;/developers.md: this page as Markdown;/llms.txt, also served as/ai.txt: an index that tells agents where the API and these docs are.