Programmatic access to verified KRA eTIMS invoice data — including the Control Unit (CU) number. Free, no authentication, no rate limits.
The KRA Invoice Verifier API lets third‑party software retrieve the verified Control Unit (CU) invoice number and other invoice details for a KRA eTIMS invoice, given the invoice’s public URL.
You send a URL; the API fetches the KRA invoice page, extracts the invoice fields, and returns them — including the CU number — as JSON. Results are cached server‑side so repeat lookups are fast.
| Item | Value |
|---|---|
| Base URL | https://www.aisheild.tech |
| Endpoint | POST /api/fetch-url/ |
| Full URL | https://www.aisheild.tech/api/fetch-url/ |
| Bypass cache | https://www.aisheild.tech/api/fetch-url/?refresh=1 |
| Method | POST only (others → HTTP 405) |
| Authentication | None required |
| CSRF | Not required |
| Rate limiting | None enforced (cache on your side) |
Only one header is required:
Content-Type: application/json X-CSRFToken is accepted but not needed.
{
"url": "https://itax.kra.go.ke/KRA-Portal/invoiceChk.htm?actionCode=loadPage&invoiceNo=123456789"
}
| Field | Type | Required | Notes |
|---|---|---|---|
url |
string | yes | Public URL of the KRA invoice. https:// is added automatically if missing. Must contain a dot in the hostname. |
Requests with a missing or malformed URL are rejected with HTTP 400.
& (as most KRA invoice URLs do),
you must escape it as ^&. See Windows Notes.
HTTP 200
Real, verified working example:
{
"success": true,
"url": "https://itax.kra.go.ke/KRA-Portal/invoiceChk.htm?actionCode=loadPage&invoiceNo=123456789",
"cached": true,
"cu_number": "123456789",
"data": [
{ "label": "Control Unit Invoice Number", "value": "123456789" },
{ "label": "Trader System Invoice No", "value": "5128" },
{ "label": "Invoice Date", "value": "02/10/2026" },
{ "label": "Total Taxable Amount", "value": "7413.79" },
{ "label": "Supplier Name", "value": "123456789 ENTERPRISES" }
]
}
| Field | Type | Description |
|---|---|---|
success | bool | true when invoice data was retrieved |
url | string | The normalized URL that was queried |
cached | bool | true = served from cache; false = freshly fetched from KRA |
cu_number | string | Verified Control Unit Invoice Number ("" if not present) |
data | array | All extracted invoice fields as {label, value} objects |
The API attempts to extract 7 fields, but a KRA invoice may legitimately contain fewer — some invoice types omit certain fields. Observed in production:
The cu_number shortcut is always present (empty string if the invoice genuinely has no CU number).
Integrators who only need the CU number should read cu_number directly rather than searching data.
Every error has the same shape:
{ "success": false, "url": "...", "error": "..." }
| Status | Meaning | What to do |
|---|---|---|
| 400 | Invalid/missing JSON body; missing URL; URL fails validation | Fix the request — URL must be non-empty and contain a dot in hostname |
| 404 | Page loaded but no KRA invoice fields found (body also has raw_text) | URL isn't a valid KRA invoice link — do not retry |
| 405 | Method other than POST | Use POST |
| 502 | Could not reach KRA (timeout or connection error) | Retry later with ?refresh=1; underlying failure is cached and next call will re-attempt |
import requests
resp = requests.post(
"https://www.aisheild.tech/api/fetch-url/",
json={"url": "https://itax.kra.go.ke/KRA-Portal/invoiceChk.htm?actionCode=loadPage&invoiceNo=123456789"},
timeout=30,
)
data = resp.json()
if data.get("success"):
print("CU Number:", data["cu_number"])
for item in data["data"]:
print(f'{item["label"]}: {item["value"]}')
else:
print("Error:", data.get("error"))
const res = await fetch("https://www.aisheild.tech/api/fetch-url/", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
url: "https://itax.kra.go.ke/KRA-Portal/invoiceChk.htm?actionCode=loadPage&invoiceNo=123456789"
}),
});
const data = await res.json();
if (data.success) {
console.log("CU Number:", data.cu_number);
} else {
console.error("Error:", data.error);
}
$ch = curl_init("https://www.aisheild.tech/api/fetch-url/");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode([
"url" => "https://itax.kra.go.ke/KRA-Portal/invoiceChk.htm?actionCode=loadPage&invoiceNo=123456789"
]),
CURLOPT_HTTPHEADER => ["Content-Type: application/json"],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
]);
$data = json_decode(curl_exec($ch), true);
if (!empty($data["success"])) {
echo "CU Number: " . $data["cu_number"];
}
curl -X POST "https://www.aisheild.tech/api/fetch-url/" \
-H "Content-Type: application/json" \
-d '{"url":"https://itax.kra.go.ke/KRA-Portal/invoiceChk.htm?actionCode=loadPage&invoiceNo=123456789"}'
curl -X POST "https://www.aisheild.tech/api/fetch-url/" -H "Content-Type: application/json" -d "{\"url\":\"https://itax.kra.go.ke/KRA-Portal/invoiceChk.htm?actionCode=loadPage^&invoiceNo=123456789\"}"
Create body.json:
{"url":"https://itax.kra.go.ke/KRA-Portal/invoiceChk.htm?actionCode=loadPage&invoiceNo=123456789"}
Then run in CMD:
curl -X POST "https://www.aisheild.tech/api/fetch-url/" -H "Content-Type: application/json" --data-binary "@body.json"
Invoke-RestMethod -Uri "https://www.aisheild.tech/api/fetch-url/" `
-Method POST `
-ContentType "application/json" `
-Body '{"url":"https://itax.kra.go.ke/KRA-Portal/invoiceChk.htm?actionCode=loadPage&invoiceNo=123456789"}'
cu_number as your primary result
It’s the verified CU number sourced directly from KRA. The data array is useful for
displaying full invoice details, but most integrations only need cu_number.
if not data.get("success"):
# error — inspect data.get("error")
elif data.get("cu_number"):
# verified CU number found
else:
# valid KRA invoice with no CU number (rare)
Some KRA invoices omit Total Tax Amount or Total Invoice Amount. Iterate over
data defensively — look fields up by label rather than by array position.
Every successful response is cached server‑side. Repeat lookups for the same URL return instantly
and don’t hit KRA again. Only pass ?refresh=1 when you have a specific reason to want
a live re‑read.
A cold fetch (cache miss) waits up to ~20s on KRA’s server. A 5‑second timeout will fail intermittently.
The failure is recorded server‑side; hammering won’t help. Use exponential backoff (e.g. 5s → 15s → 60s).
A 404 means “this page isn’t a KRA invoice” — it won’t become one on retry. Show the user a clear message instead.
cached flag
It’s a useful diagnostic: a sudden run of cached: false usually means new invoice URLs
are being queried; a steady stream of cached: true means your workload is repeating
existing URLs.
When testing from Windows CMD:
| You want to send | You must write |
|---|---|
" inside a "..." argument | \" |
& anywhere in the argument | ^& |
|, <, >, ^ | prefix with ^ too |
Otherwise CMD splits your URL at & and you’ll see errors like:
{"success": false, "error": "Invalid JSON body."}
'invoiceNo' is not recognized as an internal or external command
The easiest escapes from Windows quoting altogether are:
Invoke-RestMethod (native, no escaping of &), or--data-binary "@body.json" with cURL.| Item | Value |
|---|---|
| Endpoint | POST https://www.aisheild.tech/api/fetch-url/ |
| Bypass cache | POST https://www.aisheild.tech/api/fetch-url/?refresh=1 |
| Required header | Content-Type: application/json |
| Required body | {"url": "<invoice URL>"} |
| Success status | 200 |
| Primary result field | cu_number |
| Full result field | data (array of {label, value}) |
| Recommended client timeout | ≥ 30 s |
| Verified working example | CU 123456789, supplier ENTERPRISES |