← DoINeedVisa

DoINeedVisa API

A small, unversioned REST API for visa-requirement data by passport/destination pair - the same facts the site is built from, served over the full country matrix. JSON only, no SDK required. Base URL:

https://api.doineedvisa.to

Get a key

Every data endpoint needs an API key. The free tier gives you 300 requests / month - enough to build and test against. To get a free key, email [email protected] with a one-line description of what you’re building and your rough monthly volume. Keys are usually issued the same day.

Your key is shown once and stored only as a hash - keep it somewhere safe, and treat it like a password. Need it rotated or revoked? Email the same address. Higher-volume agency and enterprisetiers exist (see the table below); paid self-serve checkout is coming - for now, ask and we’ll set it up.

Authentication

Pass your key on every data request, either as a bearer token or the X-API-Key header. A request without a valid key returns 401. (/health is the one open endpoint - no key, for uptime probes.)

# Either header works:
curl -s "https://api.doineedvisa.to/thailand?from=canada" \
  -H "Authorization: Bearer YOUR_API_KEY"

curl -s "https://api.doineedvisa.to/thailand?from=canada" \
  -H "X-API-Key: YOUR_API_KEY"

Rate limits & tiers

Quotas are counted per calendar month (UTC). Every keyed response carries your current standing in RateLimit-* headers; once you’re over quota the API returns 429 with a Retry-After.

TierRequests / monthHow to get it
Free300Email for a key
Agency100,000Contact us
EnterpriseCustomContact us
RateLimit-Limit: 300
RateLimit-Remaining: 298
RateLimit-Reset: 1209600      # seconds until the quota resets

Endpoints

URLs are unversioned. Countries can be given as a lowercase slug (thailand) or an ISO-3166 alpha-2 code (TH) - both resolve to the same row.

GET/{to}?from={from}

The answer for one pair: does a {from} passport holder need a visa for {to}? Returns the requirement, permitted stay, the official e-visa link where one exists, the official visa-information link for destinations that need a traditional visa, and the date the row was last verified.

curl -s "https://api.doineedvisa.to/thailand?from=canada" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response

{
  "from": {
    "iso2": "CA",
    "iso3": "CAN",
    "name": "Canada",
    "slug": "canada",
    "flag": "🇨🇦"
  },
  "to": {
    "iso2": "TH",
    "iso3": "THA",
    "name": "Thailand",
    "slug": "thailand",
    "flag": "🇹🇭"
  },
  "requirement": "visa_free",
  "allowed_stay_days": 60,
  "evisa_link": "https://www.thaievisa.go.th/",
  "visa_info_link": null,
  "fee": null,
  "last_verified_at": "2026-03-01",
  "source": "passportindex.org via github.com/imorte/passport-index-data (MIT-licensed). Redistribution must preserve this attribution - see https://doineedvisa.to/terms"
}

requirement is one of visa_free, visa_on_arrival, eta, e_visa, visa_required, no_admission, or citizen (the self-pair). allowed_stay_days, evisa_link, visa_info_link, and fee are null when the dataset has no value. evisa_link is the online e-visa/ETA portal; visa_info_link is the official government page for destinations that require a traditional embassy/consulate visa; feeis the official curated cost of obtaining the visa/e-visa/ETA/visa-on-arrival (CA → TH is visa-free, so it’s null in the example above); when present it looks like { "amount": 40.27, "currency": "USD", "variant": "ESTA", "source_url": "https://...", "last_verified_at": "2026-07-26" }. Fees are curated for a subset of destinations and stale faster than the visa matrix, so check fee.last_verified_at separately from the pair’s own last_verified_at.

GET/from/{from}

One passport’s full row: every destination in the matrix (199, including the self-pair as citizen, so count is the stable country total). Each entry has the same fields as a pair response.

curl -s "https://api.doineedvisa.to/from/canada" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response (truncated)

{
  "from": { "iso2": "CA", "name": "Canada", "slug": "canada", "flag": "🇨🇦", "iso3": "CAN" },
  "count": 199,
  "destinations": [
    { "to": { "iso2": "TH", "name": "Thailand", ... }, "requirement": "visa_free", "allowed_stay_days": 60, ... },
    ...
  ],
  "source": "passportindex.org via github.com/imorte/passport-index-data (MIT-licensed). Redistribution must preserve this attribution - see https://doineedvisa.to/terms"
}

GET/countries

Country metadata for the whole matrix - ISO codes, display name, slug, and flag.

curl -s "https://api.doineedvisa.to/countries" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response (truncated)

{
  "count": 199,
  "countries": [
    { "iso2": "AD", "iso3": "AND", "name": "Andorra", "slug": "andorra", "flag": "🇦🇩" },
    ...
  ],
  "source": "..."
}

GET/health

Unauthenticated status probe. Resolves a real pair through the data layer, so a 200means “up and able to answer,” not just “process alive.” Returns 503 if the data is missing.

curl -s "https://api.doineedvisa.to/health"

Response

{
  "status": "ok",
  "countries": 199,
  "lastVerifiedAt": "2026-03-01"
}

Errors

Errors are JSON with an error code and a human message.

StatuserrorWhen
400unknown_country / missing_parameterBad or missing slug/code, or no from
401unauthorizedMissing or invalid API key
429rate_limitedMonthly quota spent - see Retry-After
404not_foundNo such endpoint
405method_not_allowedOnly GET/HEAD (and CORS OPTIONS)

Live playground

Run a real call against the live API below. Paste your key for the data endpoints, or try /health with no key to see a response shape immediately.

GET https://api.doineedvisa.to/thailand?from=canada
cURL
curl -s "https://api.doineedvisa.to/thailand?from=canada" \
  -H "Authorization: Bearer YOUR_API_KEY"

Attribution & licensing

The visa data comes from passport-index-data (scraped from passportindex.org), which is MIT-licensed. MIT permits commercial use and redistribution but requires that the copyright and permission notice travel with any substantial portion of the data. The API serves a substantial portion, so if you redistribute or expose this data, you must reproduce the notice below. Every data response also carries a short form of it in the source field and the X-Data-Source header:

passportindex.org via github.com/imorte/passport-index-data (MIT-licensed). Redistribution must preserve this attribution - see https://doineedvisa.to/terms

Full MIT license notice

MIT License

Copyright (c) 2019

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

The data is provided as is, without warranty, and is not for compliance, boarding, or eligibility determinations - requirements change and individual circumstances vary. Programmatic use is governed by the Commercial API Terms (stricter than the consumer Terms of Service) - read them before building on the API; accepting them is a condition of any paid tier.