Skip to main content

Monitor New Florida DBA Registrations for Your Brand

A fictitious-name (DBA) filing is how someone registers a trade name to do business under. Polling the Sunbiz Daily API for names that match your brand lets you spot look-alikes within days of registration — a practical, low-cost input to brand and trademark monitoring. Look-alikes also appear as new corporations, LLCs, and partnerships, so a thorough watch covers all three record types.

Search fictitious names

On /api/v2/fictitious-names/, the fictitious_name filter is a case-insensitive substring match. Pair it with a period for a rolling window of newly registered trade names. Every mirror name column is trigram-indexed, so this search is fast — the same speed as the corporate name search below.

curl -H "X-API-Key: sb_your_key_here" \ "https://sunbizdaily.com/api/v2/fictitious-names/?fictitious_name=sunshine&period=30d"

Check corporate names and partnerships too

Brand collisions also register as corporations and LLCs (corporation_name on /api/v2/filings/) and as partnerships (name on /api/v2/partnerships/). All three name filters behave the same way — substring, case-insensitive, and trigram-indexed — so cover all three for a complete watch.

# Corporations and LLCs curl -H "X-API-Key: sb_your_key_here" \ "https://sunbizdaily.com/api/v2/filings/?corporation_name=sunshine&period=30d" # Partnerships (LP / LLP / GP) curl -H "X-API-Key: sb_your_key_here" \ "https://sunbizdaily.com/api/v2/partnerships/?name=sunshine&period=30d"

Each business-name filter (fictitious_name, corporation_name, and the partnership name) matches the whole value you pass as a single substring — commas included — so a pasted trade name like Sunshine Cafe, Bakery is one lookup, not two terms. To watch several distinct brand variants, send one request per variant (see Narrow your terms below).

Get the registrant with include=parties

When a look-alike turns up, the next question is who filed it. Add include=parties and each row gains its owners (or officers / partners) inline — so a single list request returns both the matching businesses and the people behind them, with no per-row detail call.

curl -H "X-API-Key: sb_your_key_here" \ "https://sunbizdaily.com/api/v2/fictitious-names/?fictitious_name=sunshine&period=30d&include=parties" { "filings": [ { "fictitious_name": "SUNSHINE STATE PROPERTY PARTNERS", "document_number": "G26000083540", "status": "A", "county": "ORANGE", "city": "ORLANDO", "state": "FL", "zip": "32801", "owners": [ { "position": 1, "name": "BEGINNERS PROPERTY ACADEMY LLC" } ] } ], "pagination": { "total": 1, "page": 1, "per_page": 25, "total_pages": 1, "total_capped": false } }

Without include=parties the owners field is null, keeping the default payload small. Owners are redaction-masked: a registrant who filed a privacy takedown returns no parties (the business name and city still appear, so you never miss the match itself).

The reliability gotcha: poll wide, never narrow

This is the one thing that decides whether a brand watch actually catches everything. Sunbiz releases filings to us with a lag, so the most recent filing-dates are always still filling in: a match dated today may not land in the mirror until a day or two later. For fictitious names and partnerships, the API exposes only the filing date — there is no "ingested at" timestamp you can query.

That breaks the obvious design — "every run, fetch the last 7 days." Filings dated inside last week's window keep arriving after this week's window has already slid past them, so a narrow, non-overlapping poll misses them permanently, with no ingestion-time filter to catch up with later.

Corporate filings are the exception: /api/v2/filings/ carries source_date (the daily file a filing first appeared in) and accepts source_date_start/source_date_end, so for that one resource you can ask "what arrived since my last run" directly. Two caveats keep it from replacing the technique below: it does not exist on the other two resources this recipe watches, and it is null for filings that predate the daily feed, so it is a feed monitor rather than a historical filter. The wide-window-plus-dedupe approach works uniformly across all three, which is why it is what this guide teaches.

The fix is to make the window much wider than your polling interval and dedupe on your side:

  • Poll a wide, overlapping window. Run daily or weekly, but always re-scan a full period=30d. Late-arriving filings fall inside the next run's window and get picked up. period accepts yesterday, 7d, 14d, 30d, and all — any other value silently falls back to 7 days rather than erroring, so for a window wider than 30 days use an explicit start_date (e.g. start_date=2026-01-01) instead.
  • Deduplicate persistently. Keep a durable set of document_number (fictitious / partnership) and corporation_number (corporate) values you have already alerted on, so the overlap doesn't re-alert.
  • Treat "new since last run" as "in my window but not in my seen-set" — never as "filed since my last run." The two are not the same here.

This recipe watches for newly registered look-alikes. To instead check whether a name is already registered (regardless of when), drop the date window entirely — use period=all, or a start_date far enough back to cover it — since an existing registration may be months or years old.

Narrow your terms

Substring matching casts a wide net, so a short or generic term pulls in unrelated businesses: fictitious_name=sun returns SUNNY STYLES and SUNCOAST SENIOR INSURANCE ADVISORS alongside any real "Sun" brand. Watch distinctive, specific terms. The filter matches the whole value as one substring (a comma is treated literally, not as an OR separator), so to track several distinct brand variants send one request per variant:

# One request per brand variant curl -H "X-API-Key: sb_your_key_here" \ "https://sunbizdaily.com/api/v2/fictitious-names/?fictitious_name=tesla&period=30d" curl -H "X-API-Key: sb_your_key_here" \ "https://sunbizdaily.com/api/v2/fictitious-names/?fictitious_name=starlink&period=30d"

Two optional filters tighten the watch further. status=active drops dissolved or withdrawn registrations (an unknown status value returns 422, never a silent no-op), and county=MIAMI-DADE scopes to a single county if your brand is regional:

curl -H "X-API-Key: sb_your_key_here" \ "https://sunbizdaily.com/api/v2/fictitious-names/?fictitious_name=sunshine&status=active&county=MIAMI-DADE&period=30d"

One more thing to know about counts: a very broad term reports a capped total. When more than 10,000 rows match, the response returns "total": 10000 with "total_capped": true — so don't read total as an exact match count for a loose stem. Specific brand terms stay far under the cap, where total is exact.

Build the watch

Putting it together: scan all three resources over a wide overlapping window, request owners inline, and dedupe persistently so only genuinely new look-alikes alert.

import time from datetime import date, timedelta import requests API_KEY = "sb_your_key_here" HEADERS = {"X-API-Key": API_KEY} BASE = "https://sunbizdaily.com/api/v2" DAYS = 30 # look-back window; set wider than your polling interval # Persist this between runs (file, DB, Redis, etc.) — it is what makes a # wide, overlapping window safe to re-scan without re-alerting. SEEN = set() # (endpoint, name filter, dedupe key) for each record type. RESOURCES = [ ("fictitious-names", "fictitious_name", "document_number"), ("filings", "corporation_name", "corporation_number"), ("partnerships", "name", "document_number"), ] # Distinct brand variants — one query each (the name filter matches the whole # value as one substring, so a comma is literal, not an OR). Keep them specific: # substring is greedy. TERMS = ["sunshine widgets", "sunshinewidgets"] def poll_job(poll_url, timeout_s=120): # A broad sweep can be handed back as a background job. Poll until it is # done, then use the same filings + pagination envelope the synchronous # response would have returned. deadline = time.monotonic() + timeout_s while time.monotonic() < deadline: job = requests.get(BASE.rsplit("/api/v2", 1)[0] + poll_url, headers=HEADERS, timeout=30) job.raise_for_status() body = job.json() if body["status"] == "done": return body if body["status"] == "failed": raise RuntimeError("search job failed") time.sleep(2) raise TimeoutError(f"job still running after {timeout_s}s") def new_matches(endpoint, name_field, key_field, term): # Use start_date for the window, not period=Nd: `period` accepts # yesterday / 7d / 14d / 30d / all and silently falls back to 7 days for # anything else, so start_date is the only way to widen past 30 days. A # wide window absorbs late-ingested filings. params = { name_field: term, "start_date": (date.today() - timedelta(days=DAYS)).isoformat(), "include": "parties", # owners/officers/partners inline, no detail call "status": "active", "per_page": 100, } url = f"{BASE}/{endpoint}/" page = 1 while True: params["page"] = page resp = requests.get(url, headers=HEADERS, params=params, timeout=30) resp.raise_for_status() body = resp.json() if resp.status_code == 202: # Broad sweeps can be offloaded to a background job. 202 is a 2xx, # so raise_for_status() does not catch it and the body carries no # "filings" key — poll the job instead of indexing into it. body = poll_job(body["poll_url"]) for row in body["filings"]: key = row[key_field] if key not in SEEN: SEEN.add(key) yield endpoint, row pg = body["pagination"] if page >= pg["total_pages"]: break page += 1 for endpoint, name_field, key_field in RESOURCES: for term in TERMS: for kind, hit in new_matches(endpoint, name_field, key_field, term): name = hit.get("fictitious_name") or hit.get("corporation_name") or hit.get("name") owners = hit.get("owners") or hit.get("officers") or hit.get("partners") or [] who = ", ".join(o["name"] for o in owners) or "(redacted)" print(f"New {kind}: {name} filed by {who}")
  • Schedule the script daily or weekly and route new matches to email or a chat webhook. The wide window means the interval only changes how fast you hear, not whether you catch a filing.
  • Stay under the 1,000-requests-per-hour limit — a handful of brand terms across three resources is well within it.

Prefer a no-code version?

The Lead Mining workspace runs this kind of watch for you: save a brand search and get daily email alerts when new matching filings land — no script, no dedupe bookkeeping. The API is the right tool when you need the matches inside your own systems; Lead Mining is the right tool when you just want the alerts.

Next steps

Create a free key on your API Keys dashboard, then see the API documentation for the full fictitious-name, corporate, and partnership field lists.

Informational purposes only; provided without warranty. Searches run over a delayed third-party mirror with broad substring matching, so results may miss filings or include unrelated ones. Sunbiz Daily is not affiliated with the Florida Division of Corporations and accepts no liability for matches missed, false positives, or actions taken on its output. Always verify against the official Sunbiz.org records before acting, and consult an attorney for trademark matters. Not legal advice.