openapi: "3.1.0"
info:
  title: "WhereNext Relocation Intelligence API"
  description: |
    Free, open data APIs for international relocation decisions.
    95 countries, 380 cities, 4,149 schools, 27 institutional data sources.
    All data CC BY 4.0 unless noted otherwise.

    **Composite indexes:** Global Relocation Index (7 dimensions) and Cost of Living Index (5 sub-indexes).

    **Data sources:** World Bank, OECD, WHO, UNDP, Global Peace Index, Transparency International, ILO, Eurostat, PISA, EF EPI, Open-Meteo, and WhereNext curated research.

    **Citation format:** "WhereNext. [Dataset Name] 2026. getwherenext.com/data/[dataset]. CC BY 4.0."

    Documentation: https://getwherenext.com/data
    Methodology: https://getwherenext.com/methodology
    LLM context file: https://getwherenext.com/llms.txt
  version: "2026.04"
  contact:
    email: "hello@getwherenext.com"
    url: "https://getwherenext.com/data"
  license:
    name: "CC BY 4.0"
    url: "https://creativecommons.org/licenses/by/4.0/"

servers:
  - url: "https://getwherenext.com"
    description: "Production"

tags:
  - name: "Relocation Index"
    description: "Global Relocation Index — 95 countries ranked across 7 dimensions"
  - name: "Cost of Living"
    description: "Cost of living data at country and city level"
  - name: "Tax"
    description: "Expat tax rates and tax optimization data"
  - name: "Visa"
    description: "Visa requirements, passport power, and digital nomad visas"
  - name: "Schools"
    description: "International school costs, class sizes, diversity, and affordability"
  - name: "Property"
    description: "Property buying costs, yields, and mortgage access for foreign buyers"
  - name: "AI-Optimized"
    description: "Structured endpoints designed for AI/LLM consumption with JSON-LD and natural language summaries"
  - name: "Product Feed"
    description: "Google Shopping product feed for premium reports"

paths:
  /api/data/relocation-index:
    get:
      operationId: getRelocationIndex
      summary: "Global Relocation Index 2026"
      description: |
        Returns the full Global Relocation Index ranking 95 countries across 7 dimensions:
        cost, safety, healthcare, education, career, lifestyle, and infrastructure.
        Scores are 0-100 normalized within each dimension. Composite score is confidence-weighted.
      tags: ["Relocation Index"]
      responses:
        "200":
          description: "Relocation index with country rankings"
          headers:
            Cache-Control:
              schema:
                type: string
                example: "public, max-age=86400"
            Access-Control-Allow-Origin:
              schema:
                type: string
                example: "*"
          content:
            application/json:
              schema:
                type: object
                properties:
                  meta:
                    $ref: "#/components/schemas/RelocationIndexMeta"
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/RelocationIndexRow"
              example:
                meta:
                  title: "2026 Global Relocation Index"
                  version: "2026.1"
                  source: "WhereNext (getwherenext.com)"
                  license: "CC BY 4.0"
                  updated: "2026-03-07"
                  countries: 95
                  dimensions: ["cost", "safety", "healthcare", "education", "career", "lifestyle", "infrastructure"]
                  methodology: "https://getwherenext.com/methodology"
                  csv: "https://getwherenext.com/api/data/relocation-index/csv"
                data:
                  - rank: 1
                    country_code: "ch"
                    country: "Switzerland"
                    region: "Europe"
                    composite_score: 82
                    cost: 18
                    safety: 92
                    healthcare: 95
                    education: 88
                    career: 85
                    lifestyle: 80
                    infrastructure: 90
                    confidence: 0.95

  /api/data/relocation-index/csv:
    get:
      operationId: getRelocationIndexCsv
      summary: "Global Relocation Index 2026 (CSV)"
      description: |
        CSV download of the Global Relocation Index.
        Columns: rank, country_code, country, region, composite_score, cost, safety, healthcare, education, career, lifestyle, infrastructure, confidence.
      tags: ["Relocation Index"]
      responses:
        "200":
          description: "CSV file download"
          headers:
            Content-Disposition:
              schema:
                type: string
                example: 'attachment; filename="wherenext-global-relocation-index-2026.csv"'
          content:
            text/csv:
              schema:
                type: string

  /api/data/cost-of-living:
    get:
      operationId: getCostOfLiving
      summary: "Cost of Living Index 2026"
      description: |
        Returns cost of living data for 95 countries, ranked cheapest to most expensive.
        Includes overall cost index, monthly USD estimate, and sub-indexes for groceries, rent, utilities, and transport.
      tags: ["Cost of Living"]
      responses:
        "200":
          description: "Cost of living rankings"
          content:
            application/json:
              schema:
                type: object
                properties:
                  meta:
                    $ref: "#/components/schemas/CostOfLivingMeta"
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/CostOfLivingRow"
              example:
                meta:
                  title: "2026 Cost of Living Index"
                  source: "WhereNext (getwherenext.com)"
                  license: "CC BY 4.0"
                  updated: "2026-03-03"
                  countries: 95
                  csv: "https://getwherenext.com/api/data/cost-of-living/csv"
                data:
                  - rank: 1
                    country_code: "VN"
                    country: "Vietnam"
                    region: "Asia"
                    cost_index: 28
                    monthly_estimate_usd: 1024
                    grocery_index: 25
                    rent_index: 15
                    utilities_index: 30
                    transport_index: 20

  /api/data/cost-of-living/csv:
    get:
      operationId: getCostOfLivingCsv
      summary: "Cost of Living Index 2026 (CSV)"
      description: |
        CSV download of the Cost of Living Index.
        Columns: rank, country_code, country, region, cost_index, monthly_estimate_usd, grocery_index, rent_index, utilities_index, transport_index.
      tags: ["Cost of Living"]
      responses:
        "200":
          description: "CSV file download"
          headers:
            Content-Disposition:
              schema:
                type: string
                example: 'attachment; filename="wherenext-cost-of-living-2026.csv"'
          content:
            text/csv:
              schema:
                type: string

  /api/data/expat-tax-rates:
    get:
      operationId: getExpatTaxRates
      summary: "Expat Tax Rates by Country 2026"
      description: |
        Effective income tax + social contribution rates for expats across 20 popular relocation destinations.
        Rates calculated at three income tiers: $50,000, $100,000, and $200,000.
        Single filer, employment income, tax resident. Countries sorted by effective rate at $100K.
      tags: ["Tax"]
      responses:
        "200":
          description: "Tax rate data for 20 countries"
          content:
            application/json:
              schema:
                type: object
                properties:
                  dataset:
                    type: string
                    example: "expat-tax-rates-2026"
                  description:
                    type: string
                  methodology:
                    type: string
                  license:
                    type: string
                    example: "CC BY 4.0"
                  source:
                    type: string
                    format: uri
                  updated:
                    type: string
                    format: date
                  countries:
                    type: array
                    items:
                      $ref: "#/components/schemas/ExpatTaxCountry"
              example:
                dataset: "expat-tax-rates-2026"
                license: "CC BY 4.0"
                updated: "2026-03-31"
                countries:
                  - countryCode: "AE"
                    countryName: "United Arab Emirates"
                    rates:
                      - grossIncome: 50000
                        totalTax: 0
                        effectiveRate: 0
                        incomeTax: 0
                        socialContributions: 0

  /api/data/digital-nomad-visas:
    get:
      operationId: getDigitalNomadVisas
      summary: "Digital Nomad Visa Index 2026"
      description: |
        24 countries with active digital nomad visa programs.
        Includes cost, income requirements, duration, tax treatment, processing time, and key requirements.
      tags: ["Visa"]
      responses:
        "200":
          description: "Digital nomad visa programs"
          content:
            application/json:
              schema:
                type: object
                properties:
                  dataset:
                    type: string
                    example: "digital-nomad-visas-2026"
                  description:
                    type: string
                  license:
                    type: string
                    example: "CC BY 4.0"
                  updated:
                    type: string
                    format: date
                  count:
                    type: integer
                  visas:
                    type: array
                    items:
                      $ref: "#/components/schemas/DigitalNomadVisa"
              example:
                dataset: "digital-nomad-visas-2026"
                license: "CC BY 4.0"
                count: 25
                visas:
                  - countryCode: "PT"
                    countryName: "Portugal"
                    visaName: "Digital Nomad Visa (D8)"
                    region: "Europe"
                    durationMonths: 12
                    renewable: true
                    maxStayMonths: null
                    costUsd: 83
                    minIncomeUsdYear: 42120
                    processingDays: 60
                    taxTreatment: "taxed-if-resident"
                    remoteWorkAllowed: true

  /api/data/property-costs:
    get:
      operationId: getPropertyCosts
      summary: "Property Buying Costs by Country 2026"
      description: |
        Property buying costs, taxes, mortgage access, rental yields, and capital gains rates
        for foreign buyers across 19 popular relocation destinations.
        Supports optional `?format=csv` query parameter for CSV download.
      tags: ["Property"]
      parameters:
        - name: format
          in: query
          required: false
          description: "Set to 'csv' for CSV download"
          schema:
            type: string
            enum: ["csv"]
      responses:
        "200":
          description: "Property cost data (JSON or CSV depending on format param)"
          content:
            application/json:
              schema:
                type: object
                properties:
                  dataset:
                    type: string
                    example: "property-costs-2026"
                  description:
                    type: string
                  license:
                    type: string
                    example: "CC BY 4.0"
                  updated:
                    type: string
                    format: date
                  countryCount:
                    type: integer
                  countries:
                    type: array
                    items:
                      $ref: "#/components/schemas/PropertyCostCountry"
              example:
                dataset: "property-costs-2026"
                license: "CC BY 4.0"
                updated: "2026-04-03"
                countryCount: 19
                countries:
                  - countryCode: "PT"
                    countryName: "Portugal"
                    foreignOwnership: "unrestricted"
                    closingCostMinPct: 7
                    closingCostMaxPct: 12
                    mortgageAvailable: true
                    avgGrossYieldPct: 4.5
                    avgPricePerSqmUsd: 3200
                    residencyViaProperty: true
            text/csv:
              schema:
                type: string

  /api/data/visa-requirements:
    get:
      operationId: getVisaRequirements
      summary: "Visa Requirements & Passport Power Index 2026"
      description: |
        Without `passport` param: returns passport power summary for all 95 nationalities.
        With `?passport=XX`: returns visa requirements for all 95 destinations from that passport (ISO 3166-1 alpha-2).
        Access levels: freedom-of-movement, visa-free, visa-on-arrival, e-visa, visa-required.
      tags: ["Visa"]
      parameters:
        - name: passport
          in: query
          required: false
          description: "ISO 3166-1 alpha-2 country code (e.g., US, GB, DE). If omitted, returns passport power summary for all nationalities."
          schema:
            type: string
            pattern: "^[A-Za-z]{2,3}$"
            example: "US"
      responses:
        "200":
          description: "Visa requirements or passport power index"
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/VisaRequirementsPerPassport"
                  - $ref: "#/components/schemas/PassportPowerIndex"
              examples:
                perPassport:
                  summary: "Per-passport visa requirements (?passport=US)"
                  value:
                    metadata:
                      title: "Visa Requirements for US Passport Holders (2026)"
                      license: "CC BY 4.0"
                      passport_code: "us"
                      total_destinations: 95
                    summary:
                      visa_free: 62
                      visa_on_arrival: 12
                      e_visa: 8
                      visa_required: 13
                      digital_nomad_visas: 18
                    data:
                      - destination_code: "pt"
                        destination: "Portugal"
                        access_level: "visa-free"
                        max_stay_days: 90
                        work_allowed: false
                        digital_nomad_visa: true
                        path_to_residency: true
                passportPower:
                  summary: "Passport power index (no param)"
                  value:
                    metadata:
                      title: "Passport Power Index 2026"
                      license: "CC BY 4.0"
                      total_passports: 95
                    data:
                      - passport_code: "de"
                        country: "Germany"
                        visa_free_destinations: 72
                        total_destinations: 95
                        passport_power_rank: 1

  /api/data/visa-requirements/csv:
    get:
      operationId: getVisaRequirementsCsv
      summary: "Passport Power Index 2026 (CSV)"
      description: |
        CSV download of passport power summary for all 95 nationalities.
        Columns: rank, passport_code, country, visa_free, visa_on_arrival, e_visa, visa_required, digital_nomad_visas, total_destinations.
      tags: ["Visa"]
      responses:
        "200":
          description: "CSV file download"
          headers:
            Content-Disposition:
              schema:
                type: string
                example: 'attachment; filename="wherenext-passport-power-2026.csv"'
          content:
            text/csv:
              schema:
                type: string

  /api/data/school-costs:
    get:
      operationId: getSchoolCosts
      summary: "International School Costs by City"
      description: |
        Without `city` param: returns per-city school cost summaries for all 342 cities with schools.
        With `?city=dubai`: returns aggregated cost summary for a specific city (no per-school data exposed).
        Queries the school database directly (4,149 schools across 78 countries).
      tags: ["Schools"]
      parameters:
        - name: city
          in: query
          required: false
          description: "City slug (e.g., dubai, lisbon, bangkok, ho-chi-minh). If omitted, returns all cities."
          schema:
            type: string
            example: "dubai"
      responses:
        "200":
          description: "School cost summaries"
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/SchoolCostsAllCities"
                  - $ref: "#/components/schemas/SchoolCostsSingleCity"
              examples:
                allCities:
                  summary: "All cities (no param)"
                  value:
                    metadata:
                      title: "International School Costs by City (2026)"
                      license: "CC BY 4.0"
                      total_cities: 333
                      total_schools: 4149
                    data:
                      - city: "dubai"
                        city_name: "Dubai"
                        country: "United Arab Emirates"
                        country_code: "ae"
                        total_schools: 210
                        tuition_min_usd: 2500
                        tuition_max_usd: 45000
                        tuition_median_usd: 12000
                        curricula: ["American", "British", "IB"]
                singleCity:
                  summary: "Single city (?city=dubai)"
                  value:
                    metadata:
                      title: "International School Costs in Dubai, United Arab Emirates (2026)"
                      license: "CC BY 4.0"
                      city: "dubai"
                      country_code: "ae"
                    summary:
                      total_schools: 210
                      curricula: ["American", "British", "IB", "French"]
                      tuition_range_usd:
                        min: 2500
                        max: 45000
                        median: 12000
                      with_learning_support: 85
        "404":
          description: "City not found"
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string

  # Note: /api/data/school-costs/csv was removed (HTTP 410 Gone). It is
  # intentionally not documented here. Use /api/data/school-costs-index
  # with ?format=csv instead.

  /api/data/school-costs-index:
    get:
      operationId: getSchoolCostsIndex
      summary: "International School Cost Index 2026"
      description: |
        City-level school cost rankings with percentile distributions (P10, P25, median, P75, P90).
        Returns top 30 most expensive and top 30 most affordable cities. Full rankings on the data page.
        Supports `?format=csv` for CSV download.
        Minimum 3 schools per city to be included.
      tags: ["Schools"]
      parameters:
        - name: format
          in: query
          required: false
          description: "Set to 'csv' for CSV download"
          schema:
            type: string
            enum: ["csv"]
      responses:
        "200":
          description: "School cost index with city rankings"
          content:
            application/json:
              schema:
                type: object
                properties:
                  metadata:
                    type: object
                    properties:
                      title:
                        type: string
                      source:
                        type: string
                      license:
                        type: string
                      updated:
                        type: string
                        format: date
                      total_schools_with_fees:
                        type: integer
                      total_cities_ranked:
                        type: integer
                      total_countries:
                        type: integer
                      global_median_tuition_usd:
                        type: integer
                      global_p10_tuition_usd:
                        type: integer
                      global_p90_tuition_usd:
                        type: integer
                  top_expensive:
                    type: array
                    items:
                      $ref: "#/components/schemas/SchoolCostIndexCity"
                  top_affordable:
                    type: array
                    items:
                      $ref: "#/components/schemas/SchoolCostIndexCity"
              example:
                metadata:
                  title: "International School Cost Index 2026"
                  license: "CC BY 4.0"
                  total_schools_with_fees: 2900
                  total_cities_ranked: 149
                  global_median_tuition_usd: 12000
                top_expensive:
                  - rank: 1
                    city_slug: "zurich"
                    city_name: "Zurich"
                    country: "Switzerland"
                    country_code: "ch"
                    school_count: 15
                    median_tuition_usd: 32000
                    p25_tuition_usd: 25000
                    p75_tuition_usd: 40000
                    curricula: ["IB", "German", "British"]
            text/csv:
              schema:
                type: string

  /api/data/school-affordability:
    get:
      operationId: getSchoolAffordability
      summary: "International School Affordability Map 2026"
      description: |
        Schools-per-budget tiers across 200+ cities.
        Returns global bracket distribution (under $5K, $5-10K, $10-15K, $15-25K, $25-40K, over $40K)
        plus top 30 most affordable cities at $15K and $25K budget tiers.
        Uses WhereNext's true annual cost (including hidden fees) when available.
      tags: ["Schools"]
      responses:
        "200":
          description: "School affordability data"
          content:
            application/json:
              schema:
                type: object
                properties:
                  metadata:
                    type: object
                    properties:
                      title:
                        type: string
                      license:
                        type: string
                      total_schools_analyzed:
                        type: integer
                      total_cities:
                        type: integer
                      total_countries:
                        type: integer
                      schools_under_10k:
                        type: integer
                      schools_under_15k:
                        type: integer
                      schools_under_25k:
                        type: integer
                  bracket_distribution:
                    type: array
                    items:
                      $ref: "#/components/schemas/BudgetBracket"
                  top_cities_under_15k:
                    type: array
                    items:
                      $ref: "#/components/schemas/AffordableCity"
                  top_cities_under_25k:
                    type: array
                    items:
                      $ref: "#/components/schemas/AffordableCity"
              example:
                metadata:
                  title: "International School Affordability Map 2026"
                  license: "CC BY 4.0"
                  total_schools_analyzed: 2900
                  schools_under_10k: 850
                bracket_distribution:
                  - bracket: "Under $5,000/year"
                    bracket_key: "under_5k"
                    school_count: 320
                    city_count: 80
                    country_count: 30
                    pct_of_total: 11

  /api/data/school-class-sizes:
    get:
      operationId: getSchoolClassSizes
      summary: "International School Class Sizes 2026"
      description: |
        Average class sizes for international schools across 250+ cities.
        Returns top 30 cities with the smallest median class sizes.
        Minimum 3 schools per city with reported class sizes to be included.
      tags: ["Schools"]
      responses:
        "200":
          description: "Class size data by city"
          content:
            application/json:
              schema:
                type: object
                properties:
                  metadata:
                    type: object
                    properties:
                      title:
                        type: string
                      license:
                        type: string
                      total_schools_with_class_size:
                        type: integer
                      total_cities_ranked:
                        type: integer
                      total_countries:
                        type: integer
                      global_median_class_size:
                        type: integer
                      global_min_class_size:
                        type: integer
                      global_max_class_size:
                        type: integer
                  top_smallest_class_cities:
                    type: array
                    items:
                      $ref: "#/components/schemas/ClassSizeCity"
              example:
                metadata:
                  title: "International School Class Sizes 2026"
                  license: "CC BY 4.0"
                  global_median_class_size: 20
                top_smallest_class_cities:
                  - rank: 1
                    city_slug: "vienna"
                    city_name: "Vienna"
                    country: "Austria"
                    country_code: "at"
                    school_count: 12
                    median_class_size: 15
                    smallest_class_size: 8
                    largest_class_size: 22

  /api/data/school-diversity:
    get:
      operationId: getSchoolDiversity
      summary: "International School Diversity Index 2026"
      description: |
        Student nationality counts and diversity scores for international schools across 200+ cities.
        Returns top 30 most diverse cities by median number of nationalities per school.
        Minimum 3 schools per city with nationality data to be included.
      tags: ["Schools"]
      responses:
        "200":
          description: "Diversity data by city"
          content:
            application/json:
              schema:
                type: object
                properties:
                  metadata:
                    type: object
                    properties:
                      title:
                        type: string
                      license:
                        type: string
                      total_schools_with_nationality_data:
                        type: integer
                      total_cities_ranked:
                        type: integer
                      total_countries:
                        type: integer
                      global_median_nationalities:
                        type: integer
                      global_max_nationalities:
                        type: integer
                  top_diverse_cities:
                    type: array
                    items:
                      $ref: "#/components/schemas/DiverseCity"
              example:
                metadata:
                  title: "International School Diversity Index 2026"
                  license: "CC BY 4.0"
                  global_median_nationalities: 45
                top_diverse_cities:
                  - rank: 1
                    city_slug: "dubai"
                    city_name: "Dubai"
                    country: "United Arab Emirates"
                    country_code: "ae"
                    school_count: 180
                    median_nationalities: 72
                    max_nationalities: 105
                    median_diversity_score: 85

  /api/data/city-prices:
    get:
      operationId: getCityPrices
      summary: "City-Level Item Prices 2026"
      description: |
        Without `city` param: returns list of available cities with summary stats.
        With `?city=PT-Lisbon`: returns item-level prices (bread, milk, rent, transport, etc.) in USD and local currency.
        City keys use the format `{country_code}-{city_name}` (e.g., PT-Lisbon, TH-Bangkok).
        50+ cities with detailed price data.
      tags: ["Cost of Living"]
      parameters:
        - name: city
          in: query
          required: false
          description: "City key in format `{country_code}-{city_name}` (e.g., PT-Lisbon, TH-Bangkok). If omitted, returns available cities."
          schema:
            type: string
            example: "PT-Lisbon"
      responses:
        "200":
          description: "City price data"
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/CityPricesDetail"
                  - $ref: "#/components/schemas/CityPricesList"
              examples:
                cityDetail:
                  summary: "Item-level prices (?city=PT-Lisbon)"
                  value:
                    metadata:
                      title: "Item-Level Prices in Lisbon (2026)"
                      license: "CC BY 4.0"
                      city: "PT-Lisbon"
                      currency: "EUR"
                      exchange_rate: 0.92
                    data:
                      - category: "Groceries"
                        item: "Bread (500g loaf)"
                        price_usd: 1.52
                        price_local: 1.40
                      - category: "Transport"
                        item: "Monthly pass"
                        price_usd: 43.48
                        price_local: 40.00
                cityList:
                  summary: "Available cities (no param)"
                  value:
                    metadata:
                      title: "City-Level Cost of Living Prices (2026)"
                      license: "CC BY 4.0"
                      total_cities: 50
                    data:
                      - city_key: "PT-Lisbon"
                        city_name: "Lisbon"
                        country_code: "pt"
                        currency: "EUR"
                        item_count: 48
                        categories: 14
        "404":
          description: "City not found"
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                  available:
                    type: array
                    items:
                      type: string

  /api/data/ai-comparison:
    get:
      operationId: getAiComparison
      summary: "AI-Optimized Country Comparison"
      description: |
        Structured comparison data designed for AI/LLM consumption.
        Returns JSON-LD (Schema.org Dataset) with per-dimension scores, global rankings,
        monthly cost estimates, dimension winners, natural-language summary, methodology,
        and full source attribution. Content-Type is application/ld+json.
      tags: ["AI-Optimized"]
      parameters:
        - name: a
          in: query
          required: true
          description: "First country code (ISO 3166-1 alpha-2, e.g., PT)"
          schema:
            type: string
            pattern: "^[A-Za-z]{2}$"
            example: "PT"
        - name: b
          in: query
          required: true
          description: "Second country code (ISO 3166-1 alpha-2, e.g., ES)"
          schema:
            type: string
            pattern: "^[A-Za-z]{2}$"
            example: "ES"
      responses:
        "200":
          description: "Structured comparison with JSON-LD markup"
          content:
            application/ld+json:
              schema:
                $ref: "#/components/schemas/AiComparisonResponse"
              example:
                "@context": "https://schema.org"
                "@type": "Dataset"
                name: "Portugal vs Spain: Relocation Comparison Data (2026)"
                license: "https://creativecommons.org/licenses/by/4.0/"
                comparison:
                  countryA:
                    name: "Portugal"
                    code: "PT"
                    monthlyEstimateUsd: 1680
                    overallScore: 68
                    globalRank: 12
                    scores:
                      cost: 72
                      safety: 78
                      healthcare: 65
                  countryB:
                    name: "Spain"
                    code: "ES"
                    monthlyEstimateUsd: 2100
                    overallScore: 71
                    globalRank: 8
                  dimensionWins:
                    PT: 3
                    ES: 3
                    tied: 1
                  dimensions:
                    - name: "Cost of Living"
                      dimension: "cost"
                      winner: "PT"
                      scoreA: 72
                      scoreB: 58
                      insight: "Portugal is approximately 19% more affordable for expats."
                  summary: "Spain scores higher overall (71 vs 68). Portugal has advantages in cost of living."
                sources: ["World Bank", "OECD", "WHO"]
        "400":
          description: "Missing or invalid parameters"
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                  example:
                    type: string
        "404":
          description: "Country code not found"
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                  available:
                    type: array
                    items:
                      type: string

  /api/data/school-intelligence/{city}:
    get:
      operationId: getSchoolIntelligence
      summary: "Per-City School Cost Intelligence"
      description: |
        Deep intelligence report for a specific city's international schools.
        Includes stated tuition vs true annual cost (with hidden fee analysis), budget tiers,
        best-value schools, most affordable schools, curriculum distribution, regional peer
        comparison, global median comparison, and a natural-language executive summary.
        Returns JSON-LD with Schema.org Dataset markup.
      tags: ["Schools", "AI-Optimized"]
      parameters:
        - name: city
          in: path
          required: true
          description: "City slug (e.g., dubai, lisbon, bangkok, ho-chi-minh)"
          schema:
            type: string
            example: "dubai"
      responses:
        "200":
          description: "School intelligence report for the city"
          content:
            application/json:
              schema:
                type: object
                properties:
                  jsonLd:
                    type: object
                    description: "Schema.org Dataset markup"
                  city:
                    type: object
                    properties:
                      name:
                        type: string
                      slug:
                        type: string
                      country:
                        type: string
                      country_code:
                        type: string
                      region:
                        type: string
                      total_schools:
                        type: integer
                      browse_url:
                        type: string
                        format: uri
                  cost_overview:
                    type: object
                    properties:
                      median_stated_tuition_usd:
                        type: integer
                      median_true_annual_cost_usd:
                        type: integer
                      median_hidden_fee_pct:
                        type: integer
                      cheapest_true_cost_usd:
                        type: integer
                        nullable: true
                      most_expensive_true_cost_usd:
                        type: integer
                        nullable: true
                  budget_tiers:
                    type: array
                    items:
                      type: object
                      properties:
                        budget_tier:
                          type: string
                        school_count:
                          type: integer
                  best_value_schools:
                    type: array
                    items:
                      type: object
                      properties:
                        name:
                          type: string
                        slug:
                          type: string
                        value_score:
                          type: number
                          nullable: true
                        true_annual_cost_usd:
                          type: integer
                          nullable: true
                        curriculum:
                          type: array
                          items:
                            type: string
                        browse_url:
                          type: string
                          format: uri
                  most_affordable_schools:
                    type: array
                    items:
                      type: object
                      properties:
                        name:
                          type: string
                        slug:
                          type: string
                        true_annual_cost_usd:
                          type: integer
                        curriculum:
                          type: array
                          items:
                            type: string
                        browse_url:
                          type: string
                          format: uri
                  curriculum_distribution:
                    type: array
                    items:
                      type: object
                      properties:
                        curriculum:
                          type: string
                        school_count:
                          type: integer
                        percentage:
                          type: integer
                  comparison:
                    type: object
                    properties:
                      vs_global:
                        type: object
                        nullable: true
                        properties:
                          global_median_true_cost_usd:
                            type: integer
                          city_vs_global_pct:
                            type: integer
                          label:
                            type: string
                      regional_peers:
                        type: array
                        items:
                          type: object
                          properties:
                            city:
                              type: string
                            country:
                              type: string
                            median_true_cost_usd:
                              type: integer
                            school_count:
                              type: integer
                  executive_summary:
                    type: string
                    description: "Natural-language summary of the city's school landscape"
                  metadata:
                    type: object
                    properties:
                      source:
                        type: string
                      license:
                        type: string
                      updated:
                        type: string
                        format: date
                      methodology:
                        type: string
                        format: uri
                      cite_as:
                        type: string
              example:
                city:
                  name: "Dubai"
                  slug: "dubai"
                  country: "United Arab Emirates"
                  country_code: "AE"
                  total_schools: 180
                cost_overview:
                  median_stated_tuition_usd: 12000
                  median_true_annual_cost_usd: 14500
                  median_hidden_fee_pct: 21
                executive_summary: "International schools in Dubai cost a median $12,000/year in stated tuition but the true annual cost including registration, assessment, transport, and activity fees averages $14,500 -- a 21% hidden fee markup."
        "404":
          description: "City not found"
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string

  /api/data/ai-cost-of-living/{code}:
    get:
      operationId: getAiCostOfLiving
      summary: "AI-Optimized Cost of Living by Country"
      description: |
        Structured cost of living data for a single country, optimized for AI/LLM consumption.
        Returns country-level cost breakdown with city comparisons, budget estimates,
        and source attribution. Designed for AI assistants answering "how much does it cost to live in X?"
      tags: ["AI-Optimized", "Cost of Living"]
      parameters:
        - name: code
          in: path
          required: true
          description: "ISO 3166-1 alpha-2 country code (e.g., PT, ES, TH)"
          schema:
            type: string
            pattern: "^[A-Za-z]{2}$"
            example: "PT"
      responses:
        "200":
          description: "Cost of living data for the specified country"
          content:
            application/json:
              schema:
                type: object
                properties:
                  country:
                    type: string
                  country_code:
                    type: string
                  cost_index:
                    type: number
                  monthly_estimate_usd:
                    type: number
                  sub_indexes:
                    type: object
                    properties:
                      grocery:
                        type: number
                      rent:
                        type: number
                      utilities:
                        type: number
                      transport:
                        type: number
                  license:
                    type: string
                    example: "CC BY 4.0"
        "404":
          description: "Country not found"

  /api/data/ai-tax/{code}:
    get:
      operationId: getAiTax
      summary: "AI-Optimized Tax Data by Country"
      description: |
        Tax rate calculations for a single country at a specified income level.
        Optimized for AI/LLM consumption. Returns effective tax rate, income tax,
        social contributions, and any applicable special regime information.
      tags: ["AI-Optimized", "Tax"]
      parameters:
        - name: code
          in: path
          required: true
          description: "ISO 3166-1 alpha-2 country code (e.g., PT, ES, AE)"
          schema:
            type: string
            pattern: "^[A-Za-z]{2}$"
            example: "PT"
        - name: income
          in: query
          required: false
          description: "Annual gross income in USD (default: 100000)"
          schema:
            type: integer
            example: 100000
      responses:
        "200":
          description: "Tax data for the specified country and income"
          content:
            application/json:
              schema:
                type: object
                properties:
                  country:
                    type: string
                  country_code:
                    type: string
                  gross_income:
                    type: integer
                  effective_rate:
                    type: number
                  income_tax:
                    type: number
                  social_contributions:
                    type: number
                  total_tax:
                    type: number
                  special_regimes:
                    type: array
                    items:
                      type: string
                  license:
                    type: string
                    example: "CC BY 4.0"
        "404":
          description: "Country not found"

  /api/data/ai-visa:
    get:
      operationId: getAiVisa
      summary: "AI-Optimized Visa Requirements"
      description: |
        Visa requirements for a specific passport-destination pair.
        Optimized for AI/LLM consumption. Returns access level, stay duration,
        work rights, digital nomad visa availability, and path to residency.
      tags: ["AI-Optimized", "Visa"]
      parameters:
        - name: passport
          in: query
          required: true
          description: "Passport country code (ISO 3166-1 alpha-2)"
          schema:
            type: string
            pattern: "^[A-Za-z]{2}$"
            example: "US"
        - name: destination
          in: query
          required: true
          description: "Destination country code (ISO 3166-1 alpha-2)"
          schema:
            type: string
            pattern: "^[A-Za-z]{2}$"
            example: "PT"
      responses:
        "200":
          description: "Visa requirements for the passport-destination pair"
          content:
            application/json:
              schema:
                type: object
                properties:
                  passport:
                    type: string
                  destination:
                    type: string
                  access_level:
                    type: string
                    enum: ["freedom-of-movement", "visa-free", "visa-on-arrival", "e-visa", "visa-required"]
                  max_stay_days:
                    type: integer
                    nullable: true
                  work_allowed:
                    type: boolean
                  digital_nomad_visa:
                    type: boolean
                  path_to_residency:
                    type: boolean
                  notes:
                    type: string
                    nullable: true
                  license:
                    type: string
                    example: "CC BY 4.0"
        "400":
          description: "Missing parameters"
        "404":
          description: "Passport or destination not found"

  /api/data/product-feed:
    get:
      operationId: getProductFeed
      summary: "Google Shopping Product Feed"
      description: |
        Google Merchant Center product feed in RSS 2.0 / Google Shopping format.
        Lists all premium WhereNext report products (excluding bundles) for Google Shopping surfaces.
        Content-Type is application/xml.
      tags: ["Product Feed"]
      responses:
        "200":
          description: "RSS 2.0 product feed with Google Shopping namespace"
          content:
            application/xml:
              schema:
                type: string
              example: |
                <?xml version="1.0" encoding="UTF-8"?>
                <rss version="2.0" xmlns:g="http://base.google.com/ns/1.0">
                  <channel>
                    <title>WhereNext Premium Reports</title>
                    <item>
                      <g:id>wherenext-decision-brief</g:id>
                      <title>Decision Brief</title>
                      <g:price>29.00 USD</g:price>
                      <g:availability>in_stock</g:availability>
                    </item>
                  </channel>
                </rss>

  /api/data/chinese-outbound-relocation-index:
    get:
      operationId: getChineseOutboundRelocationIndex
      summary: "Chinese Outbound Relocation Index 2026"
      description: |
        Ranking of destinations for mainland Chinese and Hong Kong emigrants.
        Weighted for Chinese-origin priorities (education 90, safety 85, healthcare 80, infrastructure 80) and annotated with 2025-2026 visa/policy flags (MM2H relaunch, LTR ease, UAE Golden Visa, Japan Business Manager reform, Singapore GIP S$50M, Canada/Australia foreign-buyer bans, US EB-5 China final action September 22, 2016 per May 2026 Visa Bulletin).
        Pass ?limit=95 to retrieve the full ranked list; default returns top 30.
        License: CC BY 4.0 — free to cite with attribution to WhereNext.
      tags: ["China Outbound"]
      parameters:
        - name: limit
          in: query
          required: false
          description: "Number of top-ranked destinations to return (1-95)."
          schema:
            type: integer
            minimum: 1
            maximum: 95
            default: 30
        - name: format
          in: query
          required: false
          description: "Response format. Omit for JSON; pass 'csv' for CSV export."
          schema:
            type: string
            enum: ["csv"]
      responses:
        "200":
          description: "JSON envelope with methodology, weight profile, and ranked rows"
          content:
            application/json:
              schema:
                type: object
                properties:
                  dataset:
                    type: string
                    example: "chinese-outbound-relocation-index-2026"
                  description:
                    type: string
                  methodology:
                    type: string
                  weightProfile:
                    type: object
                  license:
                    type: string
                    example: "CC BY 4.0"
                  attribution:
                    type: string
                  source:
                    type: string
                    format: uri
                  updated:
                    type: string
                    format: date
                  limit:
                    type: integer
                  maxLimit:
                    type: integer
                  totalCountriesAnalysed:
                    type: integer
                  count:
                    type: integer
                  rows:
                    type: array
                    items:
                      type: object
                      properties:
                        rank:
                          type: integer
                        code:
                          type: string
                        name:
                          type: string
                        score:
                          type: number
                        monthlyCostUsd:
                          type: integer
                        topStrengths:
                          type: array
                          items:
                            type: string
                        visaRoute:
                          type: string
                        policy:
                          type: object
                          nullable: true

components:
  schemas:
    RelocationIndexMeta:
      type: object
      properties:
        title:
          type: string
        version:
          type: string
        source:
          type: string
        license:
          type: string
        updated:
          type: string
          format: date
        releases:
          type: string
          format: uri
        countries:
          type: integer
        dimensions:
          type: array
          items:
            type: string
        methodology:
          type: string
          format: uri
        report:
          type: string
          format: uri
        csv:
          type: string
          format: uri

    RelocationIndexRow:
      type: object
      properties:
        rank:
          type: integer
          description: "Global rank (1 = best)"
        country_code:
          type: string
          description: "ISO 3166-1 alpha-2 (lowercase)"
        country:
          type: string
        region:
          type: string
          enum: ["Europe", "Asia", "Americas", "Africa", "Middle East", "Oceania", "Other"]
        composite_score:
          type: integer
          description: "Overall score 0-100"
        cost:
          type: integer
          description: "Cost of living score (higher = more affordable)"
        safety:
          type: integer
        healthcare:
          type: integer
        education:
          type: integer
        career:
          type: integer
        lifestyle:
          type: integer
        infrastructure:
          type: integer
        confidence:
          type: number
          description: "Data confidence score 0-1"

    CostOfLivingMeta:
      type: object
      properties:
        title:
          type: string
        version:
          type: string
        source:
          type: string
        license:
          type: string
        updated:
          type: string
          format: date
        countries:
          type: integer
        methodology:
          type: string
          format: uri
        csv:
          type: string
          format: uri

    CostOfLivingRow:
      type: object
      properties:
        rank:
          type: integer
        country_code:
          type: string
        country:
          type: string
        region:
          type: string
        cost_index:
          type: number
          description: "Overall cost index (US = 82)"
        monthly_estimate_usd:
          type: number
          description: "Estimated monthly cost for a single person"
        grocery_index:
          type: number
        rent_index:
          type: number
        utilities_index:
          type: number
        transport_index:
          type: number

    ExpatTaxCountry:
      type: object
      properties:
        countryCode:
          type: string
        countryName:
          type: string
        rates:
          type: array
          items:
            type: object
            properties:
              grossIncome:
                type: integer
                enum: [50000, 100000, 200000]
              totalTax:
                type: number
              effectiveRate:
                type: number
                description: "Effective tax rate as decimal (e.g., 0.285 = 28.5%)"
              incomeTax:
                type: number
              socialContributions:
                type: number

    DigitalNomadVisa:
      type: object
      properties:
        countryCode:
          type: string
        countryName:
          type: string
        visaName:
          type: string
        region:
          type: string
        durationMonths:
          type: integer
        renewable:
          type: boolean
        maxStayMonths:
          type: integer
          nullable: true
        costUsd:
          type: number
        minIncomeUsdYear:
          type: number
        processingDays:
          type: integer
        taxTreatment:
          type: string
          description: "e.g., 'tax-exempt', 'taxed-if-resident', 'flat-rate'"
        taxNote:
          type: string
        keyRequirement:
          type: string
        remoteWorkAllowed:
          type: boolean

    PropertyCostCountry:
      type: object
      properties:
        countryCode:
          type: string
        countryName:
          type: string
        foreignOwnership:
          type: string
          description: "e.g., 'unrestricted', 'restricted', 'leasehold-only'"
        restrictionNote:
          type: string
          nullable: true
        closingCostMinPct:
          type: number
        closingCostMaxPct:
          type: number
        annualTaxMinPct:
          type: number
        annualTaxMaxPct:
          type: number
        annualTaxNote:
          type: string
          nullable: true
        mortgageAvailable:
          type: boolean
        mortgageLtvMinPct:
          type: number
        mortgageLtvMaxPct:
          type: number
        mortgageRateMinPct:
          type: number
        mortgageRateMaxPct:
          type: number
        avgGrossYieldPct:
          type: number
        rentalTaxRatePct:
          type: number
        rentalTaxNote:
          type: string
          nullable: true
        capitalGainsTaxRatePct:
          type: number
        capitalGainsTaxNote:
          type: string
          nullable: true
        avgPricePerSqmUsd:
          type: number
        residencyViaProperty:
          type: boolean
        residencyMinInvestmentUsd:
          type: number
          nullable: true
        residencyNote:
          type: string
          nullable: true
        currency:
          type: string
        lastVerified:
          type: string
          format: date

    VisaRequirementsPerPassport:
      type: object
      properties:
        metadata:
          type: object
          properties:
            title:
              type: string
            version:
              type: string
            source:
              type: string
            license:
              type: string
            updated:
              type: string
              format: date
            passport_code:
              type: string
            total_destinations:
              type: integer
        summary:
          type: object
          properties:
            visa_free:
              type: integer
            visa_on_arrival:
              type: integer
            e_visa:
              type: integer
            visa_required:
              type: integer
            digital_nomad_visas:
              type: integer
        data:
          type: array
          items:
            type: object
            properties:
              destination_code:
                type: string
              destination:
                type: string
              access_level:
                type: string
                enum: ["freedom-of-movement", "visa-free", "visa-on-arrival", "e-visa", "visa-required"]
              max_stay_days:
                type: integer
                nullable: true
              work_allowed:
                type: boolean
              digital_nomad_visa:
                type: boolean
              path_to_residency:
                type: boolean
              notes:
                type: string
                nullable: true

    PassportPowerIndex:
      type: object
      properties:
        metadata:
          type: object
          properties:
            title:
              type: string
            version:
              type: string
            source:
              type: string
            license:
              type: string
            updated:
              type: string
              format: date
            total_passports:
              type: integer
        data:
          type: array
          items:
            type: object
            properties:
              passport_code:
                type: string
              country:
                type: string
              visa_free_destinations:
                type: integer
              total_destinations:
                type: integer
              passport_power_rank:
                type: integer

    SchoolCostsAllCities:
      type: object
      properties:
        metadata:
          type: object
          properties:
            title:
              type: string
            version:
              type: string
            source:
              type: string
            license:
              type: string
            updated:
              type: string
              format: date
            total_cities:
              type: integer
            total_schools:
              type: integer
        data:
          type: array
          items:
            type: object
            properties:
              city:
                type: string
              city_name:
                type: string
              country:
                type: string
              country_code:
                type: string
              total_schools:
                type: integer
              tuition_min_usd:
                type: number
                nullable: true
              tuition_max_usd:
                type: number
                nullable: true
              tuition_median_usd:
                type: number
                nullable: true
              curricula:
                type: array
                items:
                  type: string

    SchoolCostsSingleCity:
      type: object
      properties:
        metadata:
          type: object
          properties:
            title:
              type: string
            version:
              type: string
            source:
              type: string
            license:
              type: string
            updated:
              type: string
              format: date
            city:
              type: string
            country_code:
              type: string
            disclaimer:
              type: string
        summary:
          type: object
          properties:
            total_schools:
              type: integer
            curricula:
              type: array
              items:
                type: string
            tuition_range_usd:
              type: object
              nullable: true
              properties:
                min:
                  type: number
                max:
                  type: number
                median:
                  type: number
            with_learning_support:
              type: integer

    SchoolCostIndexCity:
      type: object
      properties:
        rank:
          type: integer
        city_slug:
          type: string
        city_name:
          type: string
        country:
          type: string
        country_code:
          type: string
        school_count:
          type: integer
        median_tuition_usd:
          type: integer
        p25_tuition_usd:
          type: integer
        p75_tuition_usd:
          type: integer
        p10_tuition_usd:
          type: integer
        p90_tuition_usd:
          type: integer
        curricula:
          type: array
          items:
            type: string
        browse_url:
          type: string
          format: uri

    BudgetBracket:
      type: object
      properties:
        bracket:
          type: string
          description: "e.g., 'Under $5,000/year'"
        bracket_key:
          type: string
          description: "e.g., 'under_5k'"
        school_count:
          type: integer
        city_count:
          type: integer
        country_count:
          type: integer
        pct_of_total:
          type: integer

    AffordableCity:
      type: object
      properties:
        rank:
          type: integer
        city_slug:
          type: string
        city_name:
          type: string
        country:
          type: string
        country_code:
          type: string
        total_schools:
          type: integer
        schools_under_budget:
          type: integer
        coverage_pct:
          type: integer
          description: "Percentage of city's schools under the budget threshold"
        browse_url:
          type: string
          format: uri

    ClassSizeCity:
      type: object
      properties:
        rank:
          type: integer
        city_slug:
          type: string
        city_name:
          type: string
        country:
          type: string
        country_code:
          type: string
        school_count:
          type: integer
        median_class_size:
          type: integer
        smallest_class_size:
          type: integer
        largest_class_size:
          type: integer
        browse_url:
          type: string
          format: uri

    DiverseCity:
      type: object
      properties:
        rank:
          type: integer
        city_slug:
          type: string
        city_name:
          type: string
        country:
          type: string
        country_code:
          type: string
        school_count:
          type: integer
        median_nationalities:
          type: integer
        max_nationalities:
          type: integer
        median_diversity_score:
          type: integer
        browse_url:
          type: string
          format: uri

    CityPricesDetail:
      type: object
      properties:
        metadata:
          type: object
          properties:
            title:
              type: string
            version:
              type: string
            source:
              type: string
            license:
              type: string
            updated:
              type: string
              format: date
            city:
              type: string
            currency:
              type: string
            exchange_rate:
              type: number
            data_source:
              type: string
        data:
          type: array
          items:
            type: object
            properties:
              category:
                type: string
              item:
                type: string
              price_usd:
                type: number
              price_local:
                type: number
                nullable: true

    CityPricesList:
      type: object
      properties:
        metadata:
          type: object
          properties:
            title:
              type: string
            version:
              type: string
            source:
              type: string
            license:
              type: string
            updated:
              type: string
              format: date
            total_cities:
              type: integer
        data:
          type: array
          items:
            type: object
            properties:
              city_key:
                type: string
              city_name:
                type: string
              country_code:
                type: string
              currency:
                type: string
              item_count:
                type: integer
              categories:
                type: integer

    AiComparisonResponse:
      type: object
      properties:
        "@context":
          type: string
          const: "https://schema.org"
        "@type":
          type: string
          const: "Dataset"
        name:
          type: string
        description:
          type: string
        license:
          type: string
          format: uri
        dateModified:
          type: string
          format: date
        isAccessibleForFree:
          type: boolean
        comparison:
          type: object
          properties:
            countryA:
              $ref: "#/components/schemas/ComparisonCountry"
            countryB:
              $ref: "#/components/schemas/ComparisonCountry"
            dimensionWins:
              type: object
              additionalProperties:
                type: integer
            dimensions:
              type: array
              items:
                type: object
                properties:
                  name:
                    type: string
                  dimension:
                    type: string
                    enum: ["cost", "safety", "healthcare", "education", "career", "lifestyle", "infrastructure"]
                  winner:
                    type: string
                    nullable: true
                    description: "Country code of winner, or null if tied"
                  scoreA:
                    type: integer
                  scoreB:
                    type: integer
                  insight:
                    type: string
                    description: "Natural-language comparison sentence"
            summary:
              type: string
              description: "Natural-language overall summary"
        methodology:
          type: object
          properties:
            scoringWeights:
              type: object
            note:
              type: string
        sources:
          type: array
          items:
            type: string
        relatedLinks:
          type: object
          properties:
            fullComparison:
              type: string
              format: uri
            countryA:
              type: string
              format: uri
            countryB:
              type: string
              format: uri
            allComparisons:
              type: string
              format: uri
            apiDocs:
              type: string
              format: uri

    ComparisonCountry:
      type: object
      properties:
        name:
          type: string
        code:
          type: string
        monthlyEstimateUsd:
          type: number
        overallScore:
          type: integer
        globalRank:
          type: integer
        globalRankOutOf:
          type: integer
        scores:
          type: object
          properties:
            cost:
              type: integer
            safety:
              type: integer
            healthcare:
              type: integer
            education:
              type: integer
            career:
              type: integer
            lifestyle:
              type: integer
            infrastructure:
              type: integer
        confidenceScore:
          type: number
