This page is a work in progress. It's only available in English for now. We apologize for the inconvenience and are working on making it available in French soon.
Quickstart
Get your first customer summary in a few minutes.
1. Sign up for Kiipr App
If you don't have an account yet, create your business account for Kiipr App first.
2. Create an API key
Log in to Kiipr, open API keys in the menu and create a key. You get a client ID (kpr_ci_…) and a client secret (kpr_cs_…). Copy the secret right away: it's shown only once.
3. Get an access token
Exchange your client ID and secret for an access token, valid for 1 hour.
curl -X POST https://api.kiipr.com/oauth/token \
-u "kpr_ci_your_client_id:kpr_cs_your_client_secret" \
-d grant_type=client_credentials
{
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 3600
}
3. Look up a customer
curl -X POST https://api.kiipr.com/api/v1/customer-summary \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 6f1c2a9e-8d4b-4f7a-9c3e-2b5d7e9f1a2c" \
-d '{
"firstName": "Jane",
"lastName": "Doe",
"dateOfBirth": "1990-01-01",
"email": "jane@example.com",
"postalCode": "H2X 1Y4"
}'
You get the customer's summary back in JSON. See the full response below.
Authentication
The Kiipr API uses OAuth 2.0 with the client credentials grant.
- Send your client ID and secret to
POST /oauth/tokento get an access token. - Send the access token in the
Authorizationheader of each API call:
Authorization: Bearer YOUR_ACCESS_TOKEN
Tokens last 1 hour (expires_in: 3600). Reuse a token for every call until it expires, then request a new one. There's no need to fetch a token per call.
Treat your client secret like a password: keep it on your server, never in a browser or mobile app. You can have up to 5 active API keys and revoke any of them at any time from API keys. Revoking a key, or closing your account, stops its tokens on the very next call.
Pricing & limits
Each endpoint has a flat price in tokens per call, listed with the endpoint below. Tokens are taken from your account balance. You can buy tokens in Kiipr and activate auto-renewal.
| Rate limit | 5 calls per second per account, bursts of 20 |
|---|---|
| Access token lifetime | 1 hour |
| Active API keys | 5 |
When you're charged
- A successful call is charged its endpoint's price, even when no matching customer is found.
- Invalid requests (
400) are free. So are rejected calls (401,402,429). - A call that fails on our side (
500) is refunded automatically. - Retries are free: send the same
Idempotency-Keyand the same body to the same endpoint within an hour and you're only charged once.
The rate limit is shared by all the API keys of your account.
Endpoints
Base URL: https://api.kiipr.com
POST /oauth/token
Exchange your API key for an access token.
Request — application/x-www-form-urlencoded
Send your client ID and secret one of two ways, not both:
- HTTP Basic auth (recommended):
Authorization: Basic base64(client_id:client_secret). Most HTTP clients do this for you, e.g.curl -u. - Form fields:
client_idandclient_secretin the body.
| Field | Description | |
|---|---|---|
grant_type | required | Always client_credentials. |
client_id | required without Basic auth | Your client ID. |
client_secret | required without Basic auth | Your client secret. |
Response — 200
| Field | Description |
|---|---|
access_token | The token to send as Authorization: Bearer …. |
token_type | Always Bearer. |
expires_in | Seconds until the token expires (3600). |
POST /api/v1/customer-summary
Price: 10 tokens per call
Returns a summary of a person's history across the Kiipr network. Kiipr matches the person from the identity details you send. The more details you send, the better the match. When no one matches, the call is still charged and returns an empty summary.
Headers
| Header | Description | |
|---|---|---|
Authorization | required | Bearer YOUR_ACCESS_TOKEN |
Content-Type | required | application/json |
Idempotency-Key | optional | Up to 255 characters, for example a UUID per lookup. Retrying with the same key and body within an hour isn't charged again. |
Request body — application/json
| Field | Description | |
|---|---|---|
firstName | required | |
lastName | required | |
dateOfBirth | required | YYYY-MM-DD |
socialInsuranceNumber | optional | 9 digits |
email | optional | |
primaryPhoneNumber | optional | 10 digits |
postalCode | optional | Canadian postal code, e.g. H2X 1Y4 |
Response — 200
{
"summary": {
"active_requests": 1,
"active_loans": 1,
"total_approved_requests": 4,
"total_refused_requests": 2,
"requests_last_7_days": 1,
"requests_last_30_days": 2,
"requests_last_90_days": 3,
"unique_lenders_contacted_last_30_days": 2,
"total_loans": 4,
"total_active_loans": 1,
"total_suspended_loans": 0,
"total_loans_in_collection": 0,
"total_loans_in_loss": 0,
"total_completed_loans": 3,
"completed_without_incident_count": 2,
"completed_with_incident_count": 1,
"loan_average_amount": 850,
"total_payments": 42,
"successful_payments": 38,
"total_payment_errors": 3,
"total_delayed_payments": 1,
"payment_error_ratio": 0.071,
"delayed_payment_ratio": 0.024,
"has_payment_agreement": false,
"blacklisted_places_count": 0,
"has_bankruptcy_filing": false,
"has_consumer_proposal": false,
"last_bankruptcy_filing_date": null,
"last_consumer_proposal_date": null
},
"contact": {
"last_known_address": {
"line1": "123 Main St",
"city": "Montréal",
"province": "QC",
"country": "Canada",
"postal_code": "H2X 1Y4"
},
"last_phone_number": "5145551234",
"last_known_employer": {
"name": "Acme Inc.",
"address": null
}
},
"requests": {
"last_3_requests": [
{
"status": "evaluation",
"requested_amount": 1000,
"requested_at": "2026-10-28",
"refusal_reason_category": null
},
{
"status": "refused",
"requested_amount": 1500,
"requested_at": "2026-10-12",
"refusal_reason_category": "insufficient-income"
},
{
"status": "completed",
"requested_amount": 750,
"requested_at": "2026-06-03",
"refusal_reason_category": null
}
]
}
}
When no one matches, you get the same shape with zeros, nulls and an empty last_3_requests.
summary
| Field | Description |
|---|---|
active_requests | Requests still in progress (not completed, refused or cancelled). |
total_approved_requests / total_refused_requests | Requests approved or refused, all time. |
requests_last_7_days / _30_days / _90_days | Requests made in the last 7, 30 and 90 days. |
unique_lenders_contacted_last_30_days | Number of different lenders the person applied to in the last 30 days. |
total_loans | Loans granted, all time. |
total_active_loans (also active_loans) / total_suspended_loans / total_loans_in_collection / total_loans_in_loss / total_completed_loans | Loans by current status. |
completed_without_incident_count / completed_with_incident_count | Completed loans without, or with, payment errors. |
loan_average_amount | Average loan amount, in dollars. |
total_payments | Payments made, failed or late, all loans together. |
successful_payments / total_payment_errors / total_delayed_payments | Payments made, failed and late. |
payment_error_ratio / delayed_payment_ratio | Failed and late payments as a share of total_payments (0 to 1). |
has_payment_agreement | Whether the person has a payment agreement on any loan. |
blacklisted_places_count | Number of lenders that blacklisted the person. |
has_bankruptcy_filing / last_bankruptcy_filing_date | Bankruptcy on file, and its most recent date (YYYY-MM-DD or null). |
has_consumer_proposal / last_consumer_proposal_date | Consumer proposal on file, and its most recent date (YYYY-MM-DD or null). |
contact
| Field | Description |
|---|---|
last_known_address | Most recent address on file: line1, city, province, country, postal_code. |
last_phone_number | Most recent phone number on file. |
last_known_employer | Most recent employer: name. address is always null for now. |
requests
| Field | Description |
|---|---|
last_3_requests | The 3 most recent requests, newest first: status, requested_amount, requested_at (YYYY-MM-DD) and, for refused requests, refusal_reason_category. |
Errors
Errors return JSON with a code for your code and a message for people. Validation errors also list the invalid fields.
{
"code": "E_VALIDATION_FAILED",
"message": "E_VALIDATION_FAILED: Validation failed",
"fields": [
{ "field": "dateOfBirth", "validation": "required", "message": "..." }
]
}
Customer summary
| Status | code | Meaning | Charged |
|---|---|---|---|
| 400 | E_BAD_REQUEST | The body isn't valid JSON, or Idempotency-Key is over 255 characters. | No |
| 400 | E_VALIDATION_FAILED | A field is missing or invalid. fields says which. | No |
| 401 | — | The access token is missing, invalid or expired, its API key was revoked, or the account is closed. Get a new token. | No |
| 402 | E_INSUFFICIENT_TOKENS | Your account doesn't have enough tokens. Buy tokens in Kiipr, then retry. | No |
| 429 | — | Over 5 calls per second. Wait Retry-After seconds (1), then retry. | No |
| 500 | E_INTERNAL | Something failed on our side. Safe to retry with the same Idempotency-Key. | Refunded |
Access token
The token endpoint follows the OAuth 2.0 standard: errors carry an error field.
| Status | error | Meaning |
|---|---|---|
| 400 | invalid_request | grant_type is missing, or credentials were sent both in Basic auth and in the form. |
| 400 | unsupported_grant_type | grant_type isn't client_credentials. |
| 401 | invalid_client | Unknown or revoked client ID, wrong secret, or closed account. |
| 429 | — | Too many token requests. Reuse your token until it expires. |