Introduction

Introduction

Ktemia is a travel search API. You describe a trip for a person. The response is the providers who can sell it: airlines, agencies, and other sellers, with prices you can compare.

The API does not issue a ticket, take payment, or hold a reservation. When you open an offer, the response includes that provider’s URL. The person books on the provider’s site.

Base URL

Every request goes to https://api.ktemia.com. There is no version prefix. The API is HTTPS only.

Send JSON, and you get JSON back. A search is POST with a body. A read that follows a search is GET with query parameters.

Providers

Each offer names the provider and the price, so you can show a person the choices side by side. The booking link opens that provider’s site with the trip filled in. Leave the URL as the API returned it.

Look up an airport or city code with Search airports, then start a flight search at Search flights. For a car hire, look up a place with Search locations, then start at Search cars. Authentication, errors, and pagination are the sections below. They apply to every call.

Read more

How Ktemia compares with other travel APIs, by who can get a key: best travel API in 2026. Why a search API is not a GDS or a booking engine: GDS vs booking engine vs a search API. Search flights from Claude Code, Cursor, or Codex with the open-source [Ktemia flight search skill](https://github.com/Ktemiaa/ktemia-flights-skill): /plugin marketplace add Ktemiaa/ktemia-flights-skill, then /plugin install ktemia@ktemia.

Authentication

Every request requires an API key. Create one in the dashboard, then send it on every call.

HTTPS only

The API is HTTPS only. Call it from your server. This site’s playground is allowed to call it from the browser; other websites are not.

Errors

Success responses are the JSON shown on each endpoint. Errors use one shape: a missing key, a bad body, an empty search, an expired token.

The envelope

error.code is stable and uppercase. error.message is safe to show. error.param is the field to highlight when the input was wrong (departureDate, legs[1].date, airlines[0]). It is omitted when the failure is not about a field.

Unknown fields are rejected. The response names the field in error.param.

Status codes

400 — the request is malformed (MISSING_FIELD, TRIP_CONFLICT, INVALID_ORIGIN, INVALID_LOCATION, LEGS_OUT_OF_ORDER, INVALID_SEARCH_TOKEN, …).

401 — missing or invalid API key.

404 — NO_RESULTS on search (the trip is valid, nothing published), or NO_MORE_RESULTS on a page past the end.

410 — SEARCH_EXPIRED. The token was valid and is now gone. Start a new search; retrying this one will not help.

429 — rate limit or a temporary block after repeated auth failures. Honour Retry-After.

502 — SEARCH_FAILED, DETAILS_FAILED, AIRPORTS_UNAVAILABLE, LOCATIONS_UNAVAILABLE, or UPSTREAM_ERROR. Temporary. Retry the same request.

Pagination

Each search is fixed for 60 minutes. /flights/results and /cars/results read the next page of that search. Page 2 does not repeat page 1.

Tokens, not page numbers

/flights/search and /cars/search return searchToken for page 1. Each results response that is not the last page includes nextToken. Send that as the token query parameter to get the next 20. You cannot request a page by number.

A token that is not valid answers 400 INVALID_SEARCH_TOKEN. A token that has expired answers 410 SEARCH_EXPIRED. Start a new search.

totalResults

totalResults is the number of itineraries in the search. hasMore is true when another page exists. The last page omits nextToken. Requesting a page after that returns 404 NO_MORE_RESULTS.