RentEngine Public API (1.8.0)
Integrate your systems anywhere with RentEngine.
Changelog: CHANGELOG.md. Compatibility: compatibility.md. Machine-readable overview: llms.txt.
This API uses JWT Bearer token authentication. Follow these steps to obtain and use your API tokens:
- Log in to your RentEngine developer portal
- In the top corner, click the "Create New API Key" button
- Provide a name for your token (e.g., "Integration Name")
- Optionally check Read-only if this key should only call GET/HEAD endpoints
- Click Create
- Copy the token to your clipboard and save it securely as it will not be displayed again
Important Security Notice: Your token will only be displayed ONCE at creation time. Make sure to copy it and store it securely. For security reasons, we cannot display the token again after this point.
Include your token in all API requests as a Bearer token in the Authorization header:
Authorization: Bearer your_token_here- Store tokens securely in environment variables or a secure vault
- Never hardcode tokens in your application code
- Do not share tokens in public repositories or client-side code
- Use separate tokens for different integrations or environments
Each token generated by a user inherits that user's account permissions. Actions are logged in the name of that user and token.
Keys created as read-only include a read_only JWT claim. They may call GET and HEAD. POST, PUT, PATCH, and DELETE on /api/public/v1 return 403 with code: "read_only_token". Existing keys without the claim keep full access. MCP hides and refuses mutating tools for these keys. Metered GET endpoints (market comps) still bill. Direct Supabase REST is not gated.
Because a token can span several accounts, most endpoints take an account_id. Call GET /accounts to list your accounts and look one up by name.
Tokens will not expire. (Well not for 100 years at least). If a token is compromised or no longer needed:
- Log in to your RentEngine developer portal
- Find the token you wish to invalidate in the table in the API Keys section
- Click the "Revoke Token" icon that looks like a trash can
Once revoked, a token cannot be restored. You'll need to create a new token if needed. Endpoints that require a token answer a revoked one with 401 and code: "api_key_revoked".
Most list endpoints in the RentEngine API support pagination to efficiently handle large datasets. Paginated endpoints accept the same query parameters, but there are two response formats in use. Check the endpoint's response schema to determine which format it returns. A few endpoints return a fixed, bounded result set instead of paging (e.g. GET /market-tool/comps returns up to 200 most recently active comparables) — these do not accept pagination parameters.
Pagination Parameters (all list endpoints)
limit- Controls how many items to return per page (default: 50, max: 100)page_number- Specifies which page to retrieve (0-indexed, default: 0)
Example request with pagination:
GET /api/public/v1/lockboxes?limit=25&page_number=1This would return the second page of results with 25 items per page.
Most list endpoints return a bare JSON array containing only the requested page of items. The JSON body is unchanged from earlier /v1 releases. Pagination metadata is in response headers (not the body):
| Header | Description |
|---|---|
X-Has-More | true if another page exists, false on the last page |
X-Next-Page-Number | The page_number to request next. Omitted when X-Has-More is false. |
Endpoints using this format include:
/lockboxes/lockbox_events/lockbox_installations/units/prospects/subteams/prescreening_templates
Example response:
[
{ "id": "...", "...": "..." },
{ "id": "...", "...": "..." }
]To iterate, follow X-Next-Page-Number until X-Has-More is false. The older "array shorter than limit" rule still works.
Newer list endpoints wrap the data in an envelope that includes explicit pagination metadata, so you don't have to infer the end of the dataset from the array length.
Endpoints using this format include:
/rental_application_groups
Example response:
{
"data": [
{ "id": "...", "...": "..." },
{ "id": "...", "...": "..." }
],
"page": {
"limit": 50,
"page_number": 0,
"has_more": true,
"next_page_number": 1
}
}Fields in the page object:
limit- Thelimitvalue used for this response (echoes the request)page_number- The 0-indexed page returned (echoes the request)has_more-trueif additional pages are available,falseon the final pagenext_page_number- The page number to request next, ornullwhenhas_moreisfalse
To iterate, follow next_page_number until it is null (or has_more is false).
Performance Considerations
- Use appropriate
limitvalues based on your needs. Smaller values reduce payload size but require more API calls, which can slow down performance and be subject to rate limits. - When filtering data, apply filters in the query parameters first to reduce the total number of items that need to be fetched.
To ensure fair usage and protect our infrastructure, all API endpoints are rate limited. Rate limits are applied per user (based on your API token).
Default Rate Limits
| Tier | Requests | Window |
|---|---|---|
| Standard | 30 | 5 seconds |
| Strict | 10 | 5 seconds |
| Market Tool | 40 | 24 hours |
Most endpoints use the "Standard" tier. Endpoints that return PII or stream binary artifacts — GET /rental_applications/{id} and GET /rental_applications/{id}/documents/{documentId} — use the "Strict" tier (2 req/s).
The market comps endpoint — GET /market-tool/comps — uses the "Market Tool" tier (40 requests per 24 hours) and is metered: each successful call incurs a $0.50 charge to your account. The strict daily limit and per-call charge exist to prevent abuse of this data-intensive endpoint.
Rate Limit Headers
All API responses include headers to help you track your rate limit status:
| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum requests allowed in the current window |
X-RateLimit-Remaining | Number of requests remaining in the current window |
X-RateLimit-Reset | Unix timestamp (milliseconds) when the rate limit resets |
Handling Rate Limits
When you exceed the rate limit, you'll receive a 429 Too Many Requests response with a Retry-After header indicating how many seconds to wait before retrying.
{
"error": "Too Many Requests",
"code": "rate_limited",
"message": "Rate limit exceeded. Please try again later.",
"retryAfter": 4
}Best Practices
- Monitor the
X-RateLimit-Remainingheader to avoid hitting limits - Implement exponential backoff when retrying after a 429 response
- Cache responses when possible to reduce API calls
- Use webhooks for real-time updates instead of polling
Bearer-authenticated /api/public/v1 error JSON includes a human error string. Mapped statuses (the table below) also include a stable machine-readable code. Branch on code when present, not on the message text. Extra fields (details, retryAfter, message) are unchanged. Unmapped statuses and non-JSON errors may omit code.
code | Typical status |
|---|---|
validation_error | 400 |
unauthorized | 401 |
api_key_revoked | 401 |
forbidden | 403 |
read_only_token | 403 |
not_found | 404 |
conflict | 409 |
unprocessable_entity | 422 |
rate_limited | 429 |
internal_error | 500 |
service_unavailable | 503 |
Unauthenticated POSTs (POST /showings/create) are unchanged.
Bearer-authenticated POST endpoints accept an optional Idempotency-Key header (printable ASCII, 1–256 characters, no spaces). Keys are scoped to the authenticated user, HTTP method, and path, and are remembered for 24 hours. Unauthenticated POSTs (POST /showings/create) do not honor the header.
- The first 2xx response for a given key is stored for 24 hours.
- A retry with the same key and the same body returns that stored response (no second write).
- The same key with a different body returns
422. - A concurrent retry while the first request is still running returns
409. 4xxand5xxare not stored, so a later retry with the same key is executed again.
If you send Idempotency-Key and the idempotency store is unavailable, the API returns 503 rather than risk a duplicate write.
Bearer-authenticated /api/public/v1 responses include X-Request-Id.
- If you send
X-Request-Id(printable ASCII, 1–128 characters, no spaces), we echo it. - If you send one that does not meet those constraints, we ignore it and return our own instead — read the value back off the response rather than assuming yours was echoed.
- If you send none, we return our internal request id used for tracing.
Use X-Request-Id to match your logs to the HTTP response, and quote it when contacting support — it is the id in RentEngine logs. It is not an idempotency key — use Idempotency-Key to prevent duplicate writes.
Additive changes stay on /api/public/v1. We will not wrap legacy list arrays in { data, page }. Breaking changes ship under a new path prefix. A breaking change to — or removal of — an existing production endpoint is announced at least 30 days in advance; nothing else carries an advance-notice commitment. Full policy: compatibility.md.
RentEngine provides webhooks to notify your systems about events in real-time. This allows you to build integrations that respond immediately to changes in your RentEngine data.
Setting Up Webhooks
Webhooks can be configured through the RentEngine developer portal. You'll need to specify:
- The target URL where webhook events should be sent
- The data you want to monitor (e.g., lockboxes, units, lockbox_events)
- The event types you want to receive (INSERT, UPDATE, DELETE)
- An optional API key that will be included in webhook requests to your endpoint
Webhook Payload Structure
Webhook payloads follow this general structure:
{
"type": "INSERT|UPDATE|DELETE",
"table": "table_name",
"record": { /* The current state of the record (null for DELETE) */ },
"old_record": { /* The previous state of the record (null for INSERT) */ }
}The specific fields in record and old_record will depend on the table that triggered the event.
Event Types
INSERT: Sent when a new record is createdUPDATE: Sent when an existing record is modifiedDELETE: Sent when a record is deleted
Security Considerations
- Webhook endpoints should be HTTPS to ensure secure transmission of data
- Validate the API key included in the webhook request to ensure it's coming from RentEngine
- Implement idempotency in your webhook handlers to prevent duplicate processing
Webhook Delivery
RentEngine uses a reliable delivery system (QStash) to ensure webhooks are delivered even during temporary outages. If your endpoint is unavailable, we'll retry delivery with exponential backoff.
Live availability and incident history for the API are published at status.rentengine.io. Subscribe there — by email, Slack, RSS, or webhook — to be notified when an incident is opened, updated, or resolved.
Check the status page first if you see sustained 5xx responses or timeouts. If no incident is open and the errors persist, email eng@rentengine.io with the failing endpoint and the approximate time of the failures in UTC.