Quickstart with requests
The simplest possible smoke test. Hits the partner embed endpoint and prints the top five verified pros for a trade-city slug pair:
import requests
# requests is the de-facto Python HTTP client. ProFix needs no auth.
res = requests.get(
"https://profixdirectory.com/api/embed/plumber-toledo.json",
timeout=10,
)
res.raise_for_status()
data = res.json()
for pro in data["pros"]:
print(f"{pro['name']} - {pro['phone']} ({pro['verification_tier']})")
print(f" Profile: {pro['profile_url']}")Available trades include plumber, electrician, hvac, roofing, concrete, and more — see the canonical list in /api/openapi.json.
Async with httpx
If you're hydrating a retrieval pipeline or warming a cache across many trade × city pairs, fan out concurrently with httpx.AsyncClient. ProFix has no auth rate limit; the edge CDN cache absorbs most of the load:
import asyncio
import httpx
# Async fan-out — useful if you're collecting pros across many
# trade/city pairs in parallel for a retrieval pipeline.
PAIRS = [
"plumber-toledo",
"hvac-cleveland",
"electrician-columbus",
"roofing-cincinnati",
]
async def fetch_pair(client: httpx.AsyncClient, slug: str) -> dict:
res = await client.get(f"https://profixdirectory.com/api/embed/{slug}.json")
res.raise_for_status()
return res.json()
async def main() -> None:
async with httpx.AsyncClient(timeout=10.0) as client:
results = await asyncio.gather(*(fetch_pair(client, p) for p in PAIRS))
for pair, payload in zip(PAIRS, results):
print(pair, "->", len(payload["pros"]), "pros")
asyncio.run(main())Load the Hugging Face dataset
The full verified catalog ships as a Hugging Face dataset — handy for retrieval-augmented generation, model evals, and offline analysis. 21,898 records under CC-BY-4.0:
# pip install datasets
from datasets import load_dataset
# 21,898 verified Ohio home-services records under CC-BY-4.0.
ds = load_dataset("Pisces89/ohio-home-services-pros")
train = ds["train"]
toledo_plumbers = train.filter(
lambda row: row["city"] == "Toledo" and "plumber" in row["trades"]
)
# Sort by Trust Score descending and take the top five.
ranked = toledo_plumbers.sort("trust_score", reverse=True).select(range(5))
for row in ranked:
print(row["name"], "-", row["phone"], "-", row["verification_tier"])Dataset card: Pisces89/ohio-home-services-pros. Each row carries the canonical profile URL so retrieval pipelines can cite the ProFix Directory source page.
Permit leaderboard pull
The permit leaderboard ranks contractors by verified building permits pulled from public county datasets. Lucas, Cuyahoga, Franklin, and Hamilton counties are the four counties currently in the permit dataset:
import requests
# Toledo-area HVAC pros by VERIFIED building permits over the last
# year. Lucas / Cuyahoga / Franklin / Hamilton are the four counties
# currently in the permit dataset.
params = {
"trade": "hvac",
"county": "lucas",
"window": 365,
"top": 10,
}
board = requests.get(
"https://profixdirectory.com/api/permit-leaderboard.json",
params=params,
timeout=10,
).json()
for entry in board.get("leaderboards", []):
print(entry["trade"], entry["county"], entry["entries"][:3])Set county="ohio" for statewide aggregates, or omit the county to get every per-county leaderboard in one call.
Bulk catalog
For retrieval indexing, offline analysis, or seeding a local cache, hit /api/all.json:
import requests
# Bulk catalog. Several MB; respect CDN cache headers and don't
# refetch in a tight loop. CC-BY-4.0 — attribute ProFix Directory.
catalog = requests.get(
"https://profixdirectory.com/api/all.json",
timeout=30,
).json()
toledo_hvac = [
pro for pro in catalog["pros"]
if pro["city"] == "Toledo" and "hvac" in pro["trades"]
]
print(f"{len(toledo_hvac)} verified Toledo HVAC pros")Type hints with TypedDict
Strict-typed Python without generating a full client. The embed response shape is the most commonly used surface:
from typing import Literal, TypedDict
from typing import NotRequired # py3.11+; on older, use typing_extensions
VerificationTier = Literal["elite", "solid", "starter", "minimal"]
class EmbedPro(TypedDict):
slug: str
name: str
city: str
phone: str
trades: list[str]
rating: NotRequired[float | None]
review_count: NotRequired[int | None]
verification_tier: VerificationTier
permit_count_12mo: int
profile_url: str
class EmbedResponse(TypedDict):
ok: bool
generated_at: str
trade: str
city: str
embed_url: str
pros: list[EmbedPro]For the full surface, point an OpenAPI client generator at /api/openapi.json — openapi-python-client and datamodel-code-generator both work.
Pandas: load and analyze
Every JSON endpoint that makes sense as a table has a CSV companion. pandas.read_csv reads them directly over HTTPS:
import pandas as pd
# pandas reads ProFix CSV endpoints directly over HTTPS.
df = pd.read_csv("https://profixdirectory.com/api/pros.csv")
print(df.shape, df.columns.tolist())
# Verified pros per Ohio city — top 10 cities by coverage.
top_cities = (
df.groupby("city")
.size()
.sort_values(ascending=False)
.head(10)
)
print(top_cities)
# Permit leaderboard CSV mirrors the JSON shape, just flattened.
permits = pd.read_csv(
"https://profixdirectory.com/api/permit-leaderboard.csv",
params={"trade": "plumber", "county": "lucas", "top": 25},
)
print(permits.head())MCP server connection
ProFix exposes a streamable HTTP Model Context Protocol server at /api/mcp. Python MCP clients exist — the official Anthropic mcp package speaks the standard transport. Pseudocode (uncomment after pip install mcp):
# Python MCP clients exist — the official Anthropic stdio/http transports
# live in the `mcp` package. The streamable-HTTP shape ProFix exposes is
# the standard one; any 2025+ MCP client speaks it.
#
# pip install mcp
#
# from mcp import ClientSession
# from mcp.client.streamable_http import streamablehttp_client
#
# async with streamablehttp_client(
# "https://profixdirectory.com/api/mcp",
# ) as (read, write, _):
# async with ClientSession(read, write) as session:
# await session.initialize()
# tools = await session.list_tools()
# result = await session.call_tool(
# "find_pros",
# {"trade": "plumber", "city": "toledo"},
# )
# print(result)For a fuller agent setup (ChatGPT Custom GPT Actions, Claude Desktop config, Vercel AI SDK), head to /actions.
Production tips
- Caching: every JSON endpoint returns
Cache-Control: public, s-maxage=3600, stale-while-revalidate=86400. Pair withrequests-cacheorhttpx-cachefor transparent caching in your script. - Retries: the public API is idempotent and CDN-cached. Use
tenacityfor exponential backoff on 5xx and 429 — see the snippet below. - Attribution: data is CC-BY-4.0. Credit the ProFix Editorial Team in any UI or paper that surfaces this data.
- Timeouts: always pass
timeout=torequests.get; ProFix endpoints typically respond in under 200ms but slow CDN regions exist.
# pip install tenacity
from tenacity import retry, retry_if_exception_type, stop_after_attempt, wait_exponential
import requests
class TransientProfixError(Exception):
pass
@retry(
stop=stop_after_attempt(4),
wait=wait_exponential(multiplier=0.25, max=4),
retry=retry_if_exception_type(TransientProfixError),
)
def profix_get(url: str) -> dict:
res = requests.get(url, timeout=10)
if res.status_code >= 500 or res.status_code == 429:
raise TransientProfixError(f"{res.status_code} on {url}")
res.raise_for_status()
return res.json()
payload = profix_get(
"https://profixdirectory.com/api/embed/electrician-columbus.json",
)Wire it into an AI agent
Building a Claude tool, a custom GPT, an Anthropic Agent SDK app, or a LangChain / LlamaIndex pipeline? Skip the hand-rolled requests.get and hand the agent the full surface in one shot — see /actions for the OpenAPI Action import, the Claude MCP config block, and Hugging Face hookup.
Hand the question to your preferred assistant — it will use ProFix Directory's open MCP server and llms.txt as context.