# SiteVerdict — Visa & Entry Requirements (official sources) (`siteverdict/siteverdict-visa-entry-requirements`) Actor

Do I need a visa, eVisa or ETA? Answers per passport and destination from official government sources (GOV.UK FCDO, UK Home Office, EU law, Canada IRPR, Australia Home Affairs, India e-Visa) with citations, trip checklists and change alerts. Unknown when no official data — never guessed.

- **URL**: https://apify.com/siteverdict/siteverdict-visa-entry-requirements.md
- **Developed by:** [Aman Thakran](https://apify.com/siteverdict) (community)
- **Categories:** Travel
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## SiteVerdict — Visa & Entry Requirements (official sources)

**Do I need a visa, an eVisa or an ETA to visit this country on this passport? Get the answer from the government's own source, with a link, the date it was fetched and a content hash — or an honest "unknown — check the embassy" when no official data exists.**

> ⚠️ **Not immigration or legal advice.** Entry rules change, sometimes with little notice. Every answer is an automated summary of an official government source, with the date it was fetched. Always confirm with the official source linked, the destination embassy/consulate and your airline before you travel. Border officials make the final decision.

### Who it is for

- **Travel agencies, tour operators and OTAs** — pre-check every passenger's passport against every country on the itinerary, including transit stops.
- **Corporate travel / HR / mobility teams** — trip checklists for staff with different nationalities.
- **AI agents and chatbots** — a citation-backed tool (see `MCP-TOOL.md`) instead of a model's memory.
- **Content teams and compliance** — a "refresh" run that tells you exactly what changed in the official rules since last time.

### What you get for each traveller × destination

| Field | Example |
|---|---|
| `category` | `visa_free` · `visa_on_arrival` · `evisa` · `eta` · `visa_required` · `free_movement` · `citizen` · `unknown` |
| `maxStayDays`, `stayRule` | `90`, `90 days in any 180-day period` |
| `authorisation` | `UK Electronic Travel Authorisation (ETA)` + official link + fee note |
| `purposeCovered`, `exceedsMaxStay` | work trips and over-long stays are flagged as "visa required" with the reason |
| `passportValidity`, `transit`, `health` | quoted from the official page where it publishes them |
| `officialText` | the exact official paragraph (GOV.UK) |
| `sources[]` | publisher, URL, licence, fetched date, official "last updated" date, SHA-256 content hash |
| `confidence` | `high` / `medium` / `low` (lower when the official prose is ambiguous or two official sources differ) |
| `disclaimer` | always included |

Trip mode adds one **checklist per traveller**: actions ("get a UK ETA", "apply for a Schengen visa"), warnings (Schengen 90/180 overrun, passport expiring too soon, ETIAS status, unknowns) and a per-leg summary.

### Official sources (and what they cover)

| Destination(s) | Passports covered | Official source | Licence |
|---|---|---|---|
| ~226 destinations | **British citizen** | GOV.UK Foreign travel advice (FCDO), via the GOV.UK Content API | Open Government Licence v3.0 |
| United Kingdom | all nationalities | Immigration Rules Appendix Visitor: Visa national list; "Check if you can get an ETA" list; "Get an ETA" guide (GOV.UK Content API) | OGL v3.0 |
| 29 Schengen states + Cyprus (the 30 countries applying the EU visa list; Ireland excluded) | all nationalities | Regulation (EU) 2018/1806 Annex I & II (latest consolidated text), Visa Code Annex IV (airport transit visas) — retrieved via the EU Publications Office CELLAR API; ETIAS status from the European Commission (DG HOME) page | © European Union, Commission Decision 2011/833/EU |
| Canada | all nationalities | Immigration and Refugee Protection Regulations s.7.1, s.190 and Schedule 1.1 (Justice Laws Website XML) | Reproduction of Federal Law Order |
| Australia | all nationalities | Home Affairs: ETA (subclass 601) and eVisitor (subclass 651) eligible passports | © Commonwealth of Australia, CC BY 3.0 AU |
| India | all nationalities | Indian Visa Online — e-Visa eligible countries | Government of India website (GIGW, acknowledgement) |

**Coverage achieved (refresh of 11 Oct 2026):** 226 GOV.UK destinations for British citizens (221 classified, 5 "unknown"); for every other passport: UK, 30 EU-visa-list countries, Canada, Australia and India (34 destinations × up to 198 nationalities ≈ 6,900 official (nationality, destination) pairs). Run `mode: "coverage"` for the live matrix.

**Never used:** IATA Timatic (licensed), commercial visa websites, or any page whose robots.txt disallows us (EUR-Lex HTML is not crawled — the same legal text is taken from the Publications Office API). Sources that block automated access (e.g. US State Department, CBP, Japan MOFA) are **not** bypassed; those pairs return `unknown`.

### Input examples

**Check pairs**

```json
{
  "mode": "check",
  "checks": [
    { "nationality": "India", "destination": "United Kingdom", "durationDays": 14 },
    { "nationality": "US", "destination": "France" },
    { "nationality": "British citizen", "destination": "Thailand", "purpose": "business" }
  ]
}
```

**Bulk matrix**: `"nationalities": ["India", "Nigeria"], "destinations": ["UK", "Canada", "Germany"]`.

**Trip check**

```json
{
  "mode": "trip",
  "trip": {
    "startDate": "2026-12-01",
    "travellers": [
      { "name": "Asha", "nationality": "India", "passportExpiry": "2027-05-01" },
      { "name": "Tom", "nationality": "United Kingdom" }
    ],
    "itinerary": [
      { "country": "Netherlands", "role": "transit" },
      { "country": "Canada", "days": 10 },
      { "country": "France", "days": 7 }
    ]
  }
}
```

**Refresh + change report**: `{ "mode": "refresh", "govukRefreshScope": "changed" }` — re-fetches every official source, diffs it against the previous refresh (list additions/removals, changed stay limits, changed official text) and outputs one row per change. Schedule it daily or weekly.

### Sample output (abridged)

```json
{
  "type": "answer",
  "nationalityName": "India",
  "destinationName": "United Kingdom",
  "category": "visa_required",
  "categoryLabel": "Visa required before travel",
  "authorisation": { "name": "UK Standard Visitor visa", "applyUrl": "https://www.gov.uk/standard-visitor" },
  "summary": "India → United Kingdom (tourism, 14 days): Visa required before travel — UK Standard Visitor visa. Source: Home Office (UK) (updated 2026-03-05), fetched 2026-10-11.",
  "confidence": "high",
  "sources": [{ "id": "uk-visa-nationals", "url": "https://www.gov.uk/guidance/immigration-rules/immigration-rules-appendix-visitor-visa-national-list", "fetchedAt": "2026-10-11T07:58:12Z", "contentHash": "…" }]
}
```

### How it works

1. Each official source has its own parser (JSON from the GOV.UK Content API, consolidated EU law XHTML, Canadian regulation XML, embedded JSON on Home Affairs pages, the India e-Visa list).
2. GOV.UK prose is normalised with a conservative rule engine (negations like "visa on arrival is no longer available", conditional "if…" sentences and "exempt from K-ETA" are handled). Ambiguous text lowers the confidence instead of guessing.
3. Parsed sources are cached in a persistent named key-value store (`siteverdict-visa-kb`) for `maxCacheAgeHours` (default 24 h). GOV.UK pages are re-fetched whenever the official index shows a newer update.
4. Destination-government sources win; GOV.UK FCDO advice is used for British citizens and as a cross-check (disagreements are flagged in `notes`).

### FAQ

**Why "unknown" for my pair?** No official, machine-readable source we are permitted to use covers it (e.g. Brazilian passport → Thailand). We'd rather say "check the embassy" than guess.

**Does it know about ETIAS?** Yes — the European Commission page is checked on every refresh. As of 11 Oct 2026 it states ETIAS is not yet in operation; answers say so and switch to "ETA required" automatically when the official status changes.

**Transit?** Set `"role": "transit"` on a leg (or purpose `transit`). Schengen uses the EU airport-transit-visa list; the UK distinguishes airside/landside and flags visa nationals for DATV checks; Canada's eTA applies to air transit.

**Is the data live?** Each answer shows when its source was fetched. Set `maxCacheAgeHours: 0` to force live fetches.

### Limits

- British citizens get the widest coverage (GOV.UK). Other passports are covered only for UK, EU-visa-list countries, Canada, Australia and India.
- GOV.UK classification is text-based; always read `officialText` for edge cases (special zones, cruise visits, land borders).
- Maximum stays are short-stay visitor rules; long stays, work and study need national visas.
- US (ESTA/VWP), Japan, Singapore, UAE, Thailand and NZ government pages were not machine-readable or blocked automated access in Oct 2026, so non-British passports to those countries return "unknown".

### Pricing

Pay per event: **check** (one charged answer per traveller × destination; `unknown`/invalid/citizen answers are free) and **source-refresh** (once per refresh run). The run's maximum charge is respected — work stops cleanly when the budget is reached. See `PRICING.md`.

### Compliance & attribution

- Contains public sector information licensed under the Open Government Licence v3.0 (GOV.UK).
- © European Union, https://eur-lex.europa.eu — re-use under Commission Decision 2011/833/EU.
- Canadian federal law reproduced under the Reproduction of Federal Law Order; this is not the official version.
- Source: Australian Government Department of Home Affairs, © Commonwealth of Australia, Creative Commons Attribution 3.0 Australia.
- Government of India, Indian Visa Online — source acknowledged.
- Honest User-Agent, robots.txt respected, per-site politeness delay, no CAPTCHA/bot-protection bypass, no personal data collected (traveller names you supply are only echoed back in your own dataset).

# Changelog

This Actor's version history is a separate document: https://apify.com/siteverdict/siteverdict-visa-entry-requirements/changelog.md

# Actor input Schema

## `mode` (type: `string`):

<b>check</b>: answer nationality × destination pairs. <b>trip</b>: per-traveller checklist for an itinerary (incl. transit). <b>refresh</b>: re-fetch all official sources and report what changed since the last refresh. <b>coverage</b>: show which nationality/destination pairs are covered by official data (free).

## `checks` (type: `array`):

List of pairs to check. Each item: {"nationality": "India", "destination": "France", "purpose": "tourism", "durationDays": 10}. Country names, demonyms ("Indian") or ISO alpha-2 codes ("IN") all work. Use "British citizen"/"GB" for a full British citizen passport and "GB-BNO" for British National (Overseas).

## `nationalities` (type: `array`):

Optional. Every nationality here is checked against every destination in "Destinations (bulk)".

## `destinations` (type: `array`):

Optional. Used with "Nationalities (bulk)". In refresh mode, limits the GOV.UK pages refreshed to these destinations.

## `purpose` (type: `string`):

Default trip purpose when a check or itinerary leg does not set one. Work always needs a work visa/permit; study means short-term study.

## `durationDays` (type: `integer`):

Optional. If the stay is longer than the official visa-free/ETA limit the answer becomes "visa required" with an explanation.

## `trip` (type: `object`):

{"startDate": "2026-12-01", "purpose": "tourism", "travellers": \[{"name": "Asha", "nationality": "India", "passportExpiry": "2027-05-01"}], "itinerary": \[{"country": "United Arab Emirates", "role": "transit"}, {"country": "France", "days": 7}, {"country": "United Kingdom", "days": 5}]}. Each traveller × leg is one charged check.

## `govukRefreshScope` (type: `string`):

<b>changed</b>: only destinations whose GOV.UK page was updated since the last refresh (uses the official index timestamps — cheapest). <b>all</b>: re-fetch all ~226 pages. <b>none</b>: skip GOV.UK.

## `maxCacheAgeHours` (type: `integer`):

Official sources are cached in a persistent key-value store and reused for this long before being re-fetched (GOV.UK pages are also re-fetched whenever the official index shows a newer update). Use 0 to always fetch live.

## `includeOfficialText` (type: `boolean`):

Include the relevant official paragraph (GOV.UK) in each answer so you can read the exact wording.

## `maxChecks` (type: `integer`):

Safety cap on the number of pairs answered in one run (the run's maximum total charge also applies).

## `requestDelayMs` (type: `integer`):

Minimum gap between requests to the same government website.

## `kbStoreName` (type: `string`):

Named key-value store that keeps the parsed official sources between runs (needed for change diffs). Change it to keep separate histories.

## Actor input object example

```json
{
  "mode": "check",
  "checks": [
    {
      "nationality": "India",
      "destination": "United Kingdom",
      "purpose": "tourism",
      "durationDays": 14
    },
    {
      "nationality": "United States",
      "destination": "France",
      "purpose": "tourism",
      "durationDays": 10
    },
    {
      "nationality": "British citizen",
      "destination": "Thailand",
      "purpose": "tourism",
      "durationDays": 21
    }
  ],
  "purpose": "tourism",
  "trip": {
    "startDate": "2026-12-01",
    "purpose": "tourism",
    "travellers": [
      {
        "name": "Traveller A",
        "nationality": "India",
        "passportExpiry": "2027-05-01"
      },
      {
        "name": "Traveller B",
        "nationality": "United Kingdom"
      }
    ],
    "itinerary": [
      {
        "country": "Netherlands",
        "role": "transit"
      },
      {
        "country": "Canada",
        "days": 10
      },
      {
        "country": "France",
        "days": 7
      }
    ]
  },
  "govukRefreshScope": "changed",
  "maxCacheAgeHours": 24,
  "includeOfficialText": true,
  "maxChecks": 1000,
  "requestDelayMs": 500,
  "kbStoreName": "siteverdict-visa-kb"
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `output` (type: `string`):

No description

## `files` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "mode": "check",
    "checks": [
        {
            "nationality": "India",
            "destination": "United Kingdom",
            "purpose": "tourism",
            "durationDays": 14
        },
        {
            "nationality": "United States",
            "destination": "France",
            "purpose": "tourism",
            "durationDays": 10
        },
        {
            "nationality": "British citizen",
            "destination": "Thailand",
            "purpose": "tourism",
            "durationDays": 21
        }
    ],
    "trip": {
        "startDate": "2026-12-01",
        "purpose": "tourism",
        "travellers": [
            {
                "name": "Traveller A",
                "nationality": "India",
                "passportExpiry": "2027-05-01"
            },
            {
                "name": "Traveller B",
                "nationality": "United Kingdom"
            }
        ],
        "itinerary": [
            {
                "country": "Netherlands",
                "role": "transit"
            },
            {
                "country": "Canada",
                "days": 10
            },
            {
                "country": "France",
                "days": 7
            }
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("siteverdict/siteverdict-visa-entry-requirements").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "mode": "check",
    "checks": [
        {
            "nationality": "India",
            "destination": "United Kingdom",
            "purpose": "tourism",
            "durationDays": 14,
        },
        {
            "nationality": "United States",
            "destination": "France",
            "purpose": "tourism",
            "durationDays": 10,
        },
        {
            "nationality": "British citizen",
            "destination": "Thailand",
            "purpose": "tourism",
            "durationDays": 21,
        },
    ],
    "trip": {
        "startDate": "2026-12-01",
        "purpose": "tourism",
        "travellers": [
            {
                "name": "Traveller A",
                "nationality": "India",
                "passportExpiry": "2027-05-01",
            },
            {
                "name": "Traveller B",
                "nationality": "United Kingdom",
            },
        ],
        "itinerary": [
            {
                "country": "Netherlands",
                "role": "transit",
            },
            {
                "country": "Canada",
                "days": 10,
            },
            {
                "country": "France",
                "days": 7,
            },
        ],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("siteverdict/siteverdict-visa-entry-requirements").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "mode": "check",
  "checks": [
    {
      "nationality": "India",
      "destination": "United Kingdom",
      "purpose": "tourism",
      "durationDays": 14
    },
    {
      "nationality": "United States",
      "destination": "France",
      "purpose": "tourism",
      "durationDays": 10
    },
    {
      "nationality": "British citizen",
      "destination": "Thailand",
      "purpose": "tourism",
      "durationDays": 21
    }
  ],
  "trip": {
    "startDate": "2026-12-01",
    "purpose": "tourism",
    "travellers": [
      {
        "name": "Traveller A",
        "nationality": "India",
        "passportExpiry": "2027-05-01"
      },
      {
        "name": "Traveller B",
        "nationality": "United Kingdom"
      }
    ],
    "itinerary": [
      {
        "country": "Netherlands",
        "role": "transit"
      },
      {
        "country": "Canada",
        "days": 10
      },
      {
        "country": "France",
        "days": 7
      }
    ]
  }
}' |
apify call siteverdict/siteverdict-visa-entry-requirements --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,siteverdict/siteverdict-visa-entry-requirements"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/sv1QoS5cjeboeNuLH/builds/uG7WtaRFHx2H3q5Ay/openapi.json
