Mobile SDK Documentation — v1.0

Overview

The izi.TRAVEL Mobile SDK is available for four platforms. Each SDK exposes the same two core calls and the same model shapes, so the integration pattern is identical regardless of platform.

PlatformLanguageDistributionMin Version
FlutterDartpub.devFlutter 3 / Dart 3
AndroidKotlinMaven Central / JitPackAPI 21 (Android 5)
iOSSwiftSwift Package Manager / CocoaPodsiOS 15 / macOS 12
React NativeTypeScriptnpmReact Native 0.70

Prerequisites

Both keys are required. Calls without them return a 401 error.

How It Works

The SDK handles all authentication headers automatically on every request:

HeaderValueSource
X-IZI-API-KEYYour API keyPassed at init
HTTP-X-PARTNER-KEYYour partner keyPassed at init
HTTP-X-APPLICATION-IDApp bundle/package IDAuto-detected
HTTP-X-PARTNER-USER-IDPer-device UUIDAuto-generated & persisted
HTTP-X-USER-DEVICE-IDHardware device IDAuto-detected
HTTP-X-USER-AGENTIPHONE / ANDROIDAuto-detected

The partner user ID is generated once on first launch and stored locally. It persists across app restarts and is used for per-user charging and rate-limit tracking.

All UUIDs returned by the API are obfuscated before being handed to your app — see UUID Obfuscation.


F Flutter SDK

Installation

Add the package to pubspec.yaml:

dependencies:
  izi_travel: ^1.0.0

Then fetch dependencies:

flutter pub get

Android — internet permission

Add to android/app/src/main/AndroidManifest.xml inside <manifest>:

<uses-permission android:name="android.permission.INTERNET"/>

Initialization

Create a single client instance and reuse it throughout your app.

import 'package:izi_travel/izi_travel.dart';

final client = IziTravelClient(
  apiKey: 'YOUR_API_KEY',
  partnerKey: 'YOUR_PARTNER_KEY',
);
final client = IziTravelClient(
  apiKey: 'YOUR_API_KEY',
  partnerKey: 'YOUR_PARTNER_KEY',
  isDebug: true, // staging API + request/response logs
);
isDebug: true switches the base URL to the staging environment (client-private-api-stage.izi.travel) and enables console logging. Always use false (or omit it) in production.

Fetch Partner Tours Not Charged

Returns all tours available to your partner account. This call is not charged.

final result = await client.getPartnerTours();

for (final tour in result.tours) {
  print('${tour.title} — ${tour.coverImage}');
}

// Pagination
print('Page ${result.pagination.currentPage} of ${result.pagination.totalCount}');
print('Has more: ${result.pagination.hasMore}');

// Fetch next page
if (result.pagination.hasMore) {
  final next = await client.getPartnerTours(
    TourQueryOptions(page: 2, perPage: 20),
  );
}

Filter by language:

final result = await client.getPartnerTours(
  TourQueryOptions(
    languages: ['en', 'de'],
    page: 1,
    perPage: 20,
  ),
);

Fetch Tour Detail Charged

Fetches the full content of a single object. This call is charged — one credit is consumed per unique user per day.

// tour.uuid is an opaque token — pass it as-is, do not decode it.
final detail = await client.getTourDetail(tour.uuid, languages: ['en']);

print(detail.title);
print(detail.type);          // "tour", "museum", "stop", etc.
print(detail.tourDescription); // full HTML description
print(detail.city);
print(detail.country);
print(detail.location);      // "lat,lng" e.g. "30.449,30.317"
print(detail.coverImage);    // URL of first image
print(detail.audios);        // list of audio URLs

// Children and collections
print('${detail.children.length} stops');
print('${detail.collections.length} collection sections');

Fetch Child / Collection Detail Charged

Each item in children and collections is a TourChild with its own uuid. Pass that uuid back to getTourDetail to load the full content of that stop.

final tour = await client.getTourDetail(tourUuid);

for (final stop in tour.children) {
  final stopDetail = await client.getTourDetail(stop.uuid, languages: ['en']);
  print('${stop.title}: ${stopDetail.tourDescription}');
}

// Same pattern for collections
for (final section in tour.collections) {
  final detail = await client.getTourDetail(section.uuid, languages: ['en']);
}

Caching

Responses from getTourDetail are cached in memory for 24 hours keyed by (uuid, languages). Subsequent calls with the same arguments return instantly without hitting the API.

// Force a fresh fetch — e.g. after pull-to-refresh
client.clearCache();

Error Handling

try {
  final result = await client.getPartnerTours();
} on IziCreditLimitException {
  print('Credit limit reached');
} on IziAuthException {
  print('Invalid API key');
} on IziPartnerInactiveException {
  print('Partner account inactive');
} on IziServerException catch (e) {
  print('Server error: ${e.message}');
} on IziTravelException catch (e) {
  print('API error ${e.statusCode}: ${e.message}');
}

A Android SDK

Installation

Add JitPack to your root settings.gradle.kts:

dependencyResolutionManagement {
    repositories {
        maven { url = uri("https://jitpack.io") }
    }
}

Add the dependency in your module's build.gradle.kts:

dependencies {
    implementation("com.github.izi-travel:izi-android:1.0.0")
}
dependencies {
    implementation("travel.izi:izi-travel-android:1.0.0")
}

Required permissions

Add to AndroidManifest.xml:

<uses-permission android:name="android.permission.INTERNET"/>

Initialization

IziTravelClient requires an Android Context to persist the partner user ID and read device info.

import travel.izi.sdk.IziTravelClient

// In your Application or Activity
val client = IziTravelClient(
    context = applicationContext,
    apiKey = "YOUR_API_KEY",
    partnerKey = "YOUR_PARTNER_KEY"
)

Development mode (staging + logs):

val client = IziTravelClient(
    context = applicationContext,
    apiKey = "YOUR_API_KEY",
    partnerKey = "YOUR_PARTNER_KEY",
    isDebug = true
)

Fetch Partner Tours Not Charged

All SDK calls are suspend functions — call them from a coroutine scope.

import travel.izi.sdk.models.TourQueryOptions
import kotlinx.coroutines.launch

viewModelScope.launch {
    val result = client.getPartnerTours()

    result.tours.forEach { tour ->
        println("${tour.title} — ${tour.coverImage}")
    }

    println("Has more: ${result.pagination.hasMore}")
}

// With options
val filtered = client.getPartnerTours(
    TourQueryOptions(
        languages = listOf("en", "de"),
        page = 1,
        perPage = 20
    )
)

Fetch Tour Detail Charged

viewModelScope.launch {
    // tour.uuid is an opaque token — pass it as-is
    val detail = client.getTourDetail(tour.uuid, languages = listOf("en"))

    println(detail.title)
    println(detail.tourDescription)   // full HTML
    println(detail.city)
    println(detail.location)          // "lat,lng"
    println(detail.coverImage)

    detail.children.forEach { stop ->
        val stopDetail = client.getTourDetail(stop.uuid, languages = listOf("en"))
        println("${stop.title}: ${stopDetail.tourDescription}")
    }
}

Caching

Detail responses are cached in memory for 24 hours by (uuid, languages).

// Invalidate all cached entries
client.clearCache()

Error Handling

import travel.izi.sdk.exceptions.*

viewModelScope.launch {
    try {
        val result = client.getPartnerTours()
    } catch (e: IziAuthException) {
        // 401 — invalid API key
    } catch (e: IziPartnerInactiveException) {
        // 403 — partner inactive
    } catch (e: IziCreditLimitException) {
        // 429 — credit limit reached
    } catch (e: IziServerException) {
        // 500 — server error: e.message
    } catch (e: IziTravelException) {
        // other: e.statusCode, e.message
    }
}

iOS SDK

Installation

In Xcode: File → Add Package Dependencies, enter the repository URL, and choose version 1.0.0.

Or in your Package.swift:

dependencies: [
    .package(url: "https://github.com/izi-travel/izi-ios.git", from: "1.0.0")
],
targets: [
    .target(
        name: "YourApp",
        dependencies: ["IziTravel"]
    )
]

Add to your Podfile:

pod 'IziTravel', '~> 1.0'

Then run:

pod install

Initialization

import IziTravel

// Production
let client = IziTravelClient(
    apiKey: "YOUR_API_KEY",
    partnerKey: "YOUR_PARTNER_KEY"
)

Development mode (staging + logs):

let client = IziTravelClient(
    apiKey: "YOUR_API_KEY",
    partnerKey: "YOUR_PARTNER_KEY",
    isDebug: true
)

Fetch Partner Tours Not Charged

All SDK calls use Swift async/await.

Task {
    let result = try await client.getPartnerTours()

    for tour in result.tours {
        print("\(tour.title) — \(tour.coverImage ?? "")")
    }

    print("Has more: \(result.pagination.hasMore)")
}

// With options
let result = try await client.getPartnerTours(
    TourQueryOptions(languages: ["en", "de"], page: 1, perPage: 20)
)

Fetch Tour Detail Charged

let detail = try await client.getTourDetail(tour.uuid, languages: ["en"])

print(detail.title)
print(detail.tourDescription)  // full HTML
print(detail.city ?? "")
print(detail.location ?? "")   // "lat,lng"
print(detail.coverImage ?? "")

// Children
for stop in detail.children {
    let stopDetail = try await client.getTourDetail(stop.uuid, languages: ["en"])
    print("\(stop.title): \(stopDetail.tourDescription)")
}

// Collections
for section in detail.collections {
    let sectionDetail = try await client.getTourDetail(section.uuid)
}

Caching

Detail responses are cached in memory for 24 hours by (uuid, languages).

client.clearCache()

Error Handling

do {
    let result = try await client.getPartnerTours()
} catch IziTravelError.auth {
    // 401 — invalid API key
} catch IziTravelError.partnerInactive {
    // 403 — partner account inactive
} catch IziTravelError.creditLimit {
    // 429 — credit limit reached
} catch IziTravelError.server(let message) {
    print("Server error: \(message)")
} catch IziTravelError.notFound(let uuid) {
    print("Not found: \(uuid)")
} catch IziTravelError.unexpected(let code, let body) {
    print("HTTP \(code): \(body)")
} catch {
    print("Unexpected error: \(error)")
}

RN React Native SDK

Installation

npm install izi-travel-react-native
# or
yarn add izi-travel-react-native

Install required peer dependencies:

npm install @react-native-async-storage/async-storage

Optional — for automatic device ID detection:

npm install react-native-device-info

iOS — run pod install

cd ios && pod install

Android — internet permission

Add to android/app/src/main/AndroidManifest.xml:

<uses-permission android:name="android.permission.INTERNET"/>

Initialization

import { IziTravelClient } from 'izi-travel-react-native';

// Production
const client = new IziTravelClient({
  apiKey: 'YOUR_API_KEY',
  partnerKey: 'YOUR_PARTNER_KEY',
});

Development mode (staging + logs):

const client = new IziTravelClient({
  apiKey: 'YOUR_API_KEY',
  partnerKey: 'YOUR_PARTNER_KEY',
  isDebug: true,
});

Fetch Partner Tours Not Charged

const result = await client.getPartnerTours();

result.tours.forEach(tour => {
  console.log(`${tour.title} — ${tour.coverImage}`);
});

console.log('Has more:', result.pagination.hasMore);

// With options
const filtered = await client.getPartnerTours({
  languages: ['en', 'de'],
  page: 1,
  perPage: 20,
});

Fetch Tour Detail Charged

// tour.uuid is an opaque token — pass it as-is
const detail = await client.getTourDetail(tour.uuid, ['en']);

console.log(detail.title);
console.log(detail.tourDescription); // full HTML
console.log(detail.city);
console.log(detail.location);        // "lat,lng"
console.log(detail.coverImage);

// Children
for (const stop of detail.children) {
  const stopDetail = await client.getTourDetail(stop.uuid, ['en']);
  console.log(`${stop.title}: ${stopDetail.tourDescription}`);
}

// Collections
for (const section of detail.collections) {
  const sectionDetail = await client.getTourDetail(section.uuid);
}

Caching

Detail responses are cached in memory for 24 hours by (uuid, languages).

client.clearCache();

Error Handling

import {
  IziAuthError,
  IziPartnerInactiveError,
  IziCreditLimitError,
  IziServerError,
  IziTravelError,
} from 'izi-travel-react-native';

try {
  const result = await client.getPartnerTours();
} catch (e) {
  if (e instanceof IziAuthError) {
    // 401 — invalid API key
  } else if (e instanceof IziPartnerInactiveError) {
    // 403 — partner inactive
  } else if (e instanceof IziCreditLimitError) {
    // 429 — credit limit reached
  } else if (e instanceof IziServerError) {
    console.error('Server error:', e.message);
  } else if (e instanceof IziTravelError) {
    console.error(`HTTP ${e.statusCode}: ${e.message}`);
  }
}

Models Reference

All SDKs expose the same logical model shape. Names are adapted to each platform's naming convention.

MTGObject

Represents any izi.TRAVEL content object returned by the detail endpoint — a tour, museum, exhibit, stop, or collection entry. The type field indicates which kind.

FieldTypeDescription
uuidStringOpaque token — pass to getTourDetail
titleStringDisplay title
typeString"tour", "museum", "stop", "exhibit", etc.
languagesString[]Available language codes
languageStringLanguage of this response
idIntInternal numeric ID
imagesTourImage[]Ordered list of images
coverImageString?URL of first image (convenience getter)
audiosString[]Audio file URLs
tourDescriptionStringFull HTML description
tourRatingAverageDouble?Average rating
cityString?City name
countryString?Country name
locationString?Lat/lng string e.g. "30.449,30.317"
affiliateLinkString?Affiliate URL
purchaseTourPurchase?Pricing info for paid tours
childrenTourChild[]Child stops / exhibits
collectionsTourChild[]Collection sections

TourChild

A child stop or collection entry within an MTGObject. Pass uuid to getTourDetail to load its full content.

FieldTypeDescription
uuidStringOpaque token
titleStringDisplay title
imagesTourImage[]Ordered list of images
coverImageString?URL of first image

TourImage

FieldTypeDescription
urlStringRemote image URL
positionIntDisplay order (0-based)

TourListResponse

FieldTypeDescription
toursMTGObject[]List of tours for this page
paginationPaginationPage metadata
successBoolWhether the request succeeded

Pagination

FieldTypeDescription
currentPageIntCurrent page number (1-based)
perPageIntResults per page
totalCountIntTotal number of tours
hasMoreBooltrue when more pages exist

TourPurchase

FieldTypeDescription
currencyStringISO 4217 currency code (e.g. "EUR")
priceDoublePurchase price

Error Reference

All SDKs map HTTP status codes to typed exceptions so you never need to inspect raw status codes.

HTTP Flutter Android (Kotlin) iOS (Swift) React Native Cause
401 IziAuthException IziAuthException IziTravelError.auth IziAuthError Invalid or missing API key
403 IziPartnerInactiveException IziPartnerInactiveException IziTravelError.partnerInactive IziPartnerInactiveError Partner account inactive
429 IziCreditLimitException IziCreditLimitException IziTravelError.creditLimit IziCreditLimitError Daily credit limit reached
500 IziServerException IziServerException IziTravelError.server(msg) IziServerError Server-side error
other IziTravelException IziTravelException IziTravelError.unexpected(code,body) IziTravelError Unexpected response

UUID Obfuscation

The SDK automatically obfuscates all UUIDs before returning them to your application. The uuid field on MTGObject and TourChild is an opaque token, not the raw backend UUID.

Never attempt to decode or parse the UUID token. The SDK handles decoding internally before any API call. Store and pass the value as-is.

The obfuscation algorithm is identical across all four SDKs, so a token produced by the Android SDK is accepted by the iOS SDK and vice versa.


Charging

CallCharged?
getPartnerToursNo
getTourDetailYes

For full details on pricing, billing cycles, and invoice management, please contact izi.TRAVEL.

Rate Limits

Daily Limits

DimensionCap
ip_address × user_id500 / day
user_agent × user_id500 / day
device × user_id500 / day
user_id500 / day

Monthly Limits (unique identifier collections)

CollectionLimit
Unique IP addresses per partner5 / month
Unique user agents per partner5 / month

When a limit is exceeded, the API returns 429 and the SDK throws the credit-limit exception for that platform.

Supported Languages

Pass any of the following ISO 639-1 codes to the languages parameter. Use "any" to return content in all available languages.

enfrnlsvitdeesruptcs firotrjazhkohybehehi slhrhudaukelpletkavi kkarltazbgnolvttcaeu aaabaeafakamanasavay babibmbnbobrbscechco crcvcydvdzeeeofafffj fofygagdglgngugvhaho hthziaidieigiiikiois iujvkgkikjklkmknkrks kukvkwkylalblglilnlo lumgmhmimkmlmnmrmsmt mynanbndnengnnnrnvny ocojomorospapipsqurm rnrwsascsdsesgsisksm snsosqsrssstsuswtate tgthtitktltntotstwty uguruzvevowawoxhyiyo zazu