{"openapi": "3.1.0", "info": {"title": "MPP Public Records Gateway", "version": "1.0.0", "description": "Paid public-records lookups over business entity, parcel, and recorder indexes.", "x-guidance": "This API sells US public-records lookups: business entities and UCC liens, property parcels, and recorder/deed documents. Use the instant GET routes for jurisdictions we have indexed, and the job routes (POST /{service}/jobs, then GET /jobs/{job_id}, then GET /jobs/{job_id}/result) for full coverage everywhere else, which returns within minutes. Payment is HTTP 402: request without a credential to receive a challenge quoting the exact price, then retry the same request with a payment credential to receive the records and a receipt. Quotes are sized per record from the actual result set, so the runtime challenge is authoritative and a request that finds nothing is free.", "contact": {"email": "apify@scotthelvick.com"}}, "x-service-info": {"categories": ["public-records", "business-data", "property-data"], "docs": {"homepage": "https://records-mpp.fly.dev/", "apiReference": "https://records-mpp.fly.dev/openapi.json", "llms": "https://records-mpp.fly.dev/llms.txt"}}, "servers": [{"url": "https://records-mpp.fly.dev"}], "paths": {"/entity/search": {"get": {"summary": "Search business entities", "description": "Search a state business-entity index by entity name or identifier. Instant for indexed jurisdictions (IA, FL, AK, MS); for full coverage beyond them use POST /entity/jobs, which returns results within minutes.", "parameters": [{"name": "state", "in": "query", "description": "State entity index to search.", "required": true, "schema": {"type": "string", "enum": ["IA", "FL", "AK", "MS"]}}, {"name": "name", "in": "query", "description": "Business entity name.", "required": false, "schema": {"type": "string"}}, {"name": "id", "in": "query", "description": "State-issued business entity identifier.", "required": false, "schema": {"type": "string"}}, {"name": "mode", "in": "query", "description": "Name matching mode.", "required": false, "schema": {"type": "string", "enum": ["contains", "starts", "exact"], "default": "contains"}}, {"name": "limit", "in": "query", "description": "Maximum records to return.", "required": false, "schema": {"type": "integer", "minimum": 1, "default": 25}}], "responses": {"200": {"description": "Records returned successfully."}, "402": {"description": "Payment required; the challenge quotes the exact total."}}, "x-payment-info": {"offers": [{"amount": "1", "currency": "usd", "description": "Price per record; the runtime 402 challenge quotes the exact total and is authoritative. Paid in stablecoin (USDC) on Tempo; no minimum beyond the $0.01 floor, so this rail can pay every quote here.", "intent": "charge", "method": "tempo"}, {"amount": "1", "currency": "usd", "description": "Price per record; the runtime 402 challenge quotes the exact total and is authoritative. Paid by card via a Shared Payment Token. Cards carry a 0.50 minimum, so this rail is offered only on quotes of 0.50 or more.", "intent": "charge", "method": "stripe", "minimumAmount": "50"}], "price": {"mode": "dynamic", "currency": "USD", "min": "0.010000", "max": "0.125000"}, "protocols": [{"mpp": {"method": "tempo", "intent": "charge", "currency": "USDC.e"}}, {"mpp": {"method": "stripe", "intent": "charge", "currency": "usd"}}]}}}, "/entity/ucc": {"get": {"summary": "Search UCC lien filings", "description": "Search indexed Florida UCC lien filings by debtor name. Instant for indexed jurisdictions (FL); for full coverage beyond them use POST /entity/jobs, which returns results within minutes.", "parameters": [{"name": "state", "in": "query", "description": "State UCC index to search.", "required": true, "schema": {"type": "string", "enum": ["FL"]}}, {"name": "name", "in": "query", "description": "Debtor name.", "required": true, "schema": {"type": "string"}}, {"name": "mode", "in": "query", "description": "Debtor-name matching mode.", "required": false, "schema": {"type": "string", "enum": ["contains", "starts", "exact"], "default": "contains"}}, {"name": "limit", "in": "query", "description": "Maximum filings to return.", "required": false, "schema": {"type": "integer", "minimum": 1, "default": 25}}], "responses": {"200": {"description": "Records returned successfully."}, "402": {"description": "Payment required; the challenge quotes the exact total."}}, "x-payment-info": {"offers": [{"amount": "4", "currency": "usd", "description": "Price per record; the runtime 402 challenge quotes the exact total and is authoritative. Paid in stablecoin (USDC) on Tempo; no minimum beyond the $0.01 floor, so this rail can pay every quote here.", "intent": "charge", "method": "tempo"}, {"amount": "4", "currency": "usd", "description": "Price per record; the runtime 402 challenge quotes the exact total and is authoritative. Paid by card via a Shared Payment Token. Cards carry a 0.50 minimum, so this rail is offered only on quotes of 0.50 or more.", "intent": "charge", "method": "stripe", "minimumAmount": "50"}], "price": {"mode": "dynamic", "currency": "USD", "min": "0.010000", "max": "1.000000"}, "protocols": [{"mpp": {"method": "tempo", "intent": "charge", "currency": "USDC.e"}}, {"mpp": {"method": "stripe", "intent": "charge", "currency": "usd"}}]}}}, "/parcel/search": {"get": {"summary": "Resolve a parcel", "description": "Resolve one indexed parcel by street address or parcel identifier. Instant for indexed jurisdictions (FL, NC, NY, IN); for full coverage beyond them use POST /parcel/jobs, which returns results within minutes.", "parameters": [{"name": "state", "in": "query", "description": "State parcel index to search.", "required": true, "schema": {"type": "string", "enum": ["FL", "NC", "NY", "IN"]}}, {"name": "address", "in": "query", "description": "Street address to resolve.", "required": false, "schema": {"type": "string"}}, {"name": "parcel", "in": "query", "description": "Parcel identifier to resolve.", "required": false, "schema": {"type": "string"}}], "responses": {"200": {"description": "Records returned successfully."}, "402": {"description": "Payment required; the challenge quotes the exact total."}}, "x-payment-info": {"offers": [{"amount": "5", "currency": "usd", "description": "Price per record; the runtime 402 challenge quotes the exact total and is authoritative. Paid in stablecoin (USDC) on Tempo; no minimum beyond the $0.01 floor, so this rail can pay every quote here.", "intent": "charge", "method": "tempo"}, {"amount": "5", "currency": "usd", "description": "Price per record; the runtime 402 challenge quotes the exact total and is authoritative. Paid by card via a Shared Payment Token. Cards carry a 0.50 minimum, so this rail is offered only on quotes of 0.50 or more.", "intent": "charge", "method": "stripe", "minimumAmount": "50"}], "price": {"mode": "dynamic", "currency": "USD", "min": "0.010000", "max": "0.050000"}, "protocols": [{"mpp": {"method": "tempo", "intent": "charge", "currency": "USDC.e"}}, {"mpp": {"method": "stripe", "intent": "charge", "currency": "usd"}}]}}}, "/parcel/owner": {"get": {"summary": "Search parcels by owner", "description": "Run a full-text owner-name search over indexed parcels. Instant for indexed jurisdictions (FL, NC, NY, IN); for full coverage beyond them use POST /parcel/jobs, which returns results within minutes.", "parameters": [{"name": "state", "in": "query", "description": "State parcel index to search.", "required": true, "schema": {"type": "string", "enum": ["FL", "NC", "NY", "IN"]}}, {"name": "name", "in": "query", "description": "Parcel owner name.", "required": true, "schema": {"type": "string"}}, {"name": "county", "in": "query", "description": "Optional county filter.", "required": false, "schema": {"type": "string"}}, {"name": "limit", "in": "query", "description": "Maximum parcels to return.", "required": false, "schema": {"type": "integer", "minimum": 1, "maximum": 500, "default": 100}}], "responses": {"200": {"description": "Records returned successfully."}, "402": {"description": "Payment required; the challenge quotes the exact total."}}, "x-payment-info": {"offers": [{"amount": "5", "currency": "usd", "description": "Price per record; the runtime 402 challenge quotes the exact total and is authoritative. Paid in stablecoin (USDC) on Tempo; no minimum beyond the $0.01 floor, so this rail can pay every quote here.", "intent": "charge", "method": "tempo"}, {"amount": "5", "currency": "usd", "description": "Price per record; the runtime 402 challenge quotes the exact total and is authoritative. Paid by card via a Shared Payment Token. Cards carry a 0.50 minimum, so this rail is offered only on quotes of 0.50 or more.", "intent": "charge", "method": "stripe", "minimumAmount": "50"}], "price": {"mode": "dynamic", "currency": "USD", "min": "0.010000", "max": "25.000000"}, "protocols": [{"mpp": {"method": "tempo", "intent": "charge", "currency": "USDC.e"}}, {"mpp": {"method": "stripe", "intent": "charge", "currency": "usd"}}]}}}, "/deed/search": {"get": {"summary": "Search West Virginia deed records", "description": "Search indexed West Virginia recorder and deed documents. Instant for indexed jurisdictions (WV); for full coverage beyond them use POST /deed/jobs, which returns results within minutes.", "parameters": [{"name": "name", "in": "query", "description": "Party name.", "required": true, "schema": {"type": "string"}}, {"name": "county", "in": "query", "description": "Optional West Virginia county filter.", "required": false, "schema": {"type": "string"}}, {"name": "from", "in": "query", "description": "Earliest recording date, inclusive.", "required": false, "schema": {"type": "string", "format": "date"}}, {"name": "through", "in": "query", "description": "Latest recording date, inclusive.", "required": false, "schema": {"type": "string", "format": "date"}}, {"name": "mode", "in": "query", "description": "Party-name matching mode.", "required": false, "schema": {"type": "string", "enum": ["prefix", "tokens"], "default": "prefix"}}, {"name": "limit", "in": "query", "description": "Maximum documents to return.", "required": false, "schema": {"type": "integer", "minimum": 1, "maximum": 500, "default": 100}}], "responses": {"200": {"description": "Records returned successfully."}, "402": {"description": "Payment required; the challenge quotes the exact total."}}, "x-payment-info": {"offers": [{"amount": "1", "currency": "usd", "description": "Price per record; the runtime 402 challenge quotes the exact total and is authoritative. Paid in stablecoin (USDC) on Tempo; no minimum beyond the $0.01 floor, so this rail can pay every quote here.", "intent": "charge", "method": "tempo"}, {"amount": "1", "currency": "usd", "description": "Price per record; the runtime 402 challenge quotes the exact total and is authoritative. Paid by card via a Shared Payment Token. Cards carry a 0.50 minimum, so this rail is offered only on quotes of 0.50 or more.", "intent": "charge", "method": "stripe", "minimumAmount": "50"}], "price": {"mode": "dynamic", "currency": "USD", "min": "0.010000", "max": "2.500000"}, "protocols": [{"mpp": {"method": "tempo", "intent": "charge", "currency": "USDC.e"}}, {"mpp": {"method": "stripe", "intent": "charge", "currency": "usd"}}]}}}, "/health": {"get": {"summary": "Check service liveness", "description": "Return the gateway liveness status.", "responses": {"200": {"description": "Successful response."}}, "security": []}}, "/openapi.json": {"get": {"summary": "Get the OpenAPI discovery document", "description": "Return this OpenAPI 3.1 discovery document.", "responses": {"200": {"description": "Successful response."}}, "security": []}}, "/llms.txt": {"get": {"summary": "Get agent-readable service discovery", "description": "Return a concise plain-text guide for agent consumers.", "responses": {"200": {"description": "Successful response."}}, "security": []}}, "/entity/jobs": {"post": {"summary": "Submit a full-coverage business entity lookup", "description": "Start a entity lookup with full coverage \u2014 every jurisdiction the underlying source supports, not only the indexed ones \u2014 and return a job id to poll. Submitting is free and results are ready within minutes. Provide `names` (company names). Optional: `states` (two-letter codes), `mode` (contains|exact|starts), `includeLiens`, `limit`. Send `X-Client-Id` to get your own concurrency allowance. Collect the records from GET /jobs/{job_id}/result, which is the only paid step.", "requestBody": {"required": true, "content": {"application/json": {"schema": {"type": "object", "description": "At least one lookup is required. At most 20 lookups per request, counted across every lookup array together.", "properties": {"names": {"type": "array", "description": "Company names to resolve.", "items": {"type": "string"}, "maxItems": 20}, "states": {"type": "array", "description": "Two-letter US state codes to scope the search. Extended-coverage states are searched only when named here and bill the extended per-record rate.", "items": {"type": "string"}}, "mode": {"type": "string", "enum": ["contains", "exact", "starts"], "default": "contains", "description": "Name matching mode."}, "includeLiens": {"type": "boolean", "description": "Attach the UCC/lien join where the state supports one."}, "limit": {"type": "integer", "minimum": 1, "description": "Maximum records per lookup. Clamped to the source's own ceiling.", "maximum": 200}}, "additionalProperties": false}}}}, "responses": {"200": {"description": "Successful response."}, "202": {"description": "Job accepted; poll the returned job id."}, "400": {"description": "Nothing to look up."}, "429": {"description": "Too many unfinished jobs for this client."}}, "security": []}}, "/parcel/jobs": {"post": {"summary": "Submit a full-coverage property records lookup", "description": "Start a parcel lookup with full coverage \u2014 every jurisdiction the underlying source supports, not only the indexed ones \u2014 and return a job id to poll. Submitting is free and results are ready within minutes. Provide at least one of `addresses`, `parcels` (STATE/County/ParcelID), or `owners` (STATE/Owner or STATE/County/Owner). Optional: `county`, `state`, `includeHistory`, `limit`, `ownerLimit`. Send `X-Client-Id` to get your own concurrency allowance. Collect the records from GET /jobs/{job_id}/result, which is the only paid step.", "requestBody": {"required": true, "content": {"application/json": {"schema": {"type": "object", "description": "At least one lookup is required. At most 1000 lookups per request, counted across every lookup array together.", "properties": {"addresses": {"type": "array", "description": "Full US property street addresses.", "items": {"type": "string"}, "maxItems": 1000}, "parcels": {"type": "array", "description": "Parcel or assessor account ids as STATE/County/ParcelOrAccount.", "items": {"type": "string"}, "maxItems": 1000}, "owners": {"type": "array", "description": "Owner names as STATE/Owner Name or STATE/County/Owner Name.", "items": {"type": "string"}, "maxItems": 1000}, "county": {"type": "string", "description": "County name, without the word 'County'."}, "state": {"type": "string", "description": "Two-letter US state code."}, "includeHistory": {"type": "boolean", "description": "Include tax and sale history where available."}, "limit": {"type": "integer", "minimum": 1, "description": "Maximum records per lookup. Clamped to the source's own ceiling.", "maximum": 1000}, "ownerLimit": {"type": "integer", "minimum": 1, "maximum": 500, "description": "Maximum parcels returned per owner lookup."}}, "additionalProperties": false}}}}, "responses": {"200": {"description": "Successful response."}, "202": {"description": "Job accepted; poll the returned job id."}, "400": {"description": "Nothing to look up."}, "429": {"description": "Too many unfinished jobs for this client."}}, "security": []}}, "/deed/jobs": {"post": {"summary": "Submit a full-coverage deed records lookup", "description": "Start a deed lookup with full coverage \u2014 every jurisdiction the underlying source supports, not only the indexed ones \u2014 and return a job id to poll. Submitting is free and results are ready within minutes. Provide at least one of `addresses`, `parcels`, or `parties` (STATE/Jurisdiction/Name). Optional: `docTypes`, `from`, `through`, `limit`. Send `X-Client-Id` to get your own concurrency allowance. Collect the records from GET /jobs/{job_id}/result, which is the only paid step.", "requestBody": {"required": true, "content": {"application/json": {"schema": {"type": "object", "description": "At least one lookup is required. At most 1000 lookups per request, counted across every lookup array together.", "properties": {"addresses": {"type": "array", "description": "Property street addresses to resolve to recorded documents.", "items": {"type": "string"}, "maxItems": 1000}, "parcels": {"type": "array", "description": "Parcel lookups as STATE/Jurisdiction/ParcelID.", "items": {"type": "string"}, "maxItems": 1000}, "parties": {"type": "array", "description": "Party lookups as STATE/Jurisdiction/Name or STATE/Name.", "items": {"type": "string"}, "maxItems": 1000}, "docTypes": {"type": "array", "description": "Restrict to these recorded document types.", "items": {"type": "string"}}, "from": {"type": "string", "format": "date", "description": "Earliest recording date, inclusive."}, "through": {"type": "string", "format": "date", "description": "Latest recording date, inclusive."}, "limit": {"type": "integer", "minimum": 1, "description": "Maximum records per lookup. Clamped to the source's own ceiling.", "maximum": 1000}}, "additionalProperties": false}}}}, "responses": {"200": {"description": "Successful response."}, "202": {"description": "Job accepted; poll the returned job id."}, "400": {"description": "Nothing to look up."}, "429": {"description": "Too many unfinished jobs for this client."}}, "security": []}}, "/jobs/{job_id}": {"get": {"summary": "Check a job", "description": "Report a job's status. Free, and never returns records. While the run is unfinished the response carries `poll_after_seconds`; once it succeeds it carries `record_count`, which is exactly what the result will be priced on.", "parameters": [{"name": "job_id", "in": "path", "description": "Job id returned by the matching POST /{service}/jobs call.", "required": true, "schema": {"type": "string"}}], "responses": {"200": {"description": "Job status."}, "404": {"description": "No such job, or it has expired."}}, "security": []}}, "/jobs/{job_id}/result": {"get": {"summary": "Collect a job's records", "description": "Return the records a finished run delivered, priced per record from the count the run actually produced. A run that failed or found nothing is collectable for free and is never challenged. Records are fetched before the payment settles, so a source failure costs nothing.", "parameters": [{"name": "job_id", "in": "path", "description": "Job id returned by the matching POST /{service}/jobs call.", "required": true, "schema": {"type": "string"}}, {"name": "Authorization", "in": "header", "description": "MPP payment credential answering this route's 402 challenge. Omit it to receive the challenge.", "required": false, "schema": {"type": "string"}}], "responses": {"200": {"description": "Records returned successfully."}, "402": {"description": "Payment required; the challenge quotes the exact total."}, "409": {"description": "The run has not finished; poll first."}, "404": {"description": "No such job, or it has expired."}}, "x-payment-info": {"offers": [{"amount": "1", "currency": "usd", "description": "Price per record; the runtime 402 challenge quotes the exact total and is authoritative. Paid in stablecoin (USDC) on Tempo; no minimum beyond the $0.01 floor, so this rail can pay every quote here.", "intent": "charge", "method": "tempo"}, {"amount": "1", "currency": "usd", "description": "Price per record; the runtime 402 challenge quotes the exact total and is authoritative. Paid by card via a Shared Payment Token. Cards carry a 0.50 minimum, so this rail is offered only on quotes of 0.50 or more.", "intent": "charge", "method": "stripe", "minimumAmount": "50"}], "price": {"mode": "dynamic", "currency": "USD", "min": "0.010000", "max": "5000.000000"}, "protocols": [{"mpp": {"method": "tempo", "intent": "charge", "currency": "USDC.e"}}, {"mpp": {"method": "stripe", "intent": "charge", "currency": "usd"}}]}}}}}