Documentation is currently in beta. Report issues →

Listings API

Endpoints for browsing and retrieving accommodation listings.

List Accommodations

Retrieve a paginated list of accommodations with optional filters.

GET /api/listings

Query Parameters

ParameterTypeDefaultDescription
typestring-Filter by type: ACCOMMODATION, ACTIVITY, EXPERIENCE
regionstring-Filter by region (e.g., “Greater Accra”)
citystring-Filter by city (e.g., “Accra”)
minPricenumber-Minimum price in GHS
maxPricenumber-Maximum price in GHS
bedroomsnumber-Minimum number of bedrooms
guestsnumber-Minimum guest capacity
amenitiesstring-Comma-separated amenity IDs
checkInstring-ISO date for availability check
checkOutstring-ISO date for availability check
sortstringnewestSort: price_asc, price_desc, newest, popular
pagenumber1Page number
limitnumber20Items per page (max 50)

Response

{
  "data": [
    {
      "id": "clx123abc...",
      "type": "ACCOMMODATION",
      "title": "Luxury Beach Villa",
      "slug": "luxury-beach-villa-accra",
      "description": "A stunning beachfront property...",
      "region": "Greater Accra",
      "city": "Accra",
      "area": "Labadi",
      "priceGHS": "850.00",
      "priceUnit": "PER_NIGHT",
      "bedrooms": 3,
      "bathrooms": 2,
      "maxGuests": 6,
      "featured": true,
      "images": [
        {
          "id": "clx456...",
          "url": "https://res.cloudinary.com/...",
          "isPrimary": true
        }
      ]
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 20,
    "total": 45,
    "totalPages": 3,
    "hasMore": true
  }
}

Example

# Get luxury accommodations in Accra
curl "https://y3nko.travel/api/listings?region=Greater%20Accra&minPrice=500&sort=price_desc"

Get Single Listing

Retrieve complete details for a specific listing.

GET /api/listings/{slug}

Path Parameters

ParameterTypeDescription
slugstringListing URL slug

Response

{
  "data": {
    "id": "clx123abc...",
    "type": "ACCOMMODATION",
    "status": "PUBLISHED",
    "title": "Luxury Beach Villa",
    "slug": "luxury-beach-villa-accra",
    "description": "A stunning beachfront property with panoramic ocean views...",
    "highlights": [
      "Private beach access",
      "Infinity pool",
      "24/7 security"
    ],
    "region": "Greater Accra",
    "city": "Accra",
    "area": "Labadi",
    "address": "123 Beach Road, Labadi",
    "latitude": 5.5560,
    "longitude": -0.1469,
    "priceGHS": "850.00",
    "priceUSD": "68.00",
    "priceUnit": "PER_NIGHT",
    "propertyType": "VILLA",
    "bedrooms": 3,
    "bathrooms": 2,
    "maxGuests": 6,
    "checkInTime": "14:00",
    "checkOutTime": "11:00",
    "viewCount": 1250,
    "bookingCount": 45,
    "images": [
      {
        "id": "clx456...",
        "url": "https://res.cloudinary.com/...",
        "alt": "Villa exterior",
        "isPrimary": true,
        "order": 0
      }
    ],
    "amenities": [
      {
        "amenity": {
          "id": "clx789...",
          "name": "WiFi",
          "icon": "wifi",
          "category": "Essentials"
        }
      },
      {
        "amenity": {
          "id": "clx790...",
          "name": "Swimming Pool",
          "icon": "swimming-pool",
          "category": "Comfort"
        }
      }
    ],
    "createdAt": "2024-01-15T10:30:00Z",
    "publishedAt": "2024-01-16T08:00:00Z"
  }
}

Errors

StatusCodeDescription
404NOT_FOUNDListing not found or not published

Check Availability

Check if dates are available and get pricing.

GET /api/listings/{slug}/availability

Query Parameters

ParameterTypeRequiredDescription
checkInstringYesISO date (e.g., “2024-03-15”)
checkOutstringYesISO date (e.g., “2024-03-18”)

Response

{
  "data": {
    "available": true,
    "unavailableDates": [],
    "pricing": {
      "nights": 3,
      "pricePerNight": 850,
      "subtotal": 2550,
      "serviceFee": 255,
      "taxes": 318.75,
      "total": 3123.75,
      "currency": "GHS"
    }
  }
}

If dates are unavailable:

{
  "data": {
    "available": false,
    "unavailableDates": ["2024-03-16", "2024-03-17"],
    "pricing": null
  }
}

Get featured listings for homepage display.

GET /api/listings/featured

Query Parameters

ParameterTypeDefaultDescription
typestring-Filter by type
limitnumber6Number of listings (max 12)

Response

{
  "data": [
    {
      "id": "clx123...",
      "title": "Luxury Beach Villa",
      "slug": "luxury-beach-villa-accra",
      "region": "Greater Accra",
      "priceGHS": "850.00",
      "images": [{ "url": "...", "isPrimary": true }]
    }
  ]
}

Search Listings

Full-text search across listings.

GET /api/listings/search

Query Parameters

ParameterTypeDescription
qstringSearch query
typestringFilter by type
limitnumberResults limit (max 20)

Response

{
  "data": [
    {
      "id": "clx123...",
      "title": "Beach Villa",
      "slug": "beach-villa-accra",
      "description": "Stunning beachfront...",
      "region": "Greater Accra",
      "priceGHS": "850.00"
    }
  ]
}

Implementation Notes

Server Action Alternative

For better integration with React, use the server action:

// lib/actions/listings.ts
"use server"
 
import { prisma } from '@/lib/db'
 
export async function getListings(filters: ListingFilters) {
  const where: Prisma.ListingWhereInput = {
    status: 'PUBLISHED',
    type: filters.type || undefined,
    region: filters.region || undefined,
    priceGHS: {
      gte: filters.minPrice,
      lte: filters.maxPrice
    }
  }
 
  const [listings, total] = await prisma.$transaction([
    prisma.listing.findMany({
      where,
      include: {
        images: { where: { isPrimary: true }, take: 1 }
      },
      orderBy: getSortOrder(filters.sort),
      skip: (filters.page - 1) * filters.limit,
      take: filters.limit
    }),
    prisma.listing.count({ where })
  ])
 
  return {
    listings,
    pagination: {
      page: filters.page,
      limit: filters.limit,
      total,
      totalPages: Math.ceil(total / filters.limit),
      hasMore: filters.page * filters.limit < total
    }
  }
}

Usage in Server Component

// app/accommodations/page.tsx
import { getListings } from '@/lib/actions/listings'
 
export default async function AccommodationsPage({ searchParams }) {
  const { listings, pagination } = await getListings({
    region: searchParams.region,
    minPrice: searchParams.minPrice,
    page: searchParams.page || 1,
    limit: 20
  })
 
  return <ListingGrid listings={listings} pagination={pagination} />
}