Understand LendFoundry API rate limits, response headers, and best practices for handling rate-limited requests
Rate Limiting
Fair Usage & System StabilityLendFoundry enforces rate limits on all API endpoints to ensure fair usage, system stability, and optimal performance for all customers.
Rate limiting protects the platform from abuse and ensures consistent performance for all API consumers.
Tenant-Specific LimitsRate limits may vary based on your subscription tier and specific tenant configuration. The values shown here are typical defaults. Contact your LendFoundry representative for your specific rate limits.
Rate Limit Headers
All API responses include rate limit information in the response headers:
| Header | Description | Example |
|---|---|---|
X-RateLimit-Limit | Maximum requests allowed in current window | 100 |
X-RateLimit-Remaining | Requests remaining in current window | 95 |
X-RateLimit-Reset | Unix timestamp when window resets | 1702648800 |
Retry-After | Seconds to wait before retrying (429 only) | 60 |
Example Response Headers
HTTP/1.1 200 OK
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1702648800
Content-Type: application/jsonDefault Rate Limits
Application Submission APIs (LOS)
| Limit Type | Default Value | Window | Notes |
|---|---|---|---|
| Standard | 100 requests | Per minute | Per API key |
| Burst | 20 requests | Per second | Short bursts allowed |
| Daily | 50,000 requests | Per day | Soft limit* |
Base URL: https://loc.demo.kendra.lendfoundry.com/v1/darbaan
Active Loan Servicing APIs (LMS)
| Limit Type | Default Value | Window | Notes |
|---|---|---|---|
| Standard | 200 requests | Per minute | Per client_id |
| Burst | 30 requests | Per second | Short bursts allowed |
| Daily | 100,000 requests | Per day | Soft limit* |
Base URL: https://api.demo.lms.lendfoundry.com/v1
Soft Limits*Daily limits are soft limits. You may receive warnings but requests won't be immediately blocked. Contact support if you consistently exceed daily limits.
Rate Limits by Endpoint Category
Some endpoints have stricter rate limits due to resource intensity:
High-Volume Endpoints (Stricter Limits)
| Endpoint Category | Default Limit | Window | Reason |
|---|---|---|---|
| Search/Filter | 50 requests | Per minute | Database-intensive |
| Bulk Operations | 20 requests | Per minute | High processing load |
| Report Generation | 10 requests | Per minute | CPU-intensive |
| Credit Bureau Pulls | 30 requests | Per hour | Third-party costs |
Standard Endpoints
| Endpoint Category | Default Limit | Window |
|---|---|---|
| CRUD Operations | 100 requests | Per minute |
| Read Operations | 200 requests | Per minute |
| Webhook Config | 10 requests | Per minute |
Rate Limit Exceeded Response
When you exceed rate limits, you'll receive a 429 Too Many Requests response:
Response Body
{
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded. Please retry after the specified time.",
"details": {
"limit": 100,
"remaining": 0,
"reset_at": "2025-12-15T10:31:00Z",
"retry_after": 60
},
"trace_id": "a1b2c3d4e5"
}
}Response Headers
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1702648860
Retry-After: 60
Content-Type: application/jsonHandling Rate Limits
Strategy 1: Respect Retry-After Header
Always check the Retry-After header before retrying:
# Check response headers and wait if rate limited
curl -I "https://api.demo.lms.lendfoundry.com/v1/loan-management/search/LN-2024-001/details" \
-H "Authorization: Bearer YOUR_TOKEN"const makeRequestWithRetry = async (url, options, maxRetries = 3) => {
for (let attempt = 0; attempt < maxRetries; attempt++) {
const response = await fetch(url, options);
if (response.status === 429) {
const retryAfter = parseInt(response.headers.get('Retry-After') || '60');
console.log(`Rate limited. Waiting ${retryAfter} seconds...`);
await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
continue;
}
return response;
}
throw new Error('Max retries exceeded');
};import time
import requests
def make_request_with_retry(url, headers, max_retries=3):
"""Make request with automatic retry on rate limit."""
for attempt in range(max_retries):
response = requests.get(url, headers=headers)
if response.status_code == 429:
retry_after = int(response.headers.get('Retry-After', 60))
print(f"Rate limited. Waiting {retry_after} seconds...")
time.sleep(retry_after)
continue
return response
raise Exception("Max retries exceeded")public HttpResponse<String> makeRequestWithRetry(String url, Map<String, String> headers, int maxRetries) throws Exception {
HttpClient client = HttpClient.newHttpClient();
for (int attempt = 0; attempt < maxRetries; attempt++) {
HttpRequest.Builder builder = HttpRequest.newBuilder()
.uri(URI.create(url))
.GET();
headers.forEach(builder::header);
HttpResponse<String> response = client.send(builder.build(),
HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 429) {
String retryAfter = response.headers().firstValue("Retry-After").orElse("60");
System.out.println("Rate limited. Waiting " + retryAfter + " seconds...");
Thread.sleep(Integer.parseInt(retryAfter) * 1000);
continue;
}
return response;
}
throw new Exception("Max retries exceeded");
}Strategy 2: Exponential Backoff
Implement exponential backoff for all retryable errors:
import time
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
def create_retry_session(retries=3, backoff_factor=1):
"""Create requests session with automatic retry."""
session = requests.Session()
retry = Retry(
total=retries,
read=retries,
connect=retries,
backoff_factor=backoff_factor,
status_forcelist=(429, 500, 502, 503, 504),
respect_retry_after_header=True
)
adapter = HTTPAdapter(max_retries=retry)
session.mount('http://', adapter)
session.mount('https://', adapter)
return session
# Usage
session = create_retry_session()
response = session.get(url, headers=headers)Strategy 3: Request Batching
Reduce request count by batching operations:
# ❌ Bad - 10 separate requests
for loan_id in loan_ids:
response = requests.get(f"/loans/{loan_id}", headers=headers)
# ✅ Good - 1 batch request (if available)
response = requests.post("/loans/batch",
json={"loan_ids": loan_ids},
headers=headers
)Strategy 4: Caching
Cache responses to avoid redundant requests:
from functools import lru_cache
import time
class CachedAPIClient:
"""API client with response caching."""
def __init__(self, base_url, headers, cache_ttl=300):
self.base_url = base_url
self.headers = headers
self.cache = {}
self.cache_ttl = cache_ttl
def get_loan(self, loan_number):
"""Get loan with caching."""
cache_key = f"loan_{loan_number}"
# Check cache
if cache_key in self.cache:
data, cached_time = self.cache[cache_key]
if time.time() - cached_time < self.cache_ttl:
return data
# Fetch from API
response = requests.get(
f"{self.base_url}/loan-management/search/{loan_number}/details",
headers=self.headers
)
response.raise_for_status()
data = response.json()
# Update cache
self.cache[cache_key] = (data, time.time())
return dataRate Limit Monitoring
Monitor Current Usage
def check_rate_limit_status(response):
"""Extract rate limit info from response headers."""
from datetime import datetime
limit = int(response.headers.get('X-RateLimit-Limit', 0))
remaining = int(response.headers.get('X-RateLimit-Remaining', 0))
reset_timestamp = int(response.headers.get('X-RateLimit-Reset', 0))
if limit > 0:
usage_percent = ((limit - remaining) / limit) * 100
reset_at = datetime.fromtimestamp(reset_timestamp)
return {
"limit": limit,
"remaining": remaining,
"reset_at": reset_at,
"usage_percent": usage_percent
}
return None
# Usage
response = requests.get(url, headers=headers)
status = check_rate_limit_status(response)
if status:
print(f"Rate limit: {status['usage_percent']:.1f}% used")
print(f"Remaining: {status['remaining']} requests")
print(f"Resets at: {status['reset_at']}")Alert on High Usage
class RateLimitMonitor:
"""Monitor rate limit usage and alert when high."""
def __init__(self, warning_threshold=0.8):
self.warning_threshold = warning_threshold
def check_response(self, response):
"""Check response and alert if approaching limit."""
status = check_rate_limit_status(response)
if status and status['usage_percent'] >= self.warning_threshold * 100:
self._send_alert(status)
return status
def _send_alert(self, status):
"""Send alert when approaching rate limit."""
print(f"⚠️ Rate limit warning: {status['usage_percent']:.1f}% used")
print(f" Only {status['remaining']} requests remaining")
# Add your alerting logic here (Slack, email, etc.)
# Usage
monitor = RateLimitMonitor(warning_threshold=0.8)
response = requests.get(url, headers=headers)
monitor.check_response(response)Best Practices
✅ DO
| Practice | Implementation |
|---|---|
| Monitor rate limit headers | Check X-RateLimit-Remaining after each request |
| Implement exponential backoff | Start with 1s, double on each retry |
Respect Retry-After header | Wait specified seconds before retry |
| Batch requests when possible | Use bulk endpoints to reduce request count |
| Cache responses | Set appropriate TTL for GET requests |
| Use webhooks | Subscribe to events instead of polling |
❌ DON'T
| Anti-Pattern | Risk |
|---|---|
| Ignore rate limits | Blocked requests, degraded service |
| Retry immediately | Wastes quota, extends block time |
| Poll for status | Exhausts rate limits quickly |
| Hardcode delays | Doesn't adapt to actual limits |
Requesting Higher Limits
When to Request
- Consistently hitting rate limits with legitimate traffic
- High-volume production use case
- Enterprise tier customer
How to Request
-
Contact Support
- Email: [email protected]
- Subject: "Rate Limit Increase Request"
-
Provide Information
- Current usage patterns
- Expected request volume
- Use case description
- Business justification
-
Review Process
- Support reviews request
- May require tier upgrade
- Limits adjusted within 2-3 business days
FAQ
Q: Do rate limits reset at midnight?
A: No. Rate limits use sliding windows or fixed windows based on limit type. Check the X-RateLimit-Reset header for exact reset time.
Q: Are rate limits per API key or per organization?
A: Rate limits are per API key (LOS) or per client_id (LMS). Multiple credentials = multiple rate limit buckets.
Q: What happens if I exceed daily limits?
A: Daily limits are soft limits. You may receive warnings but requests won't be immediately blocked. Contact support if consistently exceeding.
Q: Do rate limits apply to webhooks?
A: No. Webhooks are outbound from LendFoundry to your server. Rate limits only apply to inbound API requests.
Q: Can I get higher rate limits?
A: Yes. Contact support with your use case. Higher limits may require enterprise tier subscription.
Support
Questions about rate limits?
- Email: [email protected]
- Subject: "Rate Limit Question - [Your Company]"
Properly Handling Rate Limits?Head to the Error Codes reference to understand all possible error responses.
