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.
The header
Pass X-API-Key: <your key>. A missing key answers 401 MISSING_API_KEY. A bad key answers 401 INVALID_API_KEY. Both include {"success": false, "error": {"code", "message"}, "data": null}.
Keys are secrets. Keep them in an environment variable on a server. The playground on this site keeps a key in this browser tab so you can try a call. Do not put a key in the JavaScript of your own app.
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.