API reference / Requirements

post/v1/check

Does this traveller need a visa for this destination?

Operation
checkRequirement
Authentication
API key

The core question, and the one endpoint where a wrong answer strands someone at a boarding gate. Every answer carries the statutory instrument it came from.

The body is a **person and a journey**, not a passport alone. A nationality lookup cannot express a transit exemption or a residence-permit exemption, which is why `transit_airside` and `residence_permit_iso3` exist. Omitting them does not apply those exemptions.

Request

Body

nationality_iso3stringrequired

Passport nationality, ISO 3166-1 alpha-3. **Must be an assigned code** — three uppercase letters is not enough. `XYZ` is a 400, not a verdict: an airline typo that came back as a plausible `eta_required` looked exactly like an answer, which is the worst shape a wrong answer can take here. Territories (`HKG`, `PSE`) are assigned codes and are accepted; they fall through to the destination’s statutory default.

example: SOM

destination_iso3stringrequired

one of: ETHSOMDJIKEN

example: KEN

visa_typestring

one of: touristbusinesstransitworkstudentinvestmentmedicaljournalistconferencereligioussportsresidenceentertainmentngoofficial

passport_typestring

Several exemption schedules key on passport class rather than nationality. Defaults to `ordinary`, which is not exempt anywhere diplomatic is.

one of: ordinarydiplomaticserviceofficial

transit_airsideboolean

True when the traveller stays within the transit area and does not clear immigration. An airline knows this from the booking. Omit it and transit exemptions are NOT applied — the engine will not assume an exemption it was not told about.

residence_permit_iso3string

ISO3 of a country whose residence or work permit the traveller holds.

as_ofstring

The date the answer is *about* — normally the travel date, not today. A schedule amended between now and then changes the answer, so answering with today’s rules would be answering a different question. Defaults to today.

example: 2026-11-14

Responses

200A verdict, the rules that produced it, and the products available to this traveller.
verdictstringrequired

`unknown` is a real answer, returned when we hold no verified rule for this traveller. It is never collapsed into a guess — telling someone they need no visa when we are unsure is the most damaging thing this endpoint can do.

one of: evisa_requiredvisa_required_embassyvisa_on_arrivalexempteta_requiredunknown

visa_requiredbooleannullrequired

Null when the verdict is `unknown`.

max_stay_daysintegernullrequired
notesstring[]required
productsProduct[]required

Empty for an exempt traveller, and empty for a product withdrawn under a valid rule where the traveller is exempt. Products restricted to a nationality are filtered out here.

sourcesSource[]required
citationstringrequired

The statutory instrument behind the verdict.

example: Kenya Citizenship and Immigration (Amendment) Rules, 2025 (s.48, Act No. 12 of 2011)

source_tierstringrequired

How strong the source is. `secondary` means the rule rests on an aggregation — IATA Timatic, a Wikipedia mirror — rather than a government publication. A partner deciding whether to board someone should know the difference, so it is a field rather than a footnote in our research directory.

one of: statutoryofficialsecondary

matched_rule_idstringrequired

Which rule decided this. Makes a contested verdict traceable to its source.

example: ken.transit.airside

answered_as_ofstringrequired

The date this answer is *about*, which is not the date it was given. It reads `as_of` back, so a caller checking a future flight can confirm their date was considered rather than being silently answered with today’s rules.

example: 2026-11-15

effective_fromstringnullrequired

Start of the deciding rule’s validity window. Null when it is always in force.

effective_tostringnullrequired

End of the deciding rule’s validity window. Null when it is always in force.

400The request body or query failed validation.
errorobjectrequired
401No key, an unknown key, a revoked key, or a key whose tenant is not active. All four return an identical body on purpose: distinguishing them would let a caller discover which partners we have.
errorobjectrequired
403The key is valid but does not hold the scope this route requires.
errorobjectrequired
404Not found, or not yours. A resource belonging to another tenant answers 404 rather than 403, because 403 would confirm the record exists.
errorobjectrequired
429Rate limit exceeded for this tenant.
errorobjectrequired

Example

curl
curl -X POST https://api.haavisa.com/v1/check \
  -H "Authorization: Bearer $HAAVISA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "nationality_iso3": "SOM",
    "destination_iso3": "KEN"
  }'

More in Requirements

Something unclear? The eligibility guide explains the model behind the answers; the errors guide covers the envelope.