The statewide rules behind LeaseHelper's lease generator and free tools, as read-only JSON and as an MCP server. It covers all 50 states and DC: security deposit caps, return deadlines, interest and penalties; late-fee caps and grace periods; landlord entry notice; eviction and month-to-month termination notice; rent increase notice and caps; and lease clauses that are void by statute. Every record carries its statute citation, a source URL and the date it was last verified.
Statewide rules only. City and county ordinances (rent control, just cause, local deposit rules) can be stricter and are flagged with local_ordinances_may_apply, not encoded. Informational data, not legal advice.
Base URL https://leaseagreementgenerator.com/api/v1. Send your key in a header. Keys in the query string are refused.
curl https://leaseagreementgenerator.com/api/v1/states/CA?topic=security_deposit \
-H "Authorization: Bearer lhk_your_key"
| Request | Returns | Key |
|---|---|---|
GET /api/v1/states | The 51 jurisdictions, with topics covered, record count, latest verification date, whether local ordinances may apply, and enacted law changes not yet in force | Yes |
GET /api/v1/states/{state} | Every rule record for one jurisdiction. {state} is CA, US-CA, California or new-york. Optional ?topic=late_fee,entry_notice | Yes |
GET /api/v1/rules?topic=… | One topic across jurisdictions. Optional rule=, state=CA,NY, q= (keywords), limit= (max 500), offset= | Yes |
GET /api/v1/topics | Topics and the rules in each | No |
GET /api/v1/plans | Plans and limits | No |
| topic | rules |
|---|---|
security_deposit | cap, return_deadline, interest, penalty |
late_fee | cap (cap, grace period, rule) |
entry_notice | notice |
eviction_notice | nonpayment, violation_cure, mtm_termination |
rent_increase | notice, cap |
prohibited_clauses | one rule per clause type the state's statute voids |
Every record has the same shape whatever the topic. It is built to extend to other legal domains, with jurisdiction, topic, rule, citation and verified_at.
{
"id": "us-ca/landlord_tenant/security_deposit/return_deadline",
"domain": "landlord_tenant",
"jurisdiction": { "code": "US-CA", "name": "California", "level": "state", "country": "US" },
"topic": "security_deposit",
"rule": "return_deadline",
"title": "Deposit return deadline",
"value": { "days": 21, "days_text": "21", "note": null, "return_method": "…" },
"text": "Return within 21 days",
"citation": "Cal. Civ. Code §1950.5(g)(1)",
"source_url": "https://leaseagreementgenerator.com/blog/security-deposit-laws-by-state",
"source_type": "leasehelper_reference",
"reference_url": "https://leaseagreementgenerator.com/blog/security-deposit-laws-by-state",
"quote": "No later than 21 calendar days after the tenant has vacated the premises …",
"verified_at": "2026-08-12",
"status": "statute_read",
"disclaimer": "Informational data, not legal advice. …"
}
source_url is the official statute text where we hold its address (source_type: "official_statute_text"). Otherwise it is our cited reference page.status is statute_read (read against the current official text on verified_at) or source_verified (imported from source-verified data with no dated read on that field). A field we have not verified is published as unverified with value: null, never as a figure.value field we cannot reduce to a number (for example "14 business days") is kept as text beside a numeric field that is null.{ "data": [...], "meta": {...} }. meta carries the dataset version, your plan, the attribution line and the disclaimer.Each data request counts as one call, whatever the number of records. Refused requests are not counted. Every keyed response carries X-Quota-Limit, X-Quota-Remaining, X-Quota-Reset (UTC calendar month), X-RateLimit-Limit and X-RateLimit-Remaining (per minute).
| Status | error.code |
|---|---|
| 400 | topic_required, unknown_topic, unknown_state, bad_paging (not counted) |
| 401 | missing_key, invalid_key |
| 403 | revoked |
| 429 | rate_limited (with Retry-After), quota_exceeded |
| 503 | not_configured, store_error: retry |
# Deposit return deadlines in three states
curl "https://leaseagreementgenerator.com/api/v1/rules?topic=security_deposit&rule=return_deadline&state=CA,NY,TX" \
-H "Authorization: Bearer $LH_KEY"
# Late-fee rules everywhere, 100 at a time
curl "https://leaseagreementgenerator.com/api/v1/rules?topic=late_fee&limit=100" -H "Authorization: Bearer $LH_KEY"
// JavaScript
const r = await fetch("https://leaseagreementgenerator.com/api/v1/states/TX?topic=late_fee", {
headers: { Authorization: "Bearer " + process.env.LH_KEY }
});
const { data, meta } = await r.json();
console.log(data[0].text, "—", data[0].citation, "(verified", data[0].verified_at + ")");
The same data, the same keys, over the Model Context Protocol (Streamable HTTP, stateless, JSON responses). Endpoint https://leaseagreementgenerator.com/api/mcp. Discovery (initialize, tools/list) needs no key. Each tools/call is one call against your quota.
| Tool | Arguments |
|---|---|
get_state_rules | state, optional topics[] |
compare_states | states[] (2–51), topic, optional rule |
search_rules | query, optional topic, states[], limit |
All three are read-only. Results come back as structuredContent and as JSON text, with citation, source URL, verified date and disclaimer on every record. Example client config:
{
"mcpServers": {
"leasehelper-rules": {
"type": "http",
"url": "https://leaseagreementgenerator.com/api/mcp",
"headers": { "Authorization": "Bearer lhk_your_key" }
}
}
}
curl https://leaseagreementgenerator.com/api/mcp \
-H "Authorization: Bearer $LH_KEY" -H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"compare_states","arguments":{"states":["CA","NY"],"topic":"late_fee"}}}'
Paid plans are in early access. The buttons open an email to info@leaseagreementgenerator.com and we reply within one business day (Monday–Friday). Subscriptions are monthly, cancel any time, and a cancelled key keeps working at free-tier limits. Bulk export or an annual license? Email us.
We use your email only to contact you about your key (service changes, quota, security). The key is shown once. We store only a hash of it, so we cannot show it again. Lost keys are replaced, not recovered.