Dealer API

Check devices from your own till, stock system or website. Each check returns the same result and risk rating as a check from your dashboard, and is billed the same way.

The API at a glance

JSON in, JSON out

Base URLhttps://cdr-server.vercel.app/v1

Get a key

API access is part of every dealer plan: Verified Dealer, Dealer Pro and Dealer Enterprise.

  1. 1In your dealer dashboard, open API and make a key. On Dealer Enterprise, each key belongs to a branch.
  2. 2Copy it straight away: we show it once.
  3. 3Keep it on your server, never in an app or a web page. If it leaks, revoke it and make a new one.

Send it in the Authorization header of every request:

Header
Authorization: Bearer sk_live_…

Check a device

POST/v1/checks

Checks one device and bills it like a check from your dashboard.

Send the IMEI, or a serial number as query, as JSON with your key in the Authorization header.

Request
curl -X POST https://cdr-server.vercel.app/v1/checks \
  -H "Authorization: Bearer sk_live_…" \
  -H "Content-Type: application/json" \
  -d '{"imei": "354398601234891"}'

What to send

Field
imeistringThe IMEI to check.
querystringA serial number, in place of imei. Send one of the two, 4 to 40 characters long.

The answer

Answer200
{
  "id": "6720c1a3e4b0a1f2c3d4e5f6",
  "reference": "SPV-8B21D4E911C0A7F3B2D1",
  "checkedAt": "2026-09-28T07:42:10.000Z",
  "query": "354398601234891",
  "imeiValidity": "valid",
  "device": { "manufacturer": "Apple", "model": "iPhone 13 Pro" },
  "outcome": "CLEAN",
  "result": "No adverse record",
  "riskLevel": "low",
  "findings": [
    { "tone": "ok", "headline": "No lost or stolen report against this device", "detail": "…" }
  ],
  "billing": {
    "included": false,
    "charged": 20,
    "currency": "KES",
    "creditBalance": 380,
    "includedLeft": 0
  },
  "disclaimer": "…"
}
Field
idstringThe check's id.
referencestringSimuplace's reference for this check.
checkedAtstringWhen it ran, as an ISO 8601 time.
querystringThe number as checked.
imeiValiditystringvalid, invalid (the number was never issued), or not_applicable for serial numbers.
deviceobject | nullThe maker and model, when we can tell. Either can be null.
outcomestringWhat our records say. One of the outcomes below.
resultstringThe same, in words, as your dashboard shows it. An invalid IMEI reads “Invalid IMEI”.
riskLevelstring | nulllow, medium or high, with the reasons in findings.
findingsarrayWhat the rating rests on. Each has a tone (ok, info, warn or bad), a headline and a detail.
billingobjectWhether it came out of your included checks, what it cost, and what's left.
disclaimerstringWhat a result can and can't prove, to show with it.

Outcomes

outcome
CLEAN“No adverse record”Registered on Simuplace, with no lost or stolen report.
NOT_FOUND“Not registered”Not registered on Simuplace. We hold nothing against it.
LOST“Reported lost”Its owner reported it lost.
STOLEN“Reported stolen”Its owner reported it stolen.
DISPUTED“Ownership disputed”Reported stolen, and a claim over who owns it is open.
CAUTION“Disputed history”A past dispute over it was resolved; the history stands.

See your usage

GET/v1/usage

This month's included checks, paid checks and credit, for your own systems.

Request
curl https://cdr-server.vercel.app/v1/usage \
  -H "Authorization: Bearer sk_live_…"
Answer200
{
  "planActive": true,
  "paidThrough": "2026-10-28T07:00:00.000Z",
  "month": { "start": "2026-09-28T07:00:00.000Z", "end": "2026-10-28T07:00:00.000Z" },
  "plan": "verified",
  "includedChecks": 25,
  "includedUsed": 25,
  "paidChecks": 41,
  "nextCheckPrice": 20,
  "creditBalance": 380,
  "currency": "KES"
}
Field
planActivebooleanWhether your plan is paid up.
paidThroughstring | nullThe day your plan is paid up to.
monthobject | nullThis plan month's start and end. null while the plan isn't active.
planstringverified, pro or enterprise.
includedChecksnumberThe checks your plan includes each month.
includedUsednumberIncluded checks used this month, across the dashboard, bulk uploads and the API.
paidChecksnumberChecks paid from credit this month, one at a time from the dashboard or the API.
nextCheckPricenumberWhat the next paid check costs, in KES.
creditBalancenumberYour prepaid credit, in KES.
currencystringAlways KES.

A test key answers with zeros, and with paidThrough and month set to null.

Test keys

To build against the API safely, make a test key under API in your dealer dashboard, on any plan. It starts sk_test_ and works like a live key, but every check answers with a sample result in the same shape: no device is looked up, no record is read, and nothing is charged or counted. /v1/usage answers with zeros.

The IMEI's last digit picks the answer, so you can try each one:

Last digitTry
2NOT_FOUND354398601234842
3LOST354398601234883
4STOLEN354398601234834
5DISPUTED354398601234875
6CAUTION354398601234826
Any otherCLEAN354398601234891
Wrong check digitInvalid IMEI354398601234892

A sample's reference starts SPV-00000000, and its billing is all zeros. Revoke a test key like any other.

Billing

Each month of your plan includes 25 checks on Verified Dealer, or 150 on Dealer Pro, shared across the dashboard, bulk uploads and the API. After that, each API check comes out of your prepaid credit at the price for how many you've paid for this month:

Checks in a monthPer check
1 – 499KES 20
500 – 4,999KES 15
5,000 – 24,999KES 10
25,000 – 99,999KES 7
100,000+Let's talk

On Dealer Enterprise, your contract sets the included checks and the price of each check after them. Top up credit by M-Pesa from your dashboard. A check that can't be run isn't charged.

Limits

  • Up to 120 requests a minute per key, checks and usage together.
  • Each answer to a working key says what's left in its RateLimit header; resetis the seconds until the minute starts over. A request turned away for its key doesn't count.
  • Over the limit, you get 429 rate_limited with a Retry-After header: the seconds to wait before sending again.
  • One device per request: an imei, or a serial number as query, 4 to 40 characters long. For a whole file of devices, use Bulk uploads in your dealer dashboard.
Rate limit headers
RateLimit-Policy: 120;w=60
RateLimit: limit=120, remaining=97, reset=41

Errors

Every error is JSON: a message you can show people, and a content code your system can act on (null where there's no code).

Error402
{
  "message": "You've used this month's 25 included checks, and your credit is too low for another (KES 20). Top up to keep checking.",
  "content": "insufficient_credit"
}
  • 400
    No code
    The body has no imei (or query), or it's shorter than 4 or longer than 40 characters.
    invalid
    Insurer keys only: the number isn't a valid 15-digit IMEI. It isn't charged.
  • 401
    unauthorized
    No key, a wrong key, or a revoked key.
  • 402
    plan_inactive
    Your plan isn't paid up for this month.
    insufficient_credit
    The included checks are used and your credit is too low for the next one.
  • 403
    key_paused
    The key is for a branch that isn't on your plan now. Use another key.
    api_closed
    Insurer keys only: API access is off for your insurer.
  • 429
    rate_limited
    More than 120 requests in a minute on one key. Wait the seconds in Retry-After.
  • 500
    No code
    Something went wrong on our side. A check that can't be run isn't charged, so try again.

For insurers

Insurers use the same API. Your main contact makes keys under Device checks in your insurer portal, once Simuplace's team turns API access on. Each phone is billed at your price on your monthly invoice, not from credit.

Send IMEIs only: a number that isn't a valid IMEI comes back as 400 invalid and isn't charged. Each answer also carries the phone's record:

Answer200, insurer key
{
  …
  "record": {
    "registeredSince": "2026-07-01T09:12:00.000Z",
    "ownerChanges": 1,
    "earlierClaims": 0,
    "checkedByOthers": 2
  },
  "billing": { "charged": 20, "currency": "KES", "invoice": "2026-10" }
}
Field
registeredSincestring | nullWhen its owner got it, or null if it isn't registered on Simuplace.
ownerChangesnumberHow many times it changed hands on Simuplace.
earlierClaimsnumberClaims on it under any policy: how many, never whose.
checkedByOthersnumberChecks by anyone but you and its owner in the last 30 days. Never who.
billingobjectWhat it cost, and the month whose invoice it's on.

/v1/usage answers with this month so far. A key stops working with 403 api_closed while API access is off.

Usage200, insurer key
{
  "month": { "start": "2026-09-30T21:00:00.000Z", "end": "2026-10-31T21:00:00.000Z" },
  "checks": 1284,
  "amount": 25680,
  "price": 20,
  "currency": "KES",
  "invoiceOn": "2026-10-31T21:00:00.000Z"
}
Field
monthobjectThis month's start and end, in Nairobi time.
checksnumberPhones checked this month.
amountnumberWhat they cost so far, in KES.
pricenumberYour price for each phone, in KES.
invoiceOnstringWhen this month's invoice is made: the 1st of next month.

Results reflect Simuplace's records at the moment of the check. “No adverse record” means none was identified in the data sources checked; it is not a guarantee, and not proof of ownership.

Stuck? Ask us from Help in your dealer dashboard.