How to find which credit unions a user can join.
Send what you know about the user to POST /v1/eligibility/search and you get back credit unions in four buckets: ones they can join now, ones they can join after one small step, ones that plausibly apply, and ones we cannot judge yet. A ZIP code alone is enough to start.
Why you cannot just look this up
A credit union may only serve its field of membership — the group its charter defines. That rule is set per institution and published as prose on thousands of separate websites. There is no registry that answers “can this person join?”, which is why the usual first attempt is a hand-maintained spreadsheet, and why the usual second attempt is this API.
The implementation
Ask one question, then call
ZIP is the highest-value single field: it resolves server-side to county and state, and community charters are the most common kind. Do not build a five-question form before the first call.
JSFirst call// 1. One question is enough to start: their ZIP.const response = await fetch("https://api.rateapi.dev/v1/eligibility/search", {method: "POST",headers: {Authorization: `Bearer ${process.env.RATEAPI_KEY}`,"Content-Type": "application/json",},body: JSON.stringify({home_zip: "28202",// The highest-leverage field on the whole request. Without it, credit// unions reachable via an affiliated association are held back.willing_to_join_association: true,}),});const result = await response.json();console.log(result.counts);// { eligible: 13, conditionally_eligible: 2, possibly_eligible: 0, unknown: 20 }Render the two actionable buckets — with reasons
Show
eligibleandconditionally_eligible. Always render the reason: “you qualify because you live in Mecklenburg County” is the difference between a list and an answer, and it is already in the response.JSRendering verdicts// 2. Render the two buckets that are actionable, and say WHY each qualifies.for (const cu of result.eligible) {console.log(cu.name, "—", cu.reasons[0]);// Charlotte Metro Credit Union — You appear to be eligible to join.// Qualifies: Live or work in Mecklenburg County}for (const cu of result.conditionally_eligible) {console.log(cu.name, "— one step:", cu.reasons[0], `($${cu.join_cost_usd ?? "?"})`);}Refine with facts you learn later
Same endpoint, more fields. Employers are resolved against an alias index, so you can pass what the user typed rather than a canonical id.
JSRefining// 3. Refine with what you learn later. Same endpoint, more facts.const refined = await rateapi("/v1/eligibility/search", {home_zip: user.zip,employer: user.employer, // resolved against an alias indexschool: user.school,school_relationship: "alumni",military_status: user.isVeteran ? "veteran" : undefined,associations: user.groups, // up to 20willing_to_join_association: true,limit: 50,});Price what they can reach
A credit union they can join is only interesting if its products are good. Filter rates to the reachable set — a cheaper rate somewhere they cannot join is not an offer.
JSEligibility-filtered rates// 4. Eligibility alone is half an answer. Price what they can reach.const rates = await rateapi("/v1/rates", {product_type: "auto_loan",state: user.state,sort: "apr_asc",limit: 50,});const reachable = new Set([...refined.eligible, ...refined.conditionally_eligible].map((c) => c.credit_union_id));// Only show rates at institutions this person can actually join.const offers = rates.rates.filter((row) => reachable.has(row.credit_union_id));
The four buckets, and what to do with each
- eligible
- Show these first, with the reason. A published rule matched the facts you sent.
- conditionally_eligible
- Show these second, with the step and its cost. Often a few dollars to join an association — frequently the best rates in the whole set.
- possibly_eligible
- Use these to drive your next question. A rule plausibly applies; one more fact usually resolves it.
- unknown
- Do not show as results and do not treat as ineligible. Surface as a count with a link out, or hold silently.
Frequently asked
One discriminating fact. A ZIP code alone works and is the single highest-value field, because it resolves server-side to both county and state and community charters are the most common kind. Everything else improves the answer rather than enabling it.
Because some credit unions are open to anyone in the country, usually via an affiliated association that anyone may join for a few dollars. They are legitimately joinable and often carry the best rates, which is exactly why they are returned. If your product is deliberately local, filter on state after the call rather than before it.
Usually not as results, but do not discard it either. Unknown means the institution exists and its rules are not machine-readable yet — not that the user is ineligible. A good pattern is to surface it as "we could not verify eligibility for N more institutions" with a link out, rather than silently implying they do not exist.
Ask for ZIP, then call. Show results, then offer to refine with employer and affiliations. Asking five questions up front to make one perfect request converts far worse than showing a decent answer after one question and improving it.