<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mustapha Larhrouch</title>
    <description>The latest articles on DEV Community by Mustapha Larhrouch (@mustapha_larhrouch_91a2d4).</description>
    <link>https://dev.to/mustapha_larhrouch_91a2d4</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4202910%2Ff7235d39-88d6-4190-ae0a-70e7dee2f563.png</url>
      <title>DEV Community: Mustapha Larhrouch</title>
      <link>https://dev.to/mustapha_larhrouch_91a2d4</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mustapha_larhrouch_91a2d4"/>
    <language>en</language>
    <item>
      <title>I audit my visa database against the airlines' own data every month. Here is how it works</title>
      <dc:creator>Mustapha Larhrouch</dc:creator>
      <pubDate>Fri, 23 Oct 2026 15:15:17 +0000</pubDate>
      <link>https://dev.to/mustapha_larhrouch_91a2d4/i-audit-my-visa-database-against-the-airlines-own-data-every-month-here-is-how-it-works-3675</link>
      <guid>https://dev.to/mustapha_larhrouch_91a2d4/i-audit-my-visa-database-against-the-airlines-own-data-every-month-here-is-how-it-works-3675</guid>
      <description>&lt;p&gt;If you hold a Moroccan, Nigerian or Indian passport, "where can I go without a visa?" is not a fun quiz. It decides whether a trip happens at all. The usual answer is a passport ranking site with a big headline number. I built &lt;a href="https://wandava.com/passport/morocco?utm_source=devto&amp;amp;utm_medium=community&amp;amp;utm_campaign=dataset-article" rel="noopener noreferrer"&gt;Wandava&lt;/a&gt; to give that answer per passport, and I learned quickly that the hard part is not the website. It is knowing whether each of the roughly 39,000 passport-to-destination rules is still true.&lt;/p&gt;

&lt;p&gt;This post is about how I keep that data honest: where it comes from, how I check it, and what the checks found.&lt;/p&gt;

&lt;h2&gt;
  
  
  Starting point: a good open dataset that ages
&lt;/h2&gt;

&lt;p&gt;The base is the open &lt;a href="https://github.com/visualpharm/visa-free-dataset" rel="noopener noreferrer"&gt;Passport Index dataset&lt;/a&gt; (MIT licence): one row per passport and destination, with values like "visa free", "visa on arrival", "e-visa" or "visa required". It is the best free starting point I found. But visa rules change every week, and a dataset that was right in June is wrong somewhere by October.&lt;/p&gt;

&lt;p&gt;How wrong? In my first check, on 24 September 2026, the Moroccan passport agreed with the airline-based reference (more on that below) on 154 of 198 destinations. Some differences were labels, but several were real. The worst: Thailand. On 15 September, Thailand cut its visa exemption for 21 nationalities, Morocco included. The dataset still said "visa-free, 60 days", and my site was recommending Thailand as one of the easiest trips for Moroccans. That was the moment I stopped treating the dataset as truth.&lt;/p&gt;

&lt;h2&gt;
  
  
  Corrections live in a separate file, each with a source
&lt;/h2&gt;

&lt;p&gt;I never edit the base data. Every correction is a record in its own file, applied at build time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"th-2026-30d"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"destination"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"TH"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"passports"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"AU"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"BH"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"set"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"visa-free"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"days"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"validFrom"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-09-15"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://www.tatnews.org/2026/09/thailand-introduces-new-30-day-and-15-day-visa-exemption-rules-from-15-september/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"publisher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Tourism Authority of Thailand, citing the Royal Gazette of 31 Aug 2026"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"checked"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-09-24"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"verified"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three rules keep this honest:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;No source, no correction.&lt;/strong&gt; The source must be the destination's own government (immigration service, foreign ministry, official gazette, embassy) or, when that is unreachable, an authoritative secondary source that is named as such.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;validFrom&lt;/code&gt; separates real changes from stale data.&lt;/strong&gt; Thailand changed on a known date, so the record switches on by itself. A correction of old data has no date.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The page shows it.&lt;/strong&gt; Every corrected row on a passport page now displays the publisher and the date I read it. If I can't show where a rule comes from, I don't publish it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Today there are 188 such corrections, touching about 5,000 cells.&lt;/p&gt;

&lt;h2&gt;
  
  
  The audit: comparing against what airlines check
&lt;/h2&gt;

&lt;p&gt;Airlines check passengers against IATA Timatic, which is not free. Wikipedia's "Visa requirements for X citizens" tables cite Timatic for most rows, so they are a usable proxy. A script fetches each table, parses it, and compares it with my published data, passport by passport:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;agree&lt;/strong&gt;: same entry type (and same stay length, when both give one);&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;we say easier / we say harder&lt;/strong&gt;: a real difference to investigate;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;dismissed&lt;/strong&gt;: a difference I already checked against an official source, which showed my value is right (recorded with that source, re-checked after six months);&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;labelling&lt;/strong&gt;: both labels are defensible (an "optional eVisa or visa on arrival" filed as visa on arrival, for example).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The script never changes data. It writes a report per passport and a summary grouped by destination, because one official check usually settles a row for many passports at once.&lt;/p&gt;

&lt;p&gt;The latest full run (22 October 2026) compared 38,611 rules across 199 passports. &lt;strong&gt;36,888 (95.5%) either match Timatic or were confirmed against an official source. 1,723 are still open.&lt;/strong&gt; For Morocco it is 192 of 198, with 6 open, and each of those 6 is open for a reason I can name (more below).&lt;/p&gt;

&lt;h2&gt;
  
  
  What the audits found
&lt;/h2&gt;

&lt;p&gt;A few cases show why "check the official source" is not a formality.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Georgia.&lt;/strong&gt; Morocco is on Georgia's published list of countries eligible for the e-visa. Wikipedia said visa required. I kept "e-visa" at first, because the official list said so. Then I asked the e-visa application form itself, for every citizenship: it rejects Moroccans and tells them to apply at an embassy. The published list was out of date, and Wikipedia was right. 42 passports changed to &lt;a href="https://wandava.com/passport/morocco/to/georgia?utm_source=devto&amp;amp;utm_medium=community&amp;amp;utm_campaign=dataset-article" rel="noopener noreferrer"&gt;visa required&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Saint Vincent and the Grenadines.&lt;/strong&gt; The dataset gives most visa-free passports 90 days. The foreign ministry's own page says visitors get "visa free access for a maximum stay of 1 month", except CARICOM, OECS and British passports. One record fixed it for every passport.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Andorra and San Marino.&lt;/strong&gt; Neither issues visas, so they look visa-free. But you can only reach them through France, Spain or Italy, so a Moroccan needs a Schengen visa to get there. My data says that, and the difference with Wikipedia is now recorded as checked.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What stays open.&lt;/strong&gt; Israel's online visa may or may not be open to Moroccans: press reports disagree and the official portal only renders in a browser. Namibia's portal sits behind a bot check and its official list is a scanned PDF. Solomon Islands' government site blocks scripts. Venezuela's portal needs an account. Those rules stay as they are, and stay on the open list, until an official source can be read. I don't change a rule on the word of a blog or a visa agency.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why sites disagree on the "number"
&lt;/h2&gt;

&lt;p&gt;The same passport gets very different totals on different sites, and people read that as one of them being wrong. Usually they count different things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;visa-free only&lt;/strong&gt;: for Morocco, 40;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;no step before you travel&lt;/strong&gt; (visa-free + visa on arrival): 63;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;no embassy at all&lt;/strong&gt; (adds online eTAs and eVisas): 100.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is also a definition fight inside "eVisa". Some countries let you apply online, but only through a local sponsor or with a full set of supporting documents (the UAE tourist visa, Australia's visitor visa for many nationalities). Wandava counts those as embassy-style visas, not eVisas. Passport pages now lead with the visa-free number and show how the rest adds up, so nobody mistakes 100 for "visa-free".&lt;/p&gt;

&lt;h2&gt;
  
  
  The site is static, on purpose
&lt;/h2&gt;

&lt;p&gt;Wandava is a static Nuxt site with no backend and no database. All of this runs at build time:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;npm run data&lt;/code&gt; merges the base dataset, the corrections, the visa-holder rules, official eVisa portals and airport-transit lists into one JSON file per passport and per destination;&lt;/li&gt;
&lt;li&gt;the audit script writes &lt;code&gt;data/audits/latest.json&lt;/code&gt;, which the build publishes into each passport file, so the accuracy line on a page always matches the last audit;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;npm run generate&lt;/code&gt; prerenders about 1,200 pages in English and French.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A static site makes freshness visible: each page shows the check date of its data, the sitemap's &lt;code&gt;lastmod&lt;/code&gt; follows the data date, and a deploy pings IndexNow with the pages whose data changed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Open data
&lt;/h2&gt;

&lt;p&gt;Everything above is published as JSON with a &lt;a href="https://wandava.com/data/README.md" rel="noopener noreferrer"&gt;field reference&lt;/a&gt;: one file per passport, per destination, the visa-holder rules, the official portals, and the audit summary. The full method, with an accuracy table for 12 passports, is on the &lt;a href="https://wandava.com/methodology?utm_source=devto&amp;amp;utm_medium=community&amp;amp;utm_campaign=dataset-article" rel="noopener noreferrer"&gt;methodology page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you spot a rule that is out of date, I would like to hear about it, ideally with a link to the official source. That is how the open list gets shorter.&lt;/p&gt;

</description>
      <category>opendata</category>
      <category>travel</category>
      <category>nuxt</category>
      <category>dataengineering</category>
    </item>
  </channel>
</rss>
