How we collect, verify, and publish mortgage rate data.
This is the long-form technical reference. The About page covers the editorial framing of why RatePerch exists. This page covers exactly how the data gets in, how it's validated, how it's aggregated, what our biases are, and where the data set falls short. If anything here is unclear, email hello@rateperch.com and we'll update this document.
1. Data sources
Every number on RatePerch traces back to one of four primary sources. Each is public or borrower-contributed; nothing is licensed from a proprietary data broker.
(a) Borrower-submitted offers
Borrowers can submit the rate they were quoted — credit bucket, state, loan type, term, lender name, and optionally their Loan Estimate document. This source is still early and may be empty or too small to aggregate for many states. We show sample sizes and never substitute posted lender rates for borrower quotes. Submissions are anonymous by default; even when signed in, the submitter's name is never publicly displayed. Auto-approved rows can appear within seconds; rows held for review may take up to 24 hours.
(b) Credit union posted rates
Scraped weekly from each credit union's public rate-sheet page. See §5 for the full scraping methodology, including how we handle sites that block automated access.
(c) Freddie Mac Primary Mortgage Market Survey (PMMS)
The official weekly U.S. average mortgage rate, published every Thursday by Freddie Mac. We pull this via the Federal Reserve Economic Data API and display alongside our borrower-submitted medians for context. The PMMS is a national average; our medians are state-segmented, so it's normal for state medians to deviate from PMMS by ±0.3% in either direction.
(d) NCUA Credit Union directory
The complete list of 4,000+ federally insured credit unions in the United States, sourced quarterly from NCUA Call Report data. We use this to know which CUs exist and what their headquarters state is. Eligibility classification (who can join) is layered on top — see §6.
2. Borrower submission pipeline
When a borrower submits via the submission form, the following happens in order, server-side:
- Rate limit check. Each submission is throttled per-IP to prevent abuse. We do not store raw IP addresses; we retain truncated SHA-256 digests for abuse correlation. These digests are pseudonymous technical identifiers, not a guarantee of irreversible anonymization.
- Bot challenge. Every submission carries a Cloudflare Turnstile token. We verify it server-side before doing any database work or fraud scoring. Invalid tokens fail the request with HTTP 403.
- Schema validation (Zod). Required fields must be present and within range: credit bucket from a fixed 10-bucket enum, purchase price $1–$50M, down payment 0–100%, interest rate 0.1–25%, term in {10, 15, 20, 25, 30}, etc. Invalid submissions return HTTP 400 with the offending field. No partial saves.
- Identity attach (optional).If the user is signed in via Clerk, the submission is keyed to their internal user ID for the dashboard “my offers” view. If signed out, the submission persists as anonymous and the “open to competing offers” toggle is ignored (we have nowhere to deliver bids).
- Fraud scoring. See §3. Score determines whether the row goes to
approved,pending, orrejectedstatus. - Database insert via service-role.Anon and submission metadata (hashed IP, user agent truncated to 500 chars) are stored alongside the offer. Service-role insert bypasses RLS because the public RLS policy doesn't allow anonymous writes.
- Admin notification. If the row landed in
pending, we email the admin so a human can review within hours.approvedrows don't notify (the dashboard shows the queue counts).
3. Fraud scoring & validation
Every submission runs through automated fraud screening before it's eligible for the public dataset. The screening lands each row in one of three states:
- Auto-approved when the submission passes all checks cleanly and statistical signals match peer submissions.
- Pending human review when something looks unusual but plausible — we hand-review these within 24 hours.
- Rejected at write time when the submission contains impossible values (e.g., APR below interest rate, rates outside any realistic historical range, math errors like down payment exceeding purchase price).
We use multiple independent signals — structural sanity checks, statistical comparison against the relevant peer cohort (same state, loan type, term, credit bucket), and product-specific consistency checks (e.g., FHA and VA loans have eligibility patterns that distinguish legitimate from mis-categorized submissions). We deliberately don't publish the exact rule set — fraud detection works better when potential bad actors don't know what tripwires they need to step around.
We err on the side of over-flagging: false positives land in the moderation queue (cheap to fix; we hand-approve good submissions quickly), while false negatives publish a bad rate that erodes trust. Suspicious submissions are held, not silently dropped — so a real borrower whose offer looked unusual can still see their submission moved to approved once a human eyeballs it.
4. What “verified” means
A small fraction of submissions carry the verified badge. This means the borrower uploaded a document presented as a Loan Estimate — the standardized document lenders provide after an application — and our OCR process confirmed that key structured fields match what the borrower typed within tolerance. The badge confirms document-to-form consistency; it does not mean RatePerch independently authenticated the document with the lender:
- Interest rate: must match to within 0.05 percentage points.
- Loan type, term, and purpose: must match the extracted structured fields.
- Lender name: normalized names must match after common corporate and product terms are removed.
- Loan amount: when both values are available, must match within 1%.
- Plausibility: the extracted rate must pass the applicable Freddie Mac benchmark backstop.
Submissions without an uploaded LE, or where OCR cannot confidently confirm the fields, publish as unverified. They remain borrower-contributed data but do not carry document-to-form confirmation. We make that distinction visible so readers can weight each entry appropriately.
5. Credit union rate scraping
Borrower submissions cover what borrowers actually got. Credit union posted rates cover what's currently being offered to the public. We need both, and the second one we have to fetch ourselves.
The collection workflow runs each Monday after source-level validation tests. Depending on how a lender publishes its rate sheet, we use a standard HTTP fetch, a browser-rendered page, or manual review. We collect only publicly available pricing and do not log in, bypass access controls, or present a blocked source as current.
Layout-adaptive extraction reduces dependence on brittle per-site selectors, but it is not trusted on its own. Every extracted row is constrained to known products and terms, checked for rate/APR/points plausibility, compared with the current Freddie Mac benchmark where applicable, and rejected when a source produces an unexpected shape. A source that cannot be collected reliably is omitted or reviewed manually rather than published with stale or uncertain data.
6. Credit union eligibility classification
Credit unions don't serve everyone — every CU has a “field of membership” (FOM) that defines who can join. We classify each CU into one of four eligibility types, which determines which state pages show its rates:
- National easy-join
- Anyone in the US can join, typically by donating $5–$20 to an associated non-profit. Examples: Alliant Credit Union, Bethpage Federal Credit Union, Connexus. Their rates appear on every state's page.
- Military
- Membership requires active-duty / veteran / family relationship with the military or Department of Defense. Examples: Navy Federal Credit Union, PenFed's legacy military membership tier. Effectively nationwide (military members live everywhere), so rates appear on every state's page with a clear eligibility note.
- State / regional
- Membership requires residency in specific states or counties. Examples: BECU (Washington, Idaho, Oregon, South Carolina), State Employees' Credit Union of NC. Rates appear only on the relevant state pages.
- Employer / community SEG
- Membership requires affiliation with a specific employer, university, or community group (Select Employee Group). These are the long tail of US CUs. Currently not surfaced on state pages because eligibility verification is complex; future versions may add an explicit “Do you work for X?” flow to surface them.
Classification happens at the time we add a CU to the scraper registry. The NCUA Call Report data tells us the CU's FOM type code, but the codes don't map cleanly to user-facing eligibility, so a human reviews each CU's actual membership page before we publish. If you spot a misclassification, report it from the credit union directory and we'll fix it within a week.
7. Aggregation & statistical methods
Time windows
Borrower-quoted offer aggregates use a rolling 30-day window ending today. Older offers don't roll into “this week’s medians” — they get filtered out. Credit union posted rates use the most recent effective date per (lender, loan type, term) tuple; older rates are flagged as superseded but stay in the database for historical context.
What gets aggregated
Headline figures on the homepage and state pages are the medianinterest rate within (state, loan type, term) segments, computed from approved, non-deleted offers. We use the median rather than the mean because, at the small sample sizes typical of a single state, one unusually high quote (a low-credit or non-QM offer, or a typo) would drag a mean well above the rate a typical borrower is seeing. We also drop any rate outside a 2%–12% plausibility band before aggregating — those are data-entry errors, not real first-mortgage quotes. We don't adjust for points, lender credits, or special promotions — adjusting introduces hidden judgment calls that distort comparison.
What gets shown alongside
We disclose sample sizes (“median of N offers”) everywhere a number appears. A 6.42% median from 3 submissions is not the same signal as a 6.42% median from 300 submissions, and we never want to imply otherwise.
Cohort comparisons
The “how do you compare” diagnostic uses state, loan type, and credit bucket when enough borrower observations exist. When the cohort is too small, it says so and falls back to clearly labeled posted-rate and national benchmark context rather than manufacturing a percentile.
8. Display ordering on state pages
On state-rate pages (/best-rates/[state]), credit union posted rates are grouped by loan type and displayed in this order:
- Conventional — always shown first, expanded by default. Covers ~70% of US mortgages.
- FHA — collapsed by default. Covers ~10% of mortgages, typically lower-credit / lower-down borrowers.
- VA — collapsed by default. Covers ~6% of mortgages but only for veterans / active-duty / qualifying family. Often the lowest rates available, but ineligible to most readers.
- Jumbo — collapsed by default. Covers ~14% of mortgages but only in high-cost markets (loan amount above the conforming limit).
This ordering is deliberate. A purely lowest-rate-first sort would put VA rates (typically 0.3–0.5% below conventional) at the top of every state page. Visitors who can't qualify for VA would see a number, get excited, then bounce when they realize the eligibility requirement. The same logic applies to FHA (high-DP FHA is unusual) and jumbo (most borrowers don't cross the conforming limit). Showing conventional first matches the realistic visibility hierarchy for someone searching “best mortgage rates in {state}.”
9. Update cadence
- Borrower submissions: real-time. Auto-approved offers appear in the public dataset within ~5 seconds. Pending offers appear after human moderation, typically within 24h.
- Credit union posted rates: weekly. The primary workflow runs in GitHub Actions each Monday, with validation gates, coverage checks, and failure notifications. A limited Vercel cron remains as fallback.
- Freddie Mac PMMS: on-demand at page render. The FRED API serves the latest weekly value; we cache for 5 minutes at the CDN to keep page-load fast.
- NCUA directory: manual quarterly refresh. The NCUA publishes Call Report data quarterly; we re-import to pick up newly-chartered CUs and dissolved-CU removals.
- State pages cache: 10 minutes at the Vercel edge. Pages revalidate in the background after that, so visitors during stale-time get the cached version while the next visitor gets the refreshed one.
10. Known limitations
This is what we'd want a savvy reader to know before they over-rely on our data. We'll update this section as we close items off.
- State-level only, not city-level. A “best rates in California” page is an aggregate of every California submission. Bay Area jumbo loans price differently from rural-California conventional. Future versions may add metro-level breakdowns.
- Skew when sample sizes are small. Sample sizes per (state × loan type × credit bucket × term) segment can be small enough that a single bad submission disproportionately moves the median. Mitigations: explicit sample-size disclosure on every aggregate, automated outlier detection (§3), human moderation queue.
- ARM products not yet scraped. Adjustable-rate mortgages quote with different conventions (initial fixed period + index + margin + caps). The current scraper extracts fixed-rate only. ARM borrowers should compare elsewhere for now.
- No first-time-buyer program data. State Housing Finance Agency loans, USDA loans, Down Payment Assistance programs — not currently in scope. These are valuable for the audiences that qualify and remain on the roadmap.
- Credit union long tail unrepresented. The NCUA directory contains more than 4,000 institutions, while RatePerch actively tracks only a curated subset with public, interpretable mortgage pricing. Directory inclusion does not imply current rate coverage.
- IP-based geo detection misses some users. Our state-page personalization uses Vercel's IP-to-state lookup. ISP routing through other states (especially for mobile carriers and VPN users) gives wrong results. We expose a manual state picker; future versions will auto-prompt when detection seems uncertain.
- No real-time interest rate locking quotes. Posted rates can be 24–72 hours stale by the time a borrower applies. The same lender quoting our scraped rate might quote 0.1–0.3% different when a real application hits.
11. Roadmap & planned changes
What we're actively working on, in rough order. This isn't a marketing roadmap (no “coming Q4” commitments) — just what's actually queued up and getting attention.
- Publish and improve the Monday Rate Report as a permanent, source-linked weekly record.
- Recruit the first borrower cohort and validate whether the rate diagnostic changes shopping or negotiation behavior.
- Improve state-specific coverage while hiding stale or unreliable lender data.
- Complete mortgage-compliance review before activating any loan-officer monetization or marketplace mechanics.
- Continue strengthening reproducibility, monitoring, and public methodology as the dataset grows.
12. How to report a problem
We're going to get things wrong. The methodology above describes our intent; the implementation can drift, edge cases can slip through, and the world changes. Three ways to flag something:
- Wrong CU eligibility: the credit unions directory has a per-CU report button. We review submissions within a week.
- Wrong rate, wrong lender name, wrong anything in the dataset: email hello@rateperch.com with the page URL and what's wrong. We'll fix and email back.
- Methodology question or critique:same email. If your argument changes our methodology, we'll update this page and credit the change in the “last updated” line at the top.
We treat this page as living documentation. Every change ships with a date stamp; significant changes get called out at the top of the section that changed.
