Skip to content

Bulk CSV walkthrough

The bulk endpoint accepts a single CSV with one row per (student, term) and returns a CSV with one row per result term plus one error row per failed student.

Why one row per term

Each student can have multiple enrollment periods. Keeping one row per term means you don't need a custom flat-file schema with a variable number of columns; you can produce the input from any SQL SELECT over your enrollment tables.

Endpoint

curl -X POST https://loanlimit.app/v1/calculate/bulk \
  -H 'content-type: text/csv' \
  -H 'accept: text/csv' \
  --data-binary @students.csv \
  > results.csv

Both application/json (when we add a JSON codec in the future) and text/csv will be supported via content-type negotiation. Today only text/csv is wired in both directions.

Input columns

See the full CSV schema for the column-by-column reference. The summary:

  • Per-student columns (must be identical across every row for the same student_id): dependency_status, degree_level, grade_level, subsidized_amount, plus_denied, is_legacy_borrower, cost_of_attendance, total_limit.
  • Per-term columns (one per row): term_label, term_ft_credits, term_credits_enrolled, term_actual_final_credits, term_prior_disbursement_sub, term_prior_disbursement_unsub, term_prior_disbursement_grad_plus.
  • Identity column: student_id. Rows are grouped by this value. If omitted, the API auto-assigns row:<N> based on input position.

Download the sample template for the canonical column order.

Output columns

Each result row carries the per-term result for one student. The columns are:

student_id, term_label, term_enrollment_status, term_equal_subsidized, term_equal_unsubsidized, term_equal_grad_plus, term_proportional_subsidized, term_proportional_unsubsidized, term_proportional_grad_plus, annual_ft_credits, reduced_annual_pct, total_equal_subsidized, total_equal_unsubsidized, total_equal_grad_plus, total_proportional_subsidized, total_proportional_unsubsidized, total_proportional_grad_plus, error.

The error column is empty on success and carries a human-readable error message when a student row group failed to process (in which case all numeric columns are empty for that student).

Per-student consistency

If you submit two rows for the same student_id with different values for any per-student column (e.g., one row says grade_level=first_year and another says grade_level=second_year), the API returns a single error row for that student:

Inconsistent value for 'grade_level' across rows for student STU042: row 1 has 'first_year', row 3 has 'second_year'

The other students in the request are unaffected.

Handling errors

Always read the error column for every result row. A student that fails validation does not stop the batch — other students still get computed and returned. Plan your downstream logic around partial success.

What is not supported (yet)

  • JSON request/response on this endpoint.
  • File upload via multipart/form-data (the web upload UI at loanlimit.app/bulk uses that, but the public API expects the CSV directly in the request body).
  • Streaming responses for very large batches.