CertRent Open API
Verified renters, on demand.
Enrol a renter, ask for their consent, and read bank-verified income, rent history, identity and ownership-checked landlord references — through one REST API. Consent receipts and per-market screening rules come with every response.
Sandbox keys are issued instantly and are free forever. Live keys need a short review.
Verify once, apply anywhere
Renters you enrol build one profile they keep. If they were already verified for another property, your request can be approved in seconds — no re-screening, no re-paying.
Renter-permissioned by design
Nothing verified is readable without a scoped, time-boxed consent grant from the renter. Every grant mints a consent receipt both sides can audit.
Compliance in the wire
Ask any endpoint which rules bind in NYC or Los Angeles — fee caps, source-of-income protection, fair-chance timing, adverse-action duties — and file your adverse actions through the API.
Quickstart
From zero to a screening decision
In sandbox, enrolling a renter instantly creates a synthetic, fully-verified one — so you can build the whole integration before a single real applicant touches it.
1. Enrol a renter (free)
POST /renters
curl -X POST https://certrent.com/api/v1/renters \
-H "Authorization: Bearer ck_test_…" \
-H "Content-Type: application/json" \
-d '{
"email": "applicant@example.com",
"name": "Dana Ruiz",
"property": "120 W 45th St",
"unit": "7B",
"market": "nyc",
"external_id": "your-crm-id-4471"
}'
$res = Http::withToken('ck_test_…')
->post('https://certrent.com/api/v1/renters', [
'email' => 'applicant@example.com',
'name' => 'Dana Ruiz',
'property' => '120 W 45th St',
'unit' => '7B',
'market' => 'nyc',
'external_id' => 'your-crm-id-4471',
])->json();
$renterId = $res['id'];
const res = await fetch('https://certrent.com/api/v1/renters', {
method: 'POST',
headers: {
Authorization: 'Bearer ck_test_…',
'Content-Type': 'application/json',
},
body: JSON.stringify({
email: 'applicant@example.com',
name: 'Dana Ruiz',
property: '120 W 45th St',
unit: '7B',
market: 'nyc',
external_id: 'your-crm-id-4471',
}),
});
const renter = await res.json();
import requests
renter = requests.post(
'https://certrent.com/api/v1/renters',
headers={'Authorization': 'Bearer ck_test_…'},
json={
'email': 'applicant@example.com',
'name': 'Dana Ruiz',
'property': '120 W 45th St',
'unit': '7B',
'market': 'nyc',
'external_id': 'your-crm-id-4471',
},
).json()
2. Ask the renter for consent
POST /renters/{id}/access-requests
curl -X POST https://certrent.com/api/v1/renters/42/access-requests \
-H "Authorization: Bearer ck_test_…" \
-H "Content-Type: application/json" \
-d '{
"scopes": ["read:identity","read:income","read:rent_history","read:decision"],
"purpose": "Tenant screening for 120 W 45th St, Unit 7B",
"expires_in_days": 30
}'
Http::withToken('ck_test_…')->post("https://certrent.com/api/v1/renters/{$renterId}/access-requests", [
'scopes' => ['read:identity', 'read:income', 'read:rent_history', 'read:decision'],
'purpose' => 'Tenant screening for 120 W 45th St, Unit 7B',
'expires_in_days' => 30,
]);
await fetch(`https://certrent.com/api/v1/renters/${renter.id}/access-requests`, {
method: 'POST',
headers: { Authorization: 'Bearer ck_test_…', 'Content-Type': 'application/json' },
body: JSON.stringify({
scopes: ['read:identity', 'read:income', 'read:rent_history', 'read:decision'],
purpose: 'Tenant screening for 120 W 45th St, Unit 7B',
expires_in_days: 30,
}),
});
requests.post(
f"https://certrent.com/api/v1/renters/{renter['id']}/access-requests",
headers={'Authorization': 'Bearer ck_test_…'},
json={
'scopes': ['read:identity', 'read:income', 'read:rent_history', 'read:decision'],
'purpose': 'Tenant screening for 120 W 45th St, Unit 7B',
'expires_in_days': 30,
},
)
3. Read the decision
GET /renters/{id}/decision?rent=3200
curl "https://certrent.com/api/v1/renters/42/decision?rent=3200" \
-H "Authorization: Bearer ck_test_…"
$decision = Http::withToken('ck_test_…')
->get("https://certrent.com/api/v1/renters/{$renterId}/decision", ['rent' => 3200])
->json();
const decision = await (await fetch(
`https://certrent.com/api/v1/renters/${renter.id}/decision?rent=3200`,
{ headers: { Authorization: 'Bearer ck_test_…' } },
)).json();
decision = requests.get(
f"https://certrent.com/api/v1/renters/{renter['id']}/decision",
headers={'Authorization': 'Bearer ck_test_…'},
params={'rent': 3200},
).json()
The decision object
Verified facts and an affordability ratio — never a score, never an approve/deny. The decision stays yours.
{
"profile_id": 318,
"completeness": 100,
"rentready": true,
"verified": { "identity": true, "income": true, "rent_history": true, "references": true },
"income": { "monthly_income_verified": 9400.0, "method": "bank_connected" },
"affordability": {
"status": "meets_standard",
"rent": 3200.0,
"income_to_rent_ratio": 2.94,
"rent_to_income_percent": 34.0,
"max_affordable_rent": 3133.33
},
"rent_history": { "months_observed": 12, "on_time_payments": 12, "observed_rent": 2820.0 },
"references": { "total": 2, "ownership_verified": 2 },
"confidence": { "level": "high", "income_method": "bank_connected" },
"signals": [
{ "code": "income_bank_connected", "severity": "info",
"message": "Income was read directly from the renter's bank …" }
],
"consent": { "grant_id": 77, "expires_at": "2026-08-25T14:02:11+00:00" }
}
Reference
Endpoints
Base URL https://certrent.com/api/v1 · Bearer authentication
| Method | Path | Key scope | What it does |
|---|---|---|---|
| GET | /me | — | Your client, key scopes, pricing and current usage. |
| GET | /usage?period=YYYY-MM | — | Metered usage for a billing period. |
| GET | /compliance?market=nyc|la | — | Screening rules and obligations that bind in that market. |
| POST | /renters | renters:write | Enrol a renter and invite them. Free. |
| GET | /renters | renters:read | List the renters you enrolled. Filter by status, email or external_id. |
| GET | /renters/{id} | renters:read | One renter link and its status. |
| POST | /renters/{id}/reinvite | renters:write | Re-send the invite email. |
| DELETE | /renters/{id} | renters:write | Remove your link. Never deletes the renter's account. |
| POST | /renters/{id}/access-requests | renters:write | Ask the renter to consent to specific scopes. |
| GET | /access-grants/{id} | renters:read | Consent status, scopes and expiry. |
| GET | /access-grants/{id}/receipt | renters:read | The machine-readable consent receipt. |
| DELETE | /access-grants/{id} | renters:write | Release access you no longer need. |
| GET | /renters/{id}/decision | renters:read | The screening decision object. Metered on live. |
| GET | /renters/{id}/profile | renters:read | The full consented profile. Metered on live. |
| GET/POST | /webhooks | renters:read / webhooks:write | List or create an endpoint. |
| PATCH/DELETE | /webhooks/{id} | webhooks:write | Update or remove an endpoint. |
| POST | /webhooks/{id}/test | webhooks:write | Send a signed test event. |
| GET | /webhooks/{id}/deliveries | renters:read | Inspect what we sent and how it went. |
| POST | /adverse-actions | renters:write | File the record of a denial or conditional offer. |
| GET | /adverse-actions | renters:read | Your adverse-action history. |
Read and write access are approved separately. A key with only renters:read can screen but never enrol.
Consent scopes
The renter grants these — not us. Anything outside the grant is simply absent from the response.
- read:identity
- Verified identity (name, verification status — never your ID document)
- read:income
- Bank-verified income and affordability
- read:rent_history
- Rent payment history
- read:references
- Ownership-verified landlord references
- read:decision
- The overall RentReady decision summary
Webhooks
Every delivery carries a CertRent-Signature header: t={timestamp},v1={HMAC-SHA256 of "{timestamp}.{raw body}" using your endpoint secret}. Verify it before trusting a payload. Failures retry with backoff for up to six attempts.
- renter.invited
- A renter was enrolled and invited to verify.
- renter.registered
- An invited renter created their CertRent account.
- renter.verified
- A renter completed a verification track (or all of them).
- renter.income_updated
- A renter's bank-connected income changed.
- grant.created
- A renter consented to share their profile with you.
- grant.revoked
- A renter revoked a previously granted consent.
- rent_report.updated
- A tenant's rent-reporting status changed.
Two environments
ck_test_…— Sandbox. Issued instantly, free forever. Enrolling creates a synthetic verified renter and consent is granted automatically, so you can build the whole flow end-to-end. Never a real person.ck_live_…— Live. Issued after a short review of your company and use case. Real renters, real consent, metered.
Sandbox and live data never mix: a key only ever sees its own environment.
Pricing
You pay for one thing: reading a verified profile.
Talk to us
- ✓ Enrolling and inviting renters — free.
- ✓ Consent requests, webhooks, compliance lookups — free.
- ✓ Charged once per renter per calendar month, no matter how many times you read them.
- ✓ Sandbox — free forever.
Frequently asked questions
Is there an API for tenant screening?
Yes. The CertRent Open API lets a property-management company screen applicants from inside its own leasing software, over a standard REST interface with a secret key. Sandbox keys are issued instantly and are free; live access is granted after a short review.
How do I verify an applicant's income through an API?
CertRent reads income from the renter's own bank connection rather than from uploaded documents, so there is no pay stub to forge. Your integration enrols the applicant, the applicant consents, and you receive verified monthly income together with an affordability ratio against the rent you are screening for.
Does the renter have to agree before I can see their profile?
Yes, always. Nothing verified is readable until that renter approves your specific request, choosing which categories you may see and for how long. Each approval produces a consent receipt, and the renter can revoke access at any time.
What does the tenant screening API cost?
Enrolling renters, inviting them, receiving webhooks and checking compliance rules are all free. You are charged only when you read a verified profile, once per applicant per calendar month. The sandbox is free forever.
Can one verified profile be reused across landlords?
That is the point of the network. A renter builds one verified profile and reuses it for every application, so an applicant already verified for another property can approve your request in seconds instead of being re-screened and re-charged.
Ready to build?
Register, copy your sandbox key, and make your first call in a few minutes.