Athena Bedbank API

RESTful API for accessing Athena's consolidated hotel inventory. Search hotels, check availability, and create bookings programmatically.

Content API
Hotel listings, descriptions, photos, amenities, and room types. Cacheable — content changes infrequently.
p99 < 200ms
Cache API
Pre-computed availability and pricing. Fast responses from batch-updated data. Do not cache responses client-side.
p99 < 100ms
Booking API
Create, retrieve, modify, and cancel bookings. Live availability check on every create request.
p99 < 1,000ms

Base URLs

Production Live
https://api.athena-bedbank.com/v1
Sandbox Test
https://api-sandbox.athena-bedbank.com/v1

Conventions

Currency
All monetary values are in integer cents (e.g. 10000 = €100.00). Never floating point.
Dates
All dates in YYYY-MM-DD format. All timestamps in ISO 8601 with UTC timezone.
Pagination
List endpoints use page + page_size parameters. Response wraps data in { data, total, page, page_size }.
Idempotency
Pass Idempotency-Key header on POST /v1/bookings to safely retry without duplicate bookings.

Authentication

All requests require an API key passed in the X-Api-Key header.

⚠️ Key Security
Never expose your API key in client-side code, public repositories, or logs. The key grants full access to your account's billable API usage.

Request Example

# All requests require X-Api-Key header curl -X GET \ https://api.athena-bedbank.com/v1/hotels \ -H "X-Api-Key: bed_live_your_key_here" \ -H "Accept: application/json"

Key Types

PrefixEnvironmentBehavior
bed_live_…ProductionReal hotel inventory · Metered & billed · Rate limited by tier
bed_test_…SandboxFixture data only (Meridian Hotels) · No billing · No rate limit

Validate Your Key

POST /v1/session/validate // Response: { "customer_id": "22222222-0000-0000-0000-000000000001", "customer_name": "SunTours Travel Group", "environment": "production", "tier": "Tier2", "rate_limit_rps": 200 }

Error Handling

All errors return a consistent JSON body with a machine-readable code and human-readable message.

// Error response shape { "code": "room_unavailable", "message": "Room mh-r4521 is no longer available for Jun 15–18.", "details": { "hotel_id": "mh-a1b2c3d4", "room_id": "mh-r4521" } }
HTTP StatusCodeMeaning
200Success
400invalid_parameterMissing or malformed request parameter
401invalid_api_keyKey missing, invalid, or revoked
403permission_deniedAPI product not enabled for this key
404not_foundHotel, room, or booking not found
409room_unavailableRoom no longer available (race condition on booking)
409cancellation_expiredCancellation deadline passed
422business_rule_violationMin-stay, invalid board type, date logic error
429rate_limit_exceededRequest rate exceeded — check Retry-After header
503account_suspendedAccount suspended or balance depleted

Rate Limiting

Rate limits are enforced per API key using a sliding window algorithm (1-second window).

TierRequests/secDaily LimitTypical Use
Sandbox1010,000Testing only
Tier 150500,000Small OTA
Tier 22005,000,000Mid-size OTA
Tier 31,000UnlimitedLarge OTA / Enterprise

Rate Limit Headers

# Response headers on every request: X-RateLimit-Limit: 200 # requests/sec for this key X-RateLimit-Remaining: 187 # remaining in current window X-RateLimit-Reset: 1717156800 # Unix timestamp for window reset # On 429: Retry-After: 1 # seconds to wait before retrying

Retry Pattern

// Recommended retry with exponential backoff: async function apiRequest(url, options, attempt = 0) { const res = await fetch(url, options); if (res.status === 429) { const wait = parseInt(res.headers.get('Retry-After') || '1'); await sleep(wait * 1000 * Math.pow(2, attempt)); return apiRequest(url, options, attempt + 1); } return res; }

Sandbox & Testing

The sandbox environment uses your bed_test_ key and returns fixture data (Meridian Hotels group). No real bookings, no billing.

Sandbox Base URL
https://api-sandbox.athena-bedbank.com/v1

Fixture Hotels

Hotel IDNameCityStars
mh-meridian-001Meridian Grand IstanbulIstanbul, TR⭐⭐⭐⭐⭐
mh-meridian-002Meridian Boutique AnkaraAnkara, TR⭐⭐⭐⭐
mh-meridian-003Meridian Beach AntalyaAntalya, TR⭐⭐⭐⭐⭐
mh-meridian-004Meridian City RomeRome, IT⭐⭐⭐⭐
mh-meridian-005Meridian Luxury ParisParis, FR⭐⭐⭐⭐⭐

Sandbox Booking Behavior

Content API

Access hotel content: descriptions, photos, amenities, and room types. Content is updated periodically — you may cache responses for up to 24 hours.

Caching guidance: Content responses may be cached client-side for up to 24 hours. Use the updated_since parameter for delta synchronization.
GET/v1/hotels
List hotels with filters
GET/v1/hotels/{id}
Full hotel detail
GET/v1/hotels/{id}/rooms
Hotel room types
GET /v1/hotels

Returns a paginated list of hotels. Filter by country, city, category, or star rating. Use updated_since for incremental sync.

Query Parameters

ParameterTypeDescription
country_codestringISO 3166-1 alpha-2 (e.g. TR)
citystringCity name (partial match supported)
min_starsnumberMinimum star rating (1.0–5.0)
categorystringhotel · apartment · hostel · resort
qstringFull-text search on name and description
updated_sincedatetimeISO 8601 — return only hotels updated after this timestamp (delta sync)
pageintegerPage number, default 1
page_sizeintegerResults per page, default 100, max 1000

Example

curl -X GET \ "https://api.athena-bedbank.com/v1/hotels?country_code=TR&min_stars=4&page_size=50" \ -H "X-Api-Key: bed_live_your_key"

Response

{ "data": [ { "id": "mh-a1b2c3d4", "name": "Swissotel The Bosphorus", "country_code": "TR", "city": "Istanbul", "latitude": 41.0422, "longitude": 29.0073, "star_rating": 5.0, "category": "hotel" } ], "total": 1842, "page": 1, "page_size": 50 }
GET /v1/hotels/{id}

Returns full hotel details including description, amenities, photos, and room types. Specify lang for localized content.

Path Parameters

ParameterTypeDescription
id requiredUUIDHotel ID from /v1/hotels list
langstringContent language: en (default) · tr · de · fr · es · ar
// GET /v1/hotels/mh-a1b2c3d4?lang=en { "id": "mh-a1b2c3d4", "name": "Swissotel The Bosphorus", "description": "A landmark luxury hotel overlooking the Bosphorus strait...", "amenities": ["wifi", "pool", "spa", "gym", "restaurant", "bar"], "photos": [ { "url": "https://cdn.athena-bedbank.com/hotels/mh-a1b2c3d4/01.jpg", "caption": "Exterior" }, { "url": "https://cdn.athena-bedbank.com/hotels/mh-a1b2c3d4/02.jpg", "caption": "Pool" } ], "rooms": [ { "id": "mh-r4521", "name": "Deluxe Room City View", "max_occupancy": 2 }, { "id": "mh-r4522", "name": "Suite Bosphorus View", "max_occupancy": 3 } ] }

Cache API

Pre-computed availability and pricing data. Designed for high-volume search operations.

⚠️ Do not cache responses: Despite the name, Cache API responses must not be stored client-side. Pricing and availability change frequently. Always fetch fresh data per user search.
GET /v1/availability

Returns availability and pricing for up to 50 hotels for a given date range. p99 target: 100ms.

Query Parameters

ParameterTypeDescription
hotel_ids requiredstringComma-separated hotel UUIDs, max 50
check_in requireddateYYYY-MM-DD
check_out requireddateYYYY-MM-DD (must be after check_in)
adults requiredintegerNumber of adults (1–8)
currencystringISO 4217 currency code, default EUR
// Response — array of availability results per hotel [ { "hotel_id": "mh-a1b2c3d4", "hotel_name": "Swissotel The Bosphorus", "check_in": "2026-06-15", "check_out": "2026-06-18", "nights": 3, "rooms": [ { "room_id": "mh-r4521", "room_name": "Deluxe Room City View", "board": "BB", "currency": "EUR", "net_rate_cents": 18000, "total_cents": 54000, "available": true, "cancellation_policy": "Free cancellation until 48h before check-in" } ] } ]

Booking API

Create, retrieve, modify, and cancel bookings. Every create request performs a live availability check.

⚠️ Expect 409 Conflicts: Because we check live availability at booking time, another customer may have booked the same room between your availability query and booking request. Always handle 409 room_unavailable by re-querying availability and presenting alternatives.
POST /v1/bookings

Create a booking. Live availability is checked at request time. Pass Idempotency-Key to safely retry without duplicating bookings.

Request Body

FieldTypeDescription
hotel_id requiredUUIDHotel ID
room_id requiredUUIDRoom type ID from availability response
check_in requireddateYYYY-MM-DD
check_out requireddateYYYY-MM-DD
board requiredstringRO · BB · HB · FB · AI
guests requiredarrayAt least one adult guest with first_name, last_name, type
customer_reference requiredstringYour own booking reference (stored for reconciliation)
special_requestsstringOptional free-text requests for the hotel
// Request curl -X POST https://api.athena-bedbank.com/v1/bookings \ -H "X-Api-Key: bed_live_your_key" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: bkg-suntours-20260615-001" \ -d '{ "hotel_id": "mh-a1b2c3d4", "room_id": "mh-r4521", "check_in": "2026-06-15", "check_out": "2026-06-18", "board": "BB", "customer_reference": "ST-BKG-4821", "guests": [ { "first_name": "Maria", "last_name": "Garcia", "type": "adult" }, { "first_name": "Carlos", "last_name": "Garcia", "type": "adult" } ] }' // 201 Created { "id": "bk-x9y8z7w6", "reference": "ATH-2026-88421", "hotel_name": "Swissotel The Bosphorus", "room_name": "Deluxe Room City View", "check_in": "2026-06-15", "check_out": "2026-06-18", "nights": 3, "board": "BB", "total_cents": 54000, "currency": "EUR", "status": "confirmed", "cancellation_deadline": "2026-06-13T23:59:59Z" }

Quick Start

From zero to your first booking in 5 steps.

Step 1 — Validate your key
curl -X POST https://api-sandbox.athena-bedbank.com/v1/session/validate \ -H "X-Api-Key: bed_test_your_sandbox_key"
Step 2 — List hotels
curl "https://api-sandbox.athena-bedbank.com/v1/hotels?country_code=TR&page_size=10" \ -H "X-Api-Key: bed_test_your_sandbox_key"
Step 3 — Check availability
curl "https://api-sandbox.athena-bedbank.com/v1/availability?hotel_ids=mh-meridian-001&check_in=2026-07-01&check_out=2026-07-04&adults=2" \ -H "X-Api-Key: bed_test_your_sandbox_key"
Step 4 — Create a booking
curl -X POST https://api-sandbox.athena-bedbank.com/v1/bookings \ -H "X-Api-Key: bed_test_your_sandbox_key" \ -H "Content-Type: application/json" \ -d '{"hotel_id":"mh-meridian-001","room_id":"mh-r-m001","check_in":"2026-07-01","check_out":"2026-07-04","board":"BB","customer_reference":"TEST-001","guests":[{"first_name":"Test","last_name":"User","type":"adult"}]}'
Step 5 — Complete certification
Once all sandbox test cases pass in your Customer Portal, contact support@athena-bedbank.com to request production access. Your account manager will issue a bed_live_ key within 1 business day.

Handling 409 Conflicts

A 409 on POST /v1/bookings means the room was booked by another customer between your availability check and booking request. This is normal in a multi-tenant marketplace.

// Recommended flow: async function bookWithRetry(params) { // 1. Check availability const avail = await getAvailability(params); const room = avail[0].rooms.find(r => r.available); if (!room) return { error: 'no_rooms' }; // 2. Attempt booking const res = await createBooking({ ...params, room_id: room.room_id }); // 3. Handle 409 — re-query and show alternatives if (res.status === 409) { const freshAvail = await getAvailability(params); return { alternatives: freshAvail[0].rooms.filter(r => r.available) }; } return res.json(); }
GET/v1/hotels/{id}/rooms

Returns all room types for a given hotel.

// GET /v1/hotels/mh-a1b2c3d4/rooms [ { "id": "mh-r4521", "name": "Deluxe Room City View", "bed_configuration": "1 King Bed", "max_occupancy": 2 }, { "id": "mh-r4522", "name": "Suite Bosphorus View", "bed_configuration": "1 King Bed + Living Area", "max_occupancy": 3 } ]
POST/v1/availability/batch

Query availability for multiple hotel/date combinations in a single request. Max 100 queries per call.

{ "queries": [ { "hotel_ids": ["mh-a1b2c3d4"], "check_in": "2026-06-15", "check_out": "2026-06-18", "adults": 2 }, { "hotel_ids": ["mh-e5f6g7h8"], "check_in": "2026-06-20", "check_out": "2026-06-23", "adults": 2 } ] }
GET/v1/rates

Returns detailed nightly rate breakdown for a specific hotel/room/date combination.

// GET /v1/rates?hotel_id=mh-a1b2c3d4&room_id=mh-r4521&check_in=2026-06-15&check_out=2026-06-18 { "hotel_id": "mh-a1b2c3d4", "room_id": "mh-r4521", "net_rate_cents": 18000, "total_cents": 54000, "currency": "EUR", "available": true, "night_breakdown": [ { "date": "2026-06-15", "rate_cents": 17000 }, { "date": "2026-06-16", "rate_cents": 18000 }, { "date": "2026-06-17", "rate_cents": 19000 } ] }
GET/v1/bookings/{ref}

Retrieve a booking by its Athena reference number.

// GET /v1/bookings/ATH-2026-88421 { "id": "bk-x9y8z7w6", "reference": "ATH-2026-88421", "status": "confirmed", "check_in": "2026-06-15", "check_out": "2026-06-18", "total_cents": 54000, "cancellation_deadline": "2026-06-13T23:59:59Z" }
DELETE/v1/bookings/{ref}

Cancel a booking. Returns 409 if the cancellation deadline has passed.

// 200 OK — cancellation confirmed { "reference": "ATH-2026-88421", "status": "cancelled", "refund_cents": 54000, "cancelled_at": "2026-06-12T09:31:00Z" } // 409 — past deadline { "code": "cancellation_expired", "message": "Cancellation deadline was Jun 13 23:59 UTC" }

Pagination

All list endpoints use page-based pagination.

// Request: GET /v1/hotels?page=2&page_size=100 // Response envelope: { "data": [ ... ], "total": 1842, // total matching records "page": 2, // current page "page_size": 100 // records per page }

Total pages = Math.ceil(total / page_size). For large catalogs, use updated_since on the Content API for incremental sync instead of paginating the full catalog each time.