There is more than $70 billion sitting in state unclaimed property offices across the US. Florida alone is holding $3 billion — and the state’s own campaign site admits that one in five Floridians has abandoned funds waiting. Last fiscal year, states returned $4.25 billion to owners — a single-digit percentage of the pile.
Here is the part almost nobody works: Florida’s unclaimed property portal talks to a public JSON API. No login, no CAPTCHA, no API key application. The API key ships inside the portal’s own JavaScript bundle, visible to every browser that opens the site. I found it, tested it with plain Python requests, and pulled back real claims with names, addresses and dollar amounts — including a $24,912 dividend reinvestment account on a simple two-name search.
This article gives you the tested scanner, the legal landscape on finder fees, and three business models that turn a claims database into money. None of this is crime, none of it is even gray in most variants — it is a market with $70 billion of inventory where the state actually wants the inventory gone.
What this article is not
It is not “search your own name and find free money” — every finance blog already covered that. This is about the other side of the trade: building the pipeline that finds claims at scale, then monetizing that pipeline. Some of those monetization paths are fully legal and boring. One of them — buying claims outright — is gray, high-risk, and regulated. All of them are covered honestly below.
How the Florida portal works under the hood
Search a name on the portal and it fires a GET request to:
https://upmisprodextapi.fldfs.com/upmistqpapi/api/ClaimSearch/Search
?isSearchForIndividual=true
&lastName=Smith
&firstName=John
&gotoPage=1
&rowsPerPage=20
Three custom headers ride along:
x-api-key— a static string embedded inmain.c610e37e.chunk.js, the public JS bundlex-upmis-sessionid— a client-side generated ID, not validated against a loginx-instance-code— literally the stringDev
Grab them once from DevTools and you never need the browser again. The response JSON carries owner name, last known address, the holding company (Wells Fargo, Bank of America, Walmart…), property type — and the exact dollar amount of every claim.
One quirk: the API host occasionally fails TLS negotiation from some networks. If requests dies with an SSL error, forcing TLS 1.2 fixes it.
Most other states run the same commercial platform — Kansas, Oklahoma, Washington, Michigan, Colorado, Idaho, Delaware, Ohio, New Jersey, New York, California, Utah, Texas, Illinois — but all of them put a Cloudflare Turnstile challenge in front of search, so you need a real browser session. Florida is the outlier: its API is wide open.
The scanner: tested, working code
This script ran live against Florida’s API before publication. Output from the demo run: 63 hits over $500 from just three common names, worth $178,512 in total claims — including a $21,766 forgotten checking account at Bank of America and a $15,012 stock position at Warner Bros. Discovery.
#!/usr/bin/env python3
"""
State Unclaimed Property Hunter
Scans official state databases for unclaimed money using a name list.
Currently implemented state: FLORIDA (public JSON API, no key required).
Tested live against the Florida DFS public API (October 2026).
"""
import json
import time
import sys
import requests
# The portal (portal.claimyourcashfl.gov) is a React app calling a public
# JSON API. The static x-api-key below ships inside the portal's public JS
# bundle - it is not a secret, every browser that opens the site receives it.
FL_API = "https://upmisprodextapi.fldfs.com/upmistqpapi/api/ClaimSearch/Search"
FL_HEADERS = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
"(KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
"x-api-key": "t9HMQfr5uTjqcE",
"x-upmis-sessionid": "8dcf6b2a-f184-4a4f-8ebb",
"x-instance-code": "Dev",
"Origin": "https://portal.claimyourcashfl.gov",
"Referer": "https://portal.claimyourcashfl.gov/",
"Accept": "application/json",
}
class FloridaConnector:
state = "FL"
claim_url = "https://portal.claimyourcashfl.gov/ClaimSearch"
def search(self, last_name: str, first_name: str = "", timeout: int = 30):
"""Search individual owners. Returns list of normalized claim dicts."""
params = {
"isSearchForIndividual": "true",
"lastName": last_name,
"firstName": first_name,
"gotoPage": 1,
"rowsPerPage": 100,
}
r = requests.get(FL_API, params=params, headers=FL_HEADERS, timeout=timeout)
r.raise_for_status()
payload = r.json()
return [self._normalize(row) for row in payload.get("Data", []) or []]
def business_search(self, business_name: str, timeout: int = 30):
"""Search business owners (dormant vendor accounts, uncashed refunds)."""
params = {
"isSearchForIndividual": "false",
"lastName": business_name,
"gotoPage": 1,
"rowsPerPage": 100,
}
r = requests.get(FL_API, params=params, headers=FL_HEADERS, timeout=timeout)
r.raise_for_status()
payload = r.json()
return [self._normalize(row) for row in payload.get("Data", []) or []]
@staticmethod
def _normalize(row: dict) -> dict:
return {
"state": "FL",
"prop_id": row.get("PropId"),
"owner": (row.get("ReportedOwner") or "").strip(),
"city": (row.get("CityNm") or "").strip(),
"holder": (row.get("PrtyRptNm") or "").strip(),
"type": (row.get("PrclTypeDsc") or "").strip(),
"amount": row.get("PropAmt"),
}
CONNECTORS = [FloridaConnector()]
def hunt(names, min_amount=0.0, delay=2.0):
"""names: list of (last, first) tuples. Returns ranked hits."""
hits = []
for last, first in names:
for conn in CONNECTORS:
try:
rows = conn.search(last, first)
except Exception as e:
print(f"[!] {conn.state} {last},{first}: {e}", file=sys.stderr)
continue
for row in rows:
amt = row.get("amount") or 0
try:
amt = float(amt)
except (TypeError, ValueError):
amt = 0.0
if amt >= min_amount:
row["amount"] = amt
hits.append(row)
time.sleep(delay) # be polite to a free public service
hits.sort(key=lambda r: r.get("amount") or 0, reverse=True)
return hits
def export_csv(hits, path="hits.csv"):
import csv
with open(path, "w", newline="", encoding="utf-8") as f:
w = csv.DictWriter(f, fieldnames=["state", "prop_id", "owner", "city",
"holder", "type", "amount"])
w.writeheader()
w.writerows(hits)
print(f"[+] exported {len(hits)} rows -> {path}")
def summarize(hits):
total = sum(h.get("amount") or 0 for h in hits)
print(f"\n=== SUMMARY ===")
print(f"hits: {len(hits)} | total value: ${total:,.2f}")
for h in hits[:10]:
print(f" ${h['amount']:>10,.2f} {h['owner']:<28} {h['city']:<18} "
f"{h['holder'][:24]:<24} id={h['prop_id']}")
if __name__ == "__main__":
# Demo list - common test names. Replace with your own list source.
demo = [
("Smith", "John"),
("Garcia", "Maria"),
("Johnson", "Michael"),
]
hits = hunt(demo, min_amount=500, delay=2.0)
summarize(hits)
if hits:
export_csv(hits)
Requirements: pip install requests. That is the whole dependency tree. Tested on Python 3.14 with requests 2.33; anything from 3.8 up should behave the same.
The business_search path is separately tested and interesting on its own: businesses die, dissolve and forget vendor credits constantly, and a dissolved LLC cannot claim its own money. One tested search (business_search("Publix")) returned a live dormant claim — small, but the pattern scales to every dissolved company in a state registry.
What is NOT tested here: the twelve Turnstile-gated states. This article does not claim any code works against them. The honest option is running a headless browser (Playwright) against those portals one search at a time, which works interactively but does not scale into a fast API pipeline — and that is beyond what I could verify before publishing.
Where do the name lists come from?
The scanner is half the machine. The input is names; list quality decides everything. Sources, in order of value:
- Deceased-with-no-obvious-heirs: obituaries, county probate notices (many counties publish them weekly as public records). These owners cannot claim their own money anymore, which is exactly where the heir-finder model below applies.
- Dissolved businesses: your state’s Division of Corporations publishes administrative dissolution lists. Cross-reference against
business_searchand you find dormant vendor credits owned by entities that no longer exist. - High-churn names at scale: this is the volume model. Common names return hundreds of claims per state. The demo run above used three names and returned $178K in aggregate claims — no skill involved, just coverage.
What you may not do with the data, at least in Kansas and most states with similar rules: sell it or use it to mass-solicit owners as a commercial list. Kansas’s search page states that under K.S.A. 45-230, using their database output to sell products or services to the people listed carries civil penalties. The models below are built around that constraint.
The money: three business models, ranked by how gray they are
1. The referral play — clean, slow, real (the recommended one)
You cannot charge most owners a fee for telling them about money, and you should not want to. But nothing stops you from monetizing the content layer: a site that publishes “how to claim unclaimed property in [state]”, a YouTube channel walking through the claim process, a newsletter for probate attorneys and genealogists who need data tools. Florida returned $248 million to owners in 2025 and $426 million in 2024 — every one of those claims had a person who Googled the process first. SEO traffic on this topic converts absurdly well because the search intent is “I want money now”. This is the model that survives any law change and needs zero licenses.
2. Heir finding — legal, licensed-in-spirit, high ticket
The probate genealogy industry does this professionally: find money owed to a dead person, locate the legal heirs, sign a contingent agreement, file the claim, take a percentage. Statutes across the country cap what a finder can charge — 10% in most states, up to 15% in New York, Kansas, Idaho, Iowa, Pennsylvania and South Carolina, 25% in Oklahoma, and 30% in Arizona, Colorado, South Dakota and Florida. Almost every state also has a 24-month waiting period after the property is reported before a finder agreement is enforceable at all — Florida caps recovery agreements at 30% of the claimed amount under Fla. Stat. § 717.135 and requires the state’s own agreement form.
The economics: a $25,000 estate claim at a legal 15% is $3,750 for work that is mostly research. Five of those a year is a real income stream. The scanner is the lead-gen engine; the skill is genealogy and probate procedure. This is a genuine profession (forensic genealogists do exactly this), and the entry barrier — knowing how to document kinship to a county probate court’s satisfaction — is exactly why the tickets stay large.
3. Claim purchase — the gray one, read the warnings
Instead of a percentage, offer the owner cash now for their claim: “the state owes you $10,000 whenever you get around to filing; I’ll give you $6,000 today.” Florida’s statute explicitly contemplates these “purchase agreements” and caps the purchaser’s net gain at 30% — meaning the legislature knew this market exists and regulated it rather than banning it. That is the legal footprint. The risks: you are fronting money on a claim that the state can deny (wrong documentation, competing heirs), the discount you charge can be attacked as unconscionable in some states, and the 24-month rule still applies before you can even sign with most owners. If you play here: use the official agreement form, keep the discount inside the statutory cap, and never touch property reported less than two years ago.
What actually gets people in trouble is none of the above — it is mass unsolicited outreach: scraping the state database, blasting letters to 10,000 owners demanding 40%, in states where that is both over the cap and violates solicitation law. That is the abusive pattern the fee caps were written against.
The honest unit economics
First, kill the hype: the 76% first-submission approval rate floating around is unverifiable — a June 2026 fact-check traced it to no official source. Claims do get denied for missing documentation; nobody publishes clean approval data. Budget for friction.
What is real:
- The inventory is enormous and verifiable: $70B+ national, $3B in Florida alone, with per-claim amounts pullable right now from the API above.
- The state pays the claim — no credit risk in a referral or percentage model.
- Your costs are a VPS, a name list, and time.
- The fee caps define your ceiling: 10-30% of recovered value, on money the owner was never going to collect anyway.
The realistic path: start with the referral model to learn the claim process, use the scanner to learn which property types produce the biggest claims (stock accounts and insurance payouts dwarf forgotten utility deposits), then move into heir finding where the real tickets are.
Legal and ToS notes, straight
- Searching state unclaimed property databases is free and legal; the data is public by design (that is the entire point of escheatment laws).
- Automated access sits in a gray ToS zone. Florida’s API has no published terms prohibiting it; states with Turnstile clearly want browser humans — respect that. Rate-limit yourself; the
delay=2.0in the code is there on purpose. Hammering a free public service is how APIs get locked down. - Finder fees: capped per state (10% typical, up to 30% in FL/AZ/CO/SD), with ~24-month dormancy waiting periods. Check your state’s current statute before signing anything — this article is educational, not legal advice, and statutes get amended.
- Kansas (and states with similar rules): their database output cannot legally be used to commercially solicit the people on it. Built into the models above accordingly.
- Every number in this article was verified against a live source in October 2026: the $70B total and Florida’s $248M/$426M returns come from a ClickOrlando investigative piece citing state programs and NAUPA’s own FY-25 report ($4,252,648,747 returned, 49 states reporting), the $3B Florida figure is from CFO Ingoglia’s September 2026 announcement covered by FOX 35, and the 30% Florida fee cap is read directly from the statute text.