---
title: "Lender league table (ranked roster of top lenders)"
method: POST
path: "/v1/league-table"
tags: ["Data & Analytics"]
---

# Lender league table (ranked roster of top lenders)

`POST /v1/league-table`

## Request

- Base URL: `https://api.rateapi.dev`
- URL: `https://api.rateapi.dev/v1/league-table`
- Auth: API key in header `X-API-Key`

Loan-officer competitive-landscape report: a ranked roster of the top N lenders publishing a given mortgage product in a state today — per-lender rows (name, best APR/rate, product, position, bps above the leader) — plus the market min/median/max. Extends `/v1/get_benchmarks` (which returns only min/median/max + the single lowest institution) with the full ranked table. Mortgage-only (30-year or 15-year fixed). This route is the single source of truth; the MCP `get_league_table` tool and the consumer app call it.

**Authentication required**: Pass your API key in the `X-API-Key` header.

## Request body

- object
  - `state` string, required: 2-letter US state code.
  - `product` '30yr_fixed' | '15yr_fixed', required: Mortgage product to rank.
  - `limit` integer: Top N lenders to return (default 10, clamped 1..50).
  - `metric` 'apr' | 'rate': Rank by apr (default) or rate.
  - `include_institution_context` boolean: Include compact, source-labelled NCUA member, location, asset, founding-year, and selected service facts per lender.

## Response `200`

Ranked roster of top lenders plus the market distribution.

- object
  - `state` string: Two-letter US state code.
  - `product` '30yr_fixed' | '15yr_fixed': Mortgage product the table ranks.
  - `metric` 'apr' | 'rate': Which figure the table is ranked on.
  - `limit` integer: Maximum number of records this response may contain.
  - `count` integer: Rows returned in the table.
  - `total_lenders` integer: Distinct lenders that qualified before the table was truncated.
  - `as_of` string, nullable: When the underlying rates were published.
  - `table` object[]: The ranked lenders, best first.
    - `position` integer: Position in the table, 1 being best.
    - `lender` string: Name of the credit union offering this rate.
    - `apr` number, nullable: Annual percentage rate, which includes fees and is the figure offers are ranked on.
    - `rate` number, nullable: Note rate as an annual percentage, excluding fees.
    - `product` '30yr_fixed' | '15yr_fixed': Mortgage product this row prices.
    - `product_name` string, nullable: The lender's own name for the product, verbatim from its rate page.
    - `display_name` string, nullable: Readable product label; product_name is the raw source name.
    - `bps_above_leader` integer, nullable: How far this row sits above the leading value, in basis points. Zero for the leader, null when it could not be computed.
    - `best` boolean: True for the leading row.
    - `eligibility` Eligibility: Membership-eligibility guidance for this credit union (design doc §4). Present only when the CU has a cu_eligibility record; absent otherwise. Guidance based on public charter data and institution websites — final determination is made by the institution. Current open-world records never assert ineligibility.
      - `status` 'open_to_anyone' | 'likely_eligible' | 'eligible_via_employer' | 'eligible_via_military' | 'geo_mismatch' | 'unknown', required: Eligibility assessment. `open_to_anyone`: a documented open path matched. `likely_eligible`: positive geo, school, worship, or occupation evidence matched. `eligible_via_employer`: the supplied employer matches a verified SEG list. `eligible_via_military`: the supplied military affiliation matches. `unknown`: cannot be determined from available data, including geographic non-matches. `geo_mismatch` is retained only for wire compatibility and is not emitted from current unattested records.
      - `join_path` string: Reserved legacy field. Current person-specific offer annotations omit unbound flat join paths; use graph-backed eligibility_explanation.conditions instead.
      - `eligibility_type` string, required: Legacy field-of-membership type on flat annotations. Graph-authoritative offer annotations use the neutral value `graph` rather than a stale flat path label.
      - `confidence` number, required: Flat-record confidence for legacy annotations; graph verdict confidence when an active publication is authoritative.
      - `matched_rule` object: The concrete rule that produced a positive `status`. Explanatory only — never present on `geo_mismatch` or `unknown` (those carry no positive rule).
        - `type` 'county' | 'state' | 'employer' | 'occupation' | 'military' | 'school' | 'worship' | 'association_open', required: Exact positive rule category: geographic county/state, employer, occupation, military, school, worship, or an open association path.
        - `detail` string: Human-readable specifics of the match (e.g. the matched county name or employer entry).
    - `institution_context` InstitutionContext: Compact NCUA quarterly facts for relationship-aware offer comparison. Present only when explicitly requested and available.
      - `source` string, required: The dataset these institution facts came from.
      - `cycle_date` string, date, required: Quarter-end date of the call report cycle these facts describe.
      - `members` integer, nullable, required: Current members, or null when not reported.
      - `locations` integer, required: Number of branch locations reported.
      - `total_assets` number, nullable, required: Total assets in US dollars, or null when not reported.
      - `year_opened` integer, nullable, required: Year the credit union began operating, or null when not reported.
      - `services` object, required: The subset of reported services most relevant to acting on a decision.
        - `online_membership` boolean, nullable, required: True when membership can be applied for online, or null when not reported.
        - `online_loan_application` boolean, nullable, required: True when loans can be applied for online, or null when not reported.
        - `financial_counseling` boolean, nullable, required: True when financial counseling is offered, or null when not reported.
  - `market` object: Market distribution for the slice. min/median/max are APRs; `count` is the raw row count of the slice, which can exceed `total_lenders` when a lender publishes many rate rows.
    - `min_apr` number, nullable: Lowest APR in the slice.
    - `median_apr` number, nullable: Median APR across INSTITUTIONS — one vote per lender, using each lender's single best APR (basis = `total_lenders`, not `count`). Changed 2026-08-05: previously computed over every rate row, which let a lender publishing a large rate matrix pull the median toward itself (observed 11-29bps higher than the institution-level value). Matches the median in the daily rank snapshot.
    - `max_apr` number, nullable: Highest APR in the slice.
    - `count` integer: Rate rows in the slice (NOT the median's basis — see `total_lenders`).
  - `note` string: Plain-language explanation of what this result means.

## Other responses

- `400`: The request was malformed — a missing required field, an unparseable body, or a parameter outside its allowed range. The `error` field names what was wrong; retrying the identical request will fail identically.
- `401`: No API key, or a key that is unknown or disabled. Send it in the `X-API-Key` header. Free keys are issued instantly by POST /keys with an email address. The response carries `WWW-Authenticate` pointing at this host's protected-resource metadata, and the `RateLimit-*` headers describing the Free-tier budget a key issued now would carry (scope `anonymous`) — a rejected request spends none of it, so `Remaining` equals `Limit`.
- `429`: Quota exhausted for the current window. `Retry-After` gives the seconds to wait; `upgradeInfo` explains how to lift the cap. Retrying before the reset will fail again and still counts as a request.

---

[API](https://skmtc.dev/rateapi/apis/rate-api.md) · [All operations](https://skmtc.dev/rateapi/apis/rate-api/llms.txt) · [OpenAPI document](https://skmtc.dev/rateapi/apis/rate-api/revisions/06a6db76995f?raw)
