VisaCheki API Documentation
AI-powered visa eligibility, requirements, and document review API.
Authentication
All endpoints (except / and /health) require an API key passed as a Bearer token:
Authorization: Bearer vsr_your_key_here
Request a key at [visacheki.com/developers/signup](https://visacheki.com/developers/signup). Keys are issued by hand today, usually within a working day, so tell us what you are building and we can put you on the right tier from the start.
Tiers
| Tier | Calls/month | Includes | Price |
|---|---|---|---|
| Free | 100 | Core endpoints | $0 |
| Starter | 2,000 | Core endpoints | $19/mo |
| Growth | 20,000 | Core endpoints | $79/mo |
| Pro | Unlimited | Bulk check, Data API | $299/mo |
| Enterprise | Unlimited | Bulk check, Data API, Compliance API, Webhooks | Contact us |
Every tier is also limited to 60 requests per minute, which is a burst guard rather than a plan feature: it protects the service from a runaway loop and applies the same way whether you are on Free or Enterprise.
Endpoints
GET /v1/countries
Returns all supported countries.
Response:
{
"countries": [
{ "iso": "NG", "name": "Nigeria", "region": "Africa", "flag": "π³π¬" },
{ "iso": "GB", "name": "United Kingdom", "region": "Europe", "flag": "π¬π§" }
]
}
GET /v1/origins
Returns countries with at least one outbound visa route: the passports the API can actually run a check from.
Response:
{
"origins": [
{ "iso": "GH", "name": "Ghana", "region": "Africa", "flag": "π¬π", "route_count": 4 },
{ "iso": "NG", "name": "Nigeria", "region": "Africa", "flag": "π³π¬", "route_count": 8 },
{ "iso": "ZA", "name": "South Africa", "region": "Africa", "flag": "πΏπ¦", "route_count": 2 }
]
}
GET /v1/countries/:code/requirements?origin=NG
Returns visa requirements for a specific route.
Example: GET /v1/countries/GB/requirements?origin=NG
POST /v1/check/requirements
Check if a visa is required and what the requirements are.
Request:
{
"origin": "NG",
"destination": "GB"
}
Response:
{
"requirement": {
"visa_required": true,
"visa_on_arrival": false,
"evisa_available": false,
"fee_usd": 115,
"processing_days_min": 15,
"processing_days_max": 90,
"max_stay_days": 180,
"entries_allowed": "multiple",
"documents_required": ["valid_passport", "bank_statements_6_months"],
"confidence_score": 85,
"last_verified_at": "2026-05-10T00:00:00.000Z"
}
}
POST /v1/check/eligibility
Run an AI-powered eligibility assessment based on applicant profile.
Request:
{
"origin": "NG",
"destination": "GB",
"profile": {
"employment_status": "employed",
"employer_name": "Starterdot LTD",
"monthly_income_usd": 2000,
"bank_balance_usd": 8000,
"has_property": true,
"travel_history": ["AE", "RW", "KE"],
"prior_visa_rejections": [],
"trip_purpose": "tourism",
"trip_duration_days": 14,
"has_invitation_letter": false,
"has_return_ticket": true,
"marital_status": "single",
"dependents": 0
}
}
Response:
{
"report_id": "uuid",
"assessment": {
"score": 62,
"risk_level": "medium",
"verdict": "borderline",
"flags": [
{
"field": "travel_history",
"issue": "No prior Western country travel history",
"severity": "medium"
}
],
"recommendations": [
"Increase bank balance to at least $10,000 before applying",
"Obtain a letter from employer confirming your role and salary",
"Consider applying for UAE or Schengen visa first to build travel history"
],
"summary": "Your application has a moderate chance of approval. The main concerns are limited Western travel history and bank balance slightly below recommended levels for a UK visa."
}
}
POST /v1/check/documents
Review uploaded documents for issues before submission.
Request:
{
"origin": "NG",
"destination": "GB",
"documents": [
{
"type": "passport",
"content": "Name: Samuel Ayangbola. Expiry: 2027-03-15. Nationality: Nigerian."
},
{
"type": "bank_statement",
"content": "Average balance last 6 months: $7,200. No large unexplained deposits."
}
]
}
Response:
{
"review": {
"verdict": "issues_found",
"documents": [
{ "type": "passport", "status": "ok", "issues": [], "notes": "Valid, expires well beyond travel window" },
{ "type": "bank_statement", "status": "issue", "issues": ["Balance slightly below Β£5,000 recommended for UK visitor visa"], "notes": null }
],
"missing_documents": ["proof_of_employment", "return_ticket"],
"critical_issues": [],
"summary": "Passport is fine. Bank balance is acceptable but could be stronger. Missing employment proof and return ticket."
}
}
POST /v1/check/cover-letter
Generate a visa application cover letter via AI. Plain text output, suitable for dropping into an editable textarea so the applicant can refine before submission.
Request:
{
"origin": "NG",
"destination": "GB",
"applicant": {
"name": "Samuel Ayangbola",
"employment_status": "employed",
"employer_name": "Starterdot LTD",
"role_title": "Senior Engineer",
"monthly_income_usd": 2000,
"ties_summary": "Wife and two children in Lagos, mortgage on family home, six years at current employer."
},
"trip": {
"purpose": "tourism",
"start_date": "2026-07-15",
"end_date": "2026-07-28",
"destination_city": "London",
"sponsor_summary": null
},
"prior_visas": "UAE 2023 (granted), Schengen 2024 (granted)"
}
Response:
{
"origin": "NG",
"destination": "GB",
"letter": "[Date]\n\nDear Consular Officer,\n\nI am writing in support of my application for a Standard Visitor Visa to the United Kingdom..."
}
POST /v1/check/interview-prep
Generate a personalised visa-interview study guide. Output is a JSON object with opening_tips[] and a questions[] array, each entry including question, what_they_check, model_answer_outline, and red_flags[]. Together they say what the officer is really probing for and how to answer it candidly.
Request:
{
"origin": "NG",
"destination": "GB",
"applicant": {
"employment_status": "employed",
"employer_name": "Starterdot LTD",
"role_title": "Senior Engineer",
"ties_summary": "Wife and two children in Lagos, six years at employer."
},
"trip": {
"purpose": "tourism",
"duration_days": 14,
"sponsor_summary": null
},
"prior_visas": "UAE 2023 (granted), Schengen 2024 (granted)"
}
Response (abbreviated):
{
"origin": "NG",
"destination": "GB",
"prep": {
"opening_tips": [
"Have your bank statements and employer letter on top of your file.",
"Answer in short, specific sentences; volunteer details only when asked."
],
"questions": [
{
"question": "Why are you visiting the UK?",
"what_they_check": "Whether the stated purpose is consistent and the trip is realistic for the duration.",
"model_answer_outline": "State the specific purpose β’ name the destination city β’ outline activities at a high level β’ mention return date",
"red_flags": ["Vague or shifting answers", "Hint of intent to extend stay"]
}
]
}
}
POST /v1/check/full
Runs requirements, eligibility, and document review in a single call.
Request: Combines all fields from the three endpoints above.
Error Codes
| Code | Meaning |
|---|---|
| 400 | Missing or invalid request parameters |
| 401 | Missing API key, or a key we have no record of |
| 403 | Your key is real but cannot do this: either it has been disabled, or the endpoint needs a higher tier |
| 404 | Route not found in database |
| 429 | Rate limited. Either the 60/minute burst guard or your monthly tier limit |
| 500 | Internal server error |
401 and 403 mean different things, and the difference is worth acting on. A 401 means retrying will not help until you fix the key you are sending. A 403 means the key is genuinely yours and recognised, so the fix is a plan change or a word with us, not a new key. A disabled key returns 403 with a message explaining why and a disabled_at timestamp.
The two 429s are also distinguishable. The burst guard replies {"error": "Too many requests, slow down."} and clears within the minute; the monthly limit replies with your tier, limit, used, and when it resets. Back off on the first, upgrade on the second.
Supported Routes
Two endpoints answer this, and unlike a list in a document they are never out of date:
- GET /v1/origins returns the passport countries we cover. - GET /v1/countries returns the destinations, and GET /v1/countries/:code/requirements?origin=XX tells you whether that particular pair is covered.
Build against those rather than against a hardcoded list. Coverage grows, and a route that is missing today may not be missing next month.
Need a route we do not cover? Email [api@visacheki.com](mailto:api@visacheki.com) and tell us the pair, which helps us prioritise.
Changelog
v0.1.0 - Initial release. 8 Nigeria-origin routes. Requirements, eligibility, and document review endpoints.
Data Licensing API (pro / enterprise)
Expose raw VisaCheki data for embedding in third-party products: travel insurance platforms, corporate travel tools, immigration software, banks.
| Endpoint | Tier | Description |
|---|---|---|
GET /v1/data/requirements | pro+ | Bulk visa requirements export |
GET /v1/data/changelog | pro+ | Full policy change history |
GET /v1/data/processing-stats | enterprise | Crowdsourced processing time stats per route |
GET /v1/data/outcomes | enterprise | Anonymised approval/denial rates per route |
GET /v1/data/requirements
Bulk export of all visa requirements. Paginated (max 200 per page).
Query parameters:
| Param | Type | Description |
|---|---|---|
origin | ISO code | Filter by passport country |
destination | ISO code | Filter by destination country |
confidence_min | integer 0β100 | Only return rows with confidence β₯ this value |
updated_since | YYYY-MM-DD | Only rows last verified on or after this date |
limit | integer | Results per page (max 200, default 100) |
offset | integer | Pagination offset (default 0) |
Response:
{
"data": [
{
"origin_iso": "NG",
"destination_iso": "GB",
"visa_required": true,
"visa_on_arrival": false,
"evisa_available": false,
"fee_usd": 115,
"processing_days_min": 15,
"processing_days_max": 90,
"max_stay_days": 180,
"entries_allowed": "multiple",
"documents_required": ["passport", "bank_statements", ...],
"last_verified_at": "2026-05-01",
"confidence_score": 92,
"source": "scraped",
"updated_at": "2026-05-01T10:00:00.000Z"
}
],
"total": 48,
"limit": 100,
"offset": 0,
"tier": "enterprise"
}
Note: confidence_score and source are only included for enterprise keys.
GET /v1/data/changelog
Full history of visa policy changes. Pro tier returns the last 90 days; enterprise returns the full archive.
Query parameters:
| Param | Type | Description |
|---|---|---|
origin | ISO code | Filter by origin country |
destination | ISO code | Filter by destination country |
since | YYYY-MM-DD | Earliest date to include (enterprise only; pro hard-capped at 90 days) |
limit | integer | Max 500, default 100 |
offset | integer | Pagination offset |
Response:
{
"data": [
{
"origin_iso": "NG",
"destination_iso": "GB",
"changed_field": "fee_usd",
"old_value": "100",
"new_value": "115",
"source": "scraper",
"detected_at": "2026-04-12T08:00:00.000Z"
}
],
"total": 312,
"history_cutoff": null,
"tier": "enterprise"
}
GET /v1/data/processing-stats (enterprise)
Aggregated crowdsourced processing time data per route, split by outcome. Requires at least 3 data points per route.
Response:
{
"data": [
{
"origin_iso": "NG",
"destination_iso": "GB",
"total": 47,
"approved": { "n": 31, "avg_days": 38, "min_days": 12, "max_days": 84 },
"denied": { "n": 16, "avg_days": 22, "min_days": 8, "max_days": 45 }
}
],
"total": 12
}
GET /v1/data/outcomes (enterprise)
Anonymised approval/denial rates per route, computed from user-reported outcomes. Only routes with β₯ 5 reported outcomes are included.
Response:
{
"data": [
{
"origin_iso": "NG",
"destination_iso": "GB",
"approved": 31,
"denied": 16,
"withdrawn": 2,
"total": 49,
"approval_rate_pct": 63.3
}
],
"total": 10
}