How eligibility works
Read this before you build on the answer. Most integration mistakes here are not code mistakes — they are treating a verdict as more certain than it is, or less specific than it is.
The unit is a person and a journey
Not a nationality. A passport alone cannot tell you whether someone needs a visa, because the exemptions that matter most often depend on what the traveller is doing:
- Transit. Kenya exempts passengers who remain within the airport precincts. The same passport clearing immigration needs an eTA.
- Residence, not nationality. Kenya exempts holders of its own residence and work permits, whatever passport they carry.
- Passport class. Five countries’ diplomatic and service passports are exempt on terms their ordinary passports are not.
That is why the request takes transit_airside, residence_permit_iso3 and passport_type. Omit them and those exemptions are not applied — the engine will not assume an exemption it was not told about.
Every verdict carries its source
{
"verdict": "exempt",
"matched_rule_id": "ken.exempt90.eth",
"citation": "Kenya Citizenship and Immigration (Amendment) Rules, 2025 (s.48, Act No. 12 of 2011)",
"sources": [{ "url": "https://etakenya.go.ke/general-information", "accessed": "2026-10-01" }]
}matched_rule_id names the exact rule that decided the answer. When a partner disputes a verdict, that is the difference between an argument and a lookup.
Unknown is a real answer
Where we hold no verified rule for a traveller, the verdict is unknown and visa_required is null — not false.
{
"verdict": "unknown",
"visa_required": null,
"notes": ["No verified requirement data for this destination yet."]
}Today unknown is the answer for Ethiopia, Somalia and Djibouti, whose requirement schedules are not yet transcribed. Kenya is complete. That is stated plainly rather than hidden behind a status page.
Rules are true on a date, not forever
Send as_of with the travel date. A schedule amended between today and the flight changes the answer, so answering with today's rules would be answering a different question.
curl -X POST https://api.haavisa.com/v1/check -H "Authorization: Bearer $HAAVISA_API_KEY" -H "Content-Type: application/json" -d '{
"nationality_iso3": "ETH",
"destination_iso3": "SOM",
"as_of": "2025-09-02"
}'That returns unknown. Somalia's exemption schedule comes from SCAA AIC 10/2025, effective 3 September 2025, which cancelled the instruments before it — and we have never captured those. Before that date we hold nothing verified, and the API says so rather than applying today's list backwards.
{
"verdict": "unknown",
"visa_required": null,
"notes": ["No verified rules are in force for Somalia on 2025-09-02. ..."],
"answered_as_of": "2025-09-02",
"effective_from": null,
"effective_to": null
}answered_as_of, effective_from and effective_to come back on every answer, so you can always see which date you asked about and which rule window decided it. Omit as_of and you get today.
Products are filtered for you
Where a traveller needs something, the response lists what they can buy. Two rules govern that list, and both exist because the alternative sells people things they cannot use:
- An exempt traveller gets an empty list. We do not offer an eTA to someone the statute exempts.
- Nationality-restricted products are filtered by nationality. Kenya’s five-year multiple-entry eTA is for US nationals; a Somali applicant will never see it in the response.
Some exemptions are not machine-evaluated
A few published exemptions cannot be evaluated from the inputs the API accepts — ship and aircraft crew, serving British military, holders of laissez-passers issued by named organisations. Rather than silently drop them, the response names them in a note so an agent knows when to override.
{
"verdict": "eta_required",
"notes": [
"Kenya requires an eTA from travellers who are not on the statutory exemption schedule.",
"Not evaluated: Crew of any ship, aircraft, train, vehicle or carrier proceeding to a
destination outside Kenya; Serving members of the British Military; Holders of
laissez-passers issued by UN, African Union, ... If the traveller falls into any of
these, they may be exempt."
]
}That note is written for an integration, not a traveller. The widget summarises it for a consumer audience.
Fees are never blended
government_fee_cents and service_fee_cents are always returned separately. A single blended price is how a refund becomes impossible to explain — and how a partner ends up unable to say which part of a charge was theirs.
{
"code": "KEN_ETA_TOURIST",
"government_fee_cents": 3000,
"service_fee_cents": 2900,
"currency": "USD"
}