Skip to content

Single student walkthrough

POST /v1/calculate accepts a JSON document describing one student's degree status and term schedule, and returns a JSON document with the prorated annual eligibility and per-term distributions.

Endpoint

curl -X POST https://loanlimit.app/v1/calculate \
  -H 'content-type: application/json' \
  --data-binary @student.json

Request shape

The request body has three parts: student-level fields (dependency, degree, grade), financial-aid context (subsidized amount, legacy-borrower status, optional institutional limit), and terms (an ordered list of enrollment periods).

Student-level fields

Field Type Required Notes
dependency_status "dependent" | "independent" yes Use the dependency status applicable after any dependency-override determination.
degree_level "undergraduate" | "graduate" | "professional" yes Drives the loan-limit lookup.
grade_level "first_year" | "second_year" | "third_year_and_beyond" | null required for undergraduate, must be null for graduate/professional
subsidized_amount integer ≥ 0 no (default 0) The subsidized portion of the annual limit being claimed. Must not exceed the statutory subsidized max for the student's tier.
plus_denied boolean no (default false) Set true for dependent undergraduates whose parents were denied a PLUS loan — they receive independent-tier unsubsidized limits.
is_legacy_borrower boolean no (default false) Only valid for graduate/professional. Marks a student who borrowed before July 1, 2026 and remains within their expected time to credential per §685.203(b)(2)(iv)(B).
cost_of_attendance integer or null required when is_legacy_borrower=true; must be null otherwise Used to derive legacy Grad PLUS max as COA - $20,500. Must be greater than $20,500.
total_limit integer or null optional If your institution has set a lower program limit under §685.203(m)(2), pass it here. The API uses the lower of this and the statutory max.

Terms

A list of 1–4 Term objects in chronological order:

Field Type Required Notes
label string yes E.g., "Fall", "Spring". Appears in the response.
full_time_credits number > 0 yes Your institution's full-time credit standard for the term (usually 12 for undergrads).
credits_enrolled number > 0 yes The student's enrolled credits (Title IV eligible) for the term.
actual_final_credits number ≥ 0 or null no Set when reconciling after an enrollment change — see Enrollment changes.
prior_disbursement_sub integer ≥ 0 or null no Dollar amount of subsidized loan actually disbursed for an already-disbursed term.
prior_disbursement_unsub integer ≥ 0 or null no Same for unsubsidized.
prior_disbursement_grad_plus integer ≥ 0 or null no Same for Grad PLUS (legacy borrowers only).

Single-term mode

For single-term loans (one Term in the list), include the academic-year denominator:

Field Type Required Notes
academic_year_terms integer 2–4 yes when terms has length 1 The term fraction is 1 / academic_year_terms. Use 2 for semester, 3 for trimester, 4 for quarter.

Must be null when there are 2+ terms.

Response shape

The top-level response contains:

  • annual_ft_credits — sum of full-time credits across all terms (the denominator for the reduced annual percentage).
  • reduced_annual_pct — rounded enrollment-intensity percentage applied to the annual limit.
  • annual_basic_eligibility_sub / ..._unsub / ..._grad_plus — the reduced annual limit in dollars for each loan type.
  • terms — per-term breakdown (see below).
  • total_equal / total_proportional — annual sums for each distribution method.
  • show_both_options — whether equal and proportional differ meaningfully (false when there's a single eligible term or all eligible terms have identical credits).
  • warnings — flat list of human-readable warning strings.

Each terms[i] carries label, credits_enrolled, full_time_credits, enrollment_intensity_pct, disbursement_pct, enrollment_status, equal, proportional, adjustment_sub, adjustment_unsub, adjustment_grad_plus, adjustment_explanation, adjustment_source_index, warning, and final_term_overdisbursement. See the API reference for full schemas.

Enrollment changes

When a student drops or adds credits mid-term, set actual_final_credits on the affected term. The engine recalculates the entire student's entitlement using actual enrollment, treating the original credits_enrolled as the projection.

Set prior_disbursement_* on already-disbursed terms to tell the engine what was actually given. If omitted, the engine assumes the full equal-distribution amount was disbursed.

If a prior disbursement exceeds the recalculated entitlement for that term, the carry is deducted from the next undisbursed term. The adjustment_* fields on the absorbing term report the deduction; adjustment_source_index points back to the term that caused it. If the over-disbursement is on the final term (no future term to absorb), final_term_overdisbursement: true is set; the school must seek reimbursement per regulatory process.

prior_disbursement_* values are validated against the pre-drop maximum. Submitting a value that exceeds what could have been disbursed at the original enrollment level returns a 422 with a field-level error such as terms_2_priorDisbursementSub: "Cannot exceed $4,500".

Error responses

See Error handling.