CertRent CertRent Rent with confidence

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.