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 both year and term, it uses the most recent offering. Add sectionHint (a section code like UAI) 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, or null if 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.
  • matched and unmatched: normalized codes found and not found in the catalog. Transfer credit and courses not offered since 2004 end up in unmatched.
  • ambiguous: codes offered only as titled sections, such as CS 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.