REST · JSON · v1

Hotel API Documentation

A simple, RESTful JSON API for looking up hotel/property information by ID. All endpoints accept and return application/json over HTTPS.

Overview

The Hotel API exposes a curated read‑only catalogue of properties (hotels, apartments, etc.) including their location, accommodation type, chain affiliation, facilities, and image gallery.

  • RESTful — predictable resource URLs, standard HTTP verbs and status codes.
  • JSON only — requests must send Content-Type: application/json and Accept: application/json.
  • UTF‑8 — all responses are UTF‑8 encoded.
  • ISO 8601 — timestamps use ISO 8601 with timezone (e.g. 2026-05-10T08:30:00+00:00).

Base URL & format

All endpoints below are relative to:

https://parto.pro.plus/api

Headers required on every request

HeaderValue
Acceptapplication/json
Content-Typeapplication/json (for requests with a body)

Authentication

Hotel-search endpoints are protected by a short-lived SessionId issued by POST /api/Authenticate/CreateSession. The flow mirrors the Parto CRS contract so existing client libraries can be reused with minimal change:

  1. Compute the SHA-512 hex digest of the plaintext password you received from the administrator.
  2. Call POST /api/Authenticate/CreateSession with { OfficeId, UserName, Password } to receive a SessionId.
  3. Include the SessionId in the JSON body (or as X-Session-Id header) of every protected request.
  4. The session is valid for 15 minutes and is automatically extended by another 15 minutes on every successful authenticated call.

The public /properties/lookup and /cities/search endpoints do not require authentication.

Important: only the SHA-512 digest of your password is sent over the wire — never the plaintext. Treat the digest and the resulting SessionId as secrets.

Errors

The API uses conventional HTTP status codes to signal success or failure of a request.

StatusMeaning
200OK — the request succeeded.
422Unprocessable Entity — validation failed. Inspect errors.
405Method Not Allowed — you used a wrong HTTP verb.
500Internal Server Error — something went wrong on our side.

Validation error shape

// HTTP/1.1 422 Unprocessable Entity
{
  "message": "The ids field is required.",
  "errors": {
    "ids": ["The ids field is required."]
  }
}

Rate limits

By default the API allows up to 60 requests per minute per IP. When the limit is exceeded, the API returns 429 Too Many Requests. The current limit is reflected in the response headers:

HeaderDescription
X-RateLimit-LimitMaximum requests allowed per window.
X-RateLimit-RemainingRequests left in the current window.
Retry-AfterSeconds to wait before retrying (only on 429).

Lookup properties

Returns a list of properties matching the given IDs, including their full details (location, facilities, images, etc). Up to 200 IDs may be requested in a single call.

POST /api/properties/lookup

Request body

FieldTypeRequiredDescription
idsinteger[]requiredArray of property IDs. Min 1, max 200.

Example request

curl -X POST "https://parto.pro.plus/api/properties/lookup" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{"ids":[101, 202, 303]}'

Successful response  200

{
  "data": [
    {
      "id": 101,
      "name": "Grand Plaza Hotel",
      "rating": 4.5,
      "address": "12 Avenue de la Liberté",
      "postal_code": "75008",
      "phone": "+33 1 23 45 67 89",
      "url": "https://example.com/grand-plaza",
      "last_update": "2026-05-10T08:30:00+00:00",
      "location": {
        "latitude": 48.8738,
        "longitude": 2.295,
        "city":        { "id": 7,  "name": "Paris" },
        "destination": { "id": 21, "name": "Île-de-France" },
        "country_id": 33
      },
      "accommodation": { "id": 1, "name": "Hotel" },
      "chain":         { "id": 42, "name": "Grand Collection" },
      "facilities": [
        { "id": 5,  "name": "Free Wi-Fi", "group": { "id": 2, "name": "Internet" } },
        { "id": 14, "name": "Swimming Pool", "group": { "id": 9, "name": "Wellness" } }
      ],
      "images": [
        { "url": "https://cdn.example.com/p101/1.jpg", "thumbnail_url": "https://cdn.example.com/p101/1-thumb.jpg" }
      ]
    }
  ],
  "not_found": [303]
}
Partial results: any IDs you sent that don't exist in our database are returned in the not_found array. The endpoint always returns 200 OK as long as the request itself was valid.

Validation errors  422

{
  "message": "The ids field is required.",
  "errors": {
    "ids": [
      "The ids field is required."
    ]
  }
}
  • ids must be an array.
  • ids must contain at least 1 element and at most 200.
  • Every element of ids must be an integer.

Create a session

Exchange your credentials for a short-lived SessionId that authorises calls to the protected endpoints. The contract mirrors the upstream Parto CRS exactly.

POST /api/Authenticate/CreateSession

Request body

FieldTypeDescription
OfficeId requiredstringYour numeric OfficeId, shown on your user dashboard.
UserName requiredstringYour alphanumeric API username (e.g. API582716).
Password requiredstring (hex)The SHA-512 hex digest of your plaintext password — not the plaintext.

Example request

curl -X POST "https://parto.pro.plus/api/Authenticate/CreateSession" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "OfficeId": "1273298",
    "UserName": "API582716",
    "Password": "<sha512-hex-of-your-password>"
  }'

Successful response  200

{
  "Error": null,
  "Success": true,
  "SessionId": "01492146-ae4c-f111-b885-00505682c602"
}

Invalid credentials  200

For security reasons, invalid credentials return HTTP 200 with Success:false (rather than 401), matching the upstream Parto behaviour:

{
  "Error": { "Id": "Err0102001", "Message": "Invalid login credentials supplied" },
  "Success": false,
  "SessionId": null
}

Session lifecycle

  • Each SessionId is valid for 15 minutes from the time of last use.
  • Every successful authenticated call extends the expiry by another 15 minutes (rolling window).
  • An expired or unknown SessionId yields HTTP 401 with Err0102003.
  • An inactive account yields HTTP 403 with Err0102004.

Computing the SHA-512 digest

# bash
echo -n 'MyPlaintextPassword' | openssl dgst -sha512 | awk '{print $2}'

# php
echo hash('sha512', 'MyPlaintextPassword');

// javascript (Web Crypto)
const buf = await crypto.subtle.digest('SHA-512', new TextEncoder().encode(plaintext));
const hex = [...new Uint8Array(buf)].map(b => b.toString(16).padStart(2, '0')).join('');

Hotel availability search

Proxies a hotel availability search to the upstream Parto CRS and decorates every returned itinerary with the matching local property's data (joined by Parto HotelId = properties.external_id). Upstream session management is handled server‑side and is fully transparent to clients.

POST /api/Hotel/HotelAvailability

Authentication

This endpoint requires a valid SessionId obtained from /api/Authenticate/CreateSession. Provide it either in the JSON body as "SessionId" or as the X-Session-Id request header.

Request body

FieldTypeDescription
SessionId requiredstring (UUID)The SessionId from /api/Authenticate/CreateSession. May be sent in the body or as the X-Session-Id header.
CheckIn requiredstring (YYYY‑MM‑DD)Check‑in date.
CheckOut requiredstring (YYYY‑MM‑DD)Check‑out date. Must be after CheckIn.
NationalityId requiredstring (ISO 3166‑1 alpha‑2)Guest nationality, e.g. "TR".
HotelIdintegerSingle hotel. One of HotelId/HotelIdList/CityId is required.
HotelIdListinteger[]List of hotel IDs.
CityIdintegerCity ID (Parto external ID).
CountryCodestring (ISO 3166‑1 alpha‑2)Optional country filter.
Occupancies requiredobject[]One entry per room: {AdultCount, ChildCount, ChildAges:[]}. ChildAges length must equal ChildCount.

Example request

curl -X POST "https://parto.pro.plus/api/Hotel/HotelAvailability" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "SessionId": "01492146-ae4c-f111-b885-00505682c602",
    "CheckIn": "2026-05-24",
    "CheckOut": "2026-05-26",
    "NationalityId": "TR",
    "CityId": 924,
    "Occupancies": [
      { "AdultCount": 2, "ChildCount": 0, "ChildAges": [] }
    ]
  }'

Successful response  200

The upstream Parto response is returned verbatim — every field (Success, Error, SearchId, PricedItineraries[] with all of HotelId, RoomRates, HotelPolicy, ExtraCharge, Surcharges, Amenities, Rooms, BoardCode, Currency, TotalFare, etc.) is preserved as-is. The proxy only adds two things on top:

  • A new Hotel key on each PricedItinerary containing the normalised local property record (or null if the upstream HotelId is not present in the local catalogue).
  • A new top‑level Meta object with count (total itineraries), matched (how many had local data), and not_found (HotelIds with no local match).
{
  // ── Everything below this line comes straight from Parto, untouched ──
  "Success": true,
  "Error": null,
  "SearchId": "abc-123-…",
  "PricedItineraries": [
    {
      "HotelId": 12952,
      "BoardCode": "BB",
      "Currency": "EUR",
      "TotalFare": 123.45,
      "RoomRates": [ /* full upstream room-rate objects */ ],
      "Rooms": [ /* full upstream rooms */ ],
      "HotelPolicy": [ /* … */ ],
      "ExtraCharge": [ /* … */ ],
      "Surcharges": [ /* … */ ],
      "Amenities": [ /* … */ ],
      // … any other fields returned by Parto are kept as-is …

      // ── Added by this proxy ──
      "Hotel": {
        "id": 4012,
        "external_id": 12952,
        "name": "Example Hotel",
        "rating": 4.5,
        "review_score": 8.7,
        "last_update": "2026-05-01T12:00:00+00:00",
        "contact": { "phone": "…", "fax": "…", "email": "…", "url": "…" },
        "address": {
          "line": "…",
          "postal_code": "34000",
          "city": { "id": 1, "external_id": 924, "name": "Istanbul" },
          "destination": { "id": 5, "external_id": 700, "name": "Sultanahmet" },
          "country": { "id": 1, "code": "TR", "name": "Türkiye" }
        },
        "location": { "latitude": 41.01, "longitude": 28.97 },
        "accommodation": { "id": 1, "external_id": 10, "name": "Hotel" },
        "chain": null,
        "facility_groups": [
          { "id": 3, "name": "Amenities", "facilities": [
            { "id": 11, "external_id": 2001, "name": "Free Wi-Fi" }
          ] }
        ],
        "images_count": 12,
        "images": [
          { "url": "…", "thumbnail_url": "…" }
        ]
      }
    }
  ],

  // ── Added by this proxy ──
  "Meta": {
    "count": 9,
    "matched": 8,
    "not_found": [99999]
  }
}

Validation errors  422

  • CheckIn/CheckOut must be YYYY‑MM‑DD and CheckOut must be after CheckIn.
  • Exactly one of HotelId, HotelIdList, CityId must be provided.
  • Per occupancy, ChildAges length must match ChildCount.

Upstream errors  502

If the upstream Parto CRS is unreachable or returns an unexpected error, the response is { "Success": false, "Error": "…" } with HTTP 502. Upstream business errors (e.g. invalid filters) are passed through with HTTP 200 and Success:false.

Hotel check rate

Re-prices a specific itinerary returned by /Hotel/HotelAvailability against the upstream Parto CRS to confirm the current price, cancellation policy and availability before booking. The client request is forwarded verbatim to Parto (only the upstream SessionId is injected server-side) and the upstream response is returned to the client unchanged.

POST /api/Hotel/HotelCheckRate

Authentication

Requires a valid SessionId obtained from /api/Authenticate/CreateSession. Provide it either in the JSON body as "SessionId" or via the X-Session-Id request header.

Request body

FieldTypeDescription
SessionId requiredstring (UUID)The SessionId from /api/Authenticate/CreateSession. May be sent in the body or as the X-Session-Id header.
FareSourceCode requiredstringThe opaque FareSourceCode taken from a PricedItinerary in the availability response. Send it back exactly as received — do not decode or modify.
FixStayIdstring | nullOptional fixed-stay identifier. Send null if not applicable.
Nationality requiredstring (ISO 3166-1 alpha-2)Guest nationality, e.g. "IR".

Example request

curl -X POST "https://parto.pro.plus/api/Hotel/HotelCheckRate" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "SessionId": "914363ab-981f-47f1-b6e5-6243a173df08",
    "FareSourceCode": "<paste from availability>",
    "FixStayId": null,
    "Nationality": "IR"
  }'

Successful response  200

The upstream Parto response is returned verbatim — every field (Success, Error, PricedItinerary, HotelFare, HotelPolicy, RoomRates, etc.) is preserved as-is. The proxy only adds a Hotel key on each PricedItinerary (singular or plural) carrying the normalised local property record (or null if the upstream HotelId is not present in the local catalogue) — the same shape returned by /Hotel/HotelAvailability.

Validation errors  422

  • FareSourceCode is required.
  • Nationality is required and must be a 2-letter ISO country code.

Upstream errors  502

If the upstream Parto CRS is unreachable or returns an unexpected error, the response is { "Success": false, "Error": "…" } with HTTP 502. Upstream business errors (e.g. expired FareSourceCode) are passed through with HTTP 200 and Success:false.

Property object

The canonical shape of a property returned by the API.

FieldTypeDescription
idintegerUnique property identifier.
namestringDisplay name of the property.
ratingfloat | nullStar rating, e.g. 4.5.
addressstring | nullStreet address.
postal_codestring | nullPostal/ZIP code.
phonestring | nullContact phone number.
urlstring | nullPublic website URL.
last_updateISO 8601 | nullLast time the property was modified upstream.
locationobjectLatitude, longitude, city, destination, country.
accommodationobject | nullAccommodation type (e.g. Hotel, Apartment).
chainobject | nullHotel chain affiliation.
facilitiesobject[]List of facilities, each with its group.
imagesobject[]Images with full and thumbnail URLs.

City object

The shape of a city returned by GET /cities/search.

FieldTypeDescription
idintegerInternal city identifier (stable within this API).
external_idinteger | nullUpstream city ID from the source data feed.
namestringCity name.
destinationobject | nullThe destination (region/area) the city belongs to: {id, name}.
countryobject | nullThe country the city is in: {id, code, name}. code is ISO 3166-1 alpha-2.

Changelog

2026-05-10 — v1

  • Initial public release of POST /properties/lookup.
  • Added GET /cities/search for partial-name city search.