Live & Ready

The Fastest & Cheapest
Hotel API for AI Agents

Connect your AI assistant to 2.5 million hotels worldwide. One POST request, real-time availability, prices consistently below Booking.com. Your agent books the cheapest room — automatically.

3 min
To go live
20 sec
Registration
<2s
Response time
2.5M+
Hotels
Get Your API Key AI Agent Instructions View Documentation

Built for AI Agents

Simple JSON in, structured JSON out. Designed so any LLM or agent framework can search and book hotels with a single tool call.

💰

Below Booking.com Prices

Wholesale rates from global suppliers. Every price you see is lower than what end-users pay on major OTAs.

🚀

3 Minutes to Production

Register in 20 seconds, grab your API key, send your first search. No SDK needed, no onboarding calls, no waiting.

🌎

Global Coverage

2.5M+ properties across 200+ countries. Hotels, apartments, resorts, villas — any type, any destination, any budget.

Overview

The Hotels.nl API gives your application — or your AI agent — direct access to real-time hotel availability and pricing across 2.5 million properties worldwide. Every request is a simple POST with a JSON body. Every response is clean, structured JSON ready for any LLM to parse and act on.

Two ways to search: send a location in plain text (a city, address, landmark — anything) and the API handles geocoding automatically, or pass latitude & longitude directly if you already have coordinates. Either way you get hotels with the cheapest available rate, including pricing, cancellation policy, and room details. For full rate listings, use the hotel detail endpoint. When your user picks a rate, one more call starts the booking — the guest clicks a single button to pay.

How it works

Always start with search. The search endpoint (/api/search.php) is the only way to find hotels — by name, location, or coordinates. You cannot look up a hotel by name on the hotel detail endpoint. Here's the full flow:

  1. Search — Call /api/search.php with a location (e.g. "Hilton Amsterdam") and dates. You get a list of matching hotels, each with an id and the cheapest rate including a hotelsnl_hash.
  2. Details — Take the id from the search results and call /api/hotel.php to get full hotel info, photos, policies, and all available rooms & rates. Each rate has its own hotelsnl_hash.
  3. Book — Send the hotelsnl_hash of the chosen rate to /api/booking.php. You get back a URL on hotels.nl where the guest finalizes the booking and pays — all details are pre-filled, one click to confirm.

The hotel detail endpoint requires a numeric id that you can only obtain from a search. Even if a user asks about a specific hotel by name, search for it first — the API will find it.

Base URL

https://hotels.nl/api/

Quick Start

Three steps to your first hotel search:

bash
# 1. Register and get your API key (20 seconds)
# 2. Search hotels with one request:

curl -X POST https://hotels.nl/api/search.php \
  -H "Content-Type: application/json" \
  -d '{
    "apikey": "YOUR_API_KEY",
    "location": "Dam Square, Amsterdam",
    "checkin": "2026-06-15",
    "checkout": "2026-06-18"
  }'

# 3. That's it. You get hotels with the cheapest rate and pricing back instantly.

Using an AI Agent?

If you're connecting an AI assistant or LLM agent to this API, point it to the Agent Instructions page. It's a plain-text document that contains everything an AI needs to understand the API, construct requests, and interpret responses — no HTML, no fluff. Just give your agent the API key and that URL.

https://hotels.nl/api/agent.php

Authentication

Every request must include your API key in the JSON body as the apikey field. No headers, no OAuth, no tokens to refresh. One key, every request.

Don't have a key yet? Register here — it takes 20 seconds.

json body
{
    "apikey": "your_api_key_here",
    ...
}

Error Handling

The API uses standard HTTP status codes. Errors return a JSON object with an error field.

  • 200 Success
  • 400 Bad Request — missing or invalid parameters
  • 401 Unauthorized — API key is missing
  • 403 Forbidden — API key is invalid
  • 405 Method Not Allowed — only POST accepted
  • 429 Too Many Requests — rate limit exceeded
error response
{
    "error": "Required: location, checkin, checkout"
}

Rate Limits

To ensure fair usage and protect the underlying hotel suppliers, the search.php and hotel.php endpoints are rate-limited per IP address.

5

requests per minute
per endpoint, per IP

200

requests per day
per endpoint, per IP

When a limit is exceeded, the API returns HTTP 429 with a Retry-After header indicating how many seconds to wait. The response body contains an error field explaining which limit was reached.

rate limit response
{
    "error": "Rate limit exceeded: maximum 5 requests per minute. Please wait and try again."
}
Need higher limits?

The default limits are sufficient for most use cases. If your application requires more requests, contact info@hotels.nl to request a higher daily quota. Include your API key and a description of your use case.

Endpoints

POST /api/hotel.php Hotel details + availability

Returns full hotel details together with room availability and rates. You must provide either a search_id from a previous search or checkin + checkout dates. Requests without dates are not accepted.

⚡ Rate-limited: 5 requests/minute, 200 requests/day per IP. See Rate Limits.

Search first

This endpoint requires a numeric hotel id, which you can only get from a /api/search.php response. You cannot search by hotel name here. Always start with a search, then use the id from the results to call this endpoint.

Required Parameters

NameTypeDescription
apikeystringrequiredYour API key
idintrequiredHotel ID from the id field in search results

Availability (one method required)

NameTypeDescription
Method 1 — Use a previous search
search_idstringThe search_id from a search response. Pulls this hotel's rates from the cached search.
Method 2 — Fresh availability lookup
checkinstringCheck-in date (YYYY-MM-DD)
checkoutstringCheck-out date (YYYY-MM-DD)
personsintNumber of persons for the room (1–5). Default: 2
currencystringPrice currency. Default: EUR
residencystringGuest country code. Default: nl

One of these methods is required. search_id is preferred — it's instant. If not available, send checkin + checkout for a fresh lookup.

Optional

NameTypeDescription
languagestringResponse language. Default: en

Response Fields (static)

FieldDescription
idInternal hotel ID
nameHotel name
locationAddress, coordinates, city, country, distance to center
timesCheck-in/out times, front desk hours
propertyTotal rooms, floors, year built/renovated, payment methods
descriptionsStructured text descriptions
imagesArray of {url, category} — 1024×768 photos with category label
amenitiesGrouped amenities (included + paid)
policiesInternet, parking, meals, pets, extra beds, shuttle

Response Fields (when availability requested)

FieldDescription
availabilitySource (search_cache or live), dates, nights, currency. Contains message if no rooms found.
roomsArray of room objects, grouped by type (class + quality + bedding). Room fields that are null or false are omitted. Each room contains a rates array with up to 4 options: nomeal/breakfast × refundable/non-refundable. Only the cheapest per combination is shown. Each rate includes hotelsnl_hash (deterministic booking reference — send to /api/booking.php to reserve), search_hash (internal reference for booking process), meal, refundable, pricing, cancellation, and booking. Taxes/VAT are only shown if not included and amount > 0. Empty array if no rooms available.

Response Codes

  • 200 Hotel details returned
  • 400 Missing hotel ID, or missing search_id / checkin+checkout
  • 403 Invalid API key
  • 404 Hotel not found in database

Request — With search_id (preferred)

{
    "apikey": "YOUR_API_KEY",
    "id": 1584,
    "search_id": "a3f8c9..."
}

Request — Fresh availability

{
    "apikey": "YOUR_API_KEY",
    "id": 1584,
    "checkin": "2026-06-15",
    "checkout": "2026-06-18",
    "persons": 2
}

Response — With availability

{
    "id": 1584,
    "name": "NH Groningen",
    "type": "Hotel",
    "star_rating": 4,
    "location": {
        "address": "Hanzeplein 132",
        "city": "Groningen",
        "country_code": "NL"
    },
    "times": {"check_in": "15:00", "check_out": "12:00"},
    "property": {"total_rooms": 155, "floors": 7},
    "descriptions": [...],
    "images": [
        {"url": "https://cdn.worldota.net/t/1024x768/...", "category": "Exterior"},
        {"url": "https://cdn.worldota.net/t/1024x768/...", "category": "Guest Rooms"}
    ],
    "amenities": [...],
    "policies": {...},
    "availability": {
        "source": "search_cache",
        "checkin": "2026-06-15",
        "checkout": "2026-06-18",
        "nights": 3,
        "currency": "EUR"
    },
    "rooms": [
        {
            "room_name": "Standard Double",
            "room_class": "Room",
            "quality": "Standard",
            "bedding": "Double",
            "capacity": "Double",
            "bathroom": "Private",
            "rates": [
                {
                    "hotelsnl_hash": "1584-a0b2c3d0e2f3g0h0i0j2k0l0-1-20260615-20260618-2",
                    "search_hash": "sr-019d1f54-6ee0-7a9e-8cef-a87c39a6cb98",
                    "meal": "nomeal",
                    "refundable": false,
                    "pricing": {
                        "total_price": "389.00",
                        "currency": "EUR",
                        "price_per_night": ["129.67", "129.67", "129.66"]
                    },
                    "cancellation": {
                        "free_cancellation_before": null,
                        "policies": [...]
                    },
                    "booking": {"rooms_available": 3}
                },
                {
                    "hotelsnl_hash": "1584-a0b2c3d0e2f3g0h0i0j2k0l0-4-20260615-20260618-2",
                    "search_hash": "sr-019d1f54-6ee0-7a9e-8cef-b92d48b7dc42",
                    "meal": "breakfast",
                    "refundable": true,
                    "pricing": {
                        "total_price": "429.00",
                        "currency": "EUR",
                        "price_per_night": ["143.00", "143.00", "143.00"],
                        "taxes": [{"name": "city_tax", "amount": "9.30", "currency": "EUR"}]
                    },
                    "cancellation": {
                        "free_cancellation_before": "2026-06-13T14:00:00",
                        "policies": [...]
                    },
                    "booking": {"rooms_available": 5}
                }
            ]
        }
    ]
}

Response — No availability

{
    "id": 1584,
    "name": "NH Groningen",
    ...
    "availability": {
        "source": "live",
        "checkin": "2026-12-31",
        "checkout": "2027-01-01",
        "message": "No rooms available for the requested dates."
    },
    "rooms": []
}
POST /api/booking.php Start a hotel booking

Initiates a hotel reservation. Send the hotelsnl_hash from any rate in the search or hotel response together with your API key. The system takes it from there — you get back a URL where the guest finalizes the booking with a single click.

Why a finalization URL?

The hotel providers we work with have extremely complex booking systems that require many technical steps behind the scenes — availability checks, rate verification, pre-booking confirmations, payment tokenization, and more. None of this is relevant to your user. We handle all of it server-side so your integration stays simple. The finalization page the guest lands on already has all their details pre-filled (name, email, phone from registration). All they need to do is press one button to start the payment process and confirm the reservation.

Parameters

NameTypeDescription
apikeystringrequiredYour API key
hotelsnl_hashstringrequiredThe hotelsnl_hash from any rate in a search or hotel response. Contains the hotel, room type, meal plan, dates, and number of persons.

That's it — just two fields. The hotelsnl_hash encodes all reservation details (hotel, room, rate type, dates, persons). Guest information is automatically pulled from your registered account.

What happens after the call

  1. A booking record is created with status processing.
  2. Our system starts working through the provider's multi-step booking pipeline in the background — verifying availability, locking the rate, confirming pricing, and preparing the payment session.
  3. The returned URL points to a finalization page on hotels.nl where the guest reviews the booking summary and completes payment with one click.

Response Fields

FieldDescription
status"ok" when the booking was successfully initiated.
booking_urlURL to the finalization page. Redirect the guest here or present it as a link. The page has all guest data pre-filled — one button to pay and confirm.

Response Codes

  • 200 Booking initiated successfully
  • 400 Missing or invalid hotelsnl_hash
  • 401 Missing API key
  • 403 Invalid API key
One-click checkout

The finalization page is designed for speed. We pre-fill the guest's name, email, and phone number from their API account. The guest sees a clean summary of the hotel, room, dates, price, and cancellation policy — then clicks one button to pay. No forms to fill out, no re-entering details.

Request

{
    "apikey": "YOUR_API_KEY",
    "hotelsnl_hash": "1584-a0b2c3d0e2f3g0h0i0j2k0l0-1-20260615-20260618-2"
}

Response

{
    "status": "ok",
    "booking_url": "https://hotels.nl/api/book/finalize.php?id=hFHUbO5sDBTeu0lLT6ZuDz8yhysv"
}

What the guest sees

The finalization page at the booking_url shows:

  • Hotel name, room type, and bedding
  • Check-in / check-out dates and number of nights
  • Total price, city tax (if any), and meal plan
  • Cancellation policy with deadline
  • Guest details (pre-filled from account)
  • One button to confirm and pay
POST /api/booking_overview.php List all your bookings

Returns a list of all bookings associated with your API key. Only bookings that have progressed past the initial creation stage are included (i.e. bookings where a payment was attempted). Results are sorted by booking date, newest first.

Parameters

NameTypeDescription
apikeystringrequiredYour API key

Response Fields

FieldDescription
status"ok" on success.
countTotal number of bookings returned.
bookingsArray of booking objects (see below).

Booking Object

FieldDescription
booking_idHotels.nl internal booking ID.
bookhashUnique booking reference hash. Use this to retrieve details or cancel.
booked_atDate and time the booking was created.
arrivalCheck-in date (YYYY-MM-DD).
departureCheck-out date (YYYY-MM-DD).
guest_nameFull name of the lead guest.
hotel_nameName of the booked hotel.
room_nameRoom type description.
statusOne of: payment_failed, booking_failed, booking_confirmed, booking_cancelled.

Response Codes

  • 200 Success — bookings returned (may be empty array)
  • 401 Missing API key
  • 403 Invalid API key

Request

{
    "apikey": "YOUR_API_KEY"
}

Response

{
    "status": "ok",
    "count": 2,
    "bookings": [
        {
            "booking_id": 14,
            "bookhash": "aBcDeFgHiJkLmNoPqRsTuVwXyZ1234",
            "booked_at": "2026-03-25 14:32:01",
            "arrival": "2026-06-15",
            "departure": "2026-06-18",
            "guest_name": "John Doe",
            "hotel_name": "Grand Hotel Bonanno",
            "room_name": "Superior Double Room",
            "status": "booking_confirmed"
        },
        {
            "booking_id": 12,
            "bookhash": "xYzAbCdEfGhIjKlMnOpQrStUvWx9876",
            "booked_at": "2026-03-20 09:15:44",
            "arrival": "2026-07-01",
            "departure": "2026-07-04",
            "guest_name": "Jane Smith",
            "hotel_name": "Hotel Bernini Palace",
            "room_name": "Deluxe Twin Room",
            "status": "booking_cancelled"
        }
    ]
}
POST /api/retrieve_booking.php Get full booking details

Retrieves all details for a single booking. The booking must belong to the account associated with the API key. Returns comprehensive information including hotel details, guest data, pricing, cancellation policy, and current status.

Parameters

NameTypeDescription
apikeystringrequiredYour API key
bookhashstringrequiredThe 30-character booking reference hash (from booking_overview or the original booking response).

Key Response Fields

FieldDescription
status"ok" on success.
bookingObject containing all booking fields (see example).
booking.statusOne of: new, processing, booking_failed, booking_confirmed, booking_cancelled.
booking.refundabletrue if the rate allows free cancellation, false for non-refundable rates.
booking.free_cancellation_beforeDeadline for free cancellation (ISO datetime string), or null if non-refundable.
booking.hotel_nameHotel name from our database.
booking.hotel_addressHotel street address.

The response includes all booking fields except internal system fields (hashes, internal IDs, Mollie payment details). Hotel information (name, address, city, country, stars, phone, image) is joined automatically.

Response Codes

  • 200 Success — booking details returned
  • 400 Missing bookhash
  • 401 Missing API key
  • 403 Invalid API key or booking belongs to another account
  • 404 Booking not found

Request

{
    "apikey": "YOUR_API_KEY",
    "bookhash": "aBcDeFgHiJkLmNoPqRsTuVwXyZ1234"
}

Response

{
    "status": "ok",
    "booking": {
        "id": 14,
        "user_id": 1,
        "hotel_id": 1584,
        "arrival": "2026-06-15",
        "departure": "2026-06-18",
        "persons": 2,
        "first_name": "John",
        "last_name": "Doe",
        "email": "john@example.com",
        "phone": "+31612345678",
        "client_remarks": "Late arrival around 22:00",
        "bookhash": "aBcDeFgHiJkLmNoPqRsTuVwXyZ1234",
        "room_name": "Superior Double Room",
        "bedding": "Double Bed",
        "meal": "breakfast",
        "total_price": "342.50",
        "currency": "EUR",
        "refundable": true,
        "free_cancellation_before": "2026-06-02T21:59:00",
        "status": "booking_confirmed",
        "status_description": "booking_confirmed",
        "paid": "yes",
        "ratehawk_order_id": "H-12345678",
        "hotel_name": "Grand Hotel Bonanno",
        "hotel_address": "Via Carlo Francesco Gabba, 17",
        "hotel_city": "Pisa",
        "hotel_country": "IT",
        "hotel_stars": 4,
        "created_at": "2026-03-25 14:32:01"
    }
}
POST /api/book/cancel.php Cancel a confirmed reservation

Cancels a confirmed reservation. Only bookings with status booking_confirmed can be cancelled, and only within the free cancellation window. After the cancellation deadline, cancellation is no longer possible and no refund will be given.

Cancellation rules

Cancellation is only possible for refundable rates and only before the free_cancellation_before deadline. Non-refundable bookings cannot be cancelled. After the free cancellation deadline has passed, cancellation is no longer possible and no refund will be given. Use retrieve_booking.php to check whether a booking can still be cancelled.

Parameters

NameTypeDescription
apikeystringrequiredYour API key
bookhashstringrequiredThe 30-character booking reference hash.

Response Fields

FieldDescription
status"cancelled" on success.
booking_idHotels.nl internal booking ID.
bookhashThe booking reference hash (echo).

What happens after cancellation

  1. The cancellation is sent to the hotel supplier immediately.
  2. The booking status changes to booking_cancelled.
  3. A cancellation confirmation email is sent to the guest.
  4. Payment settlement is handled by our team within two working days.

Response Codes

  • 200 Booking cancelled successfully
  • 400 Missing bookhash
  • 401 Missing API key
  • 403 Invalid API key, or cancellation deadline passed / non-refundable
  • 404 Booking not found

Request

{
    "apikey": "YOUR_API_KEY",
    "bookhash": "aBcDeFgHiJkLmNoPqRsTuVwXyZ1234"
}

Success Response

{
    "status": "cancelled",
    "booking_id": 14,
    "bookhash": "aBcDeFgHiJkLmNoPqRsTuVwXyZ1234"
}

Error: Deadline Passed

{
    "error": "Free cancellation deadline (2026-06-02T21:59:00) has passed. Contact info@hotels.nl for assistance."
}

Error: Already Cancelled

{
    "error": "This reservation has already been cancelled.",
    "status": "already_cancelled"
}

For AI Agent Developers

This API is specifically designed to be called by AI agents and LLM tool-use frameworks. Here's why it works out of the box:

Natural language locations

No need to pre-geocode. Send "Central Park, NYC" or "Schiphol Airport" — we handle the rest.

Single-key auth

No OAuth flows, no token refresh. Just pass apikey in the body. Perfect for agent tool definitions.

Structured, consistent responses

Clean JSON with predictable field names. No HTML, no XML, no pagination tokens to manage.

Smart caching

Identical searches within 1 hour return instantly from cache. Your agent can retry without wasting time or money.