> For the complete documentation index, see [llms.txt](https://syticks.gitbook.io/merpi-by-syticks/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://syticks.gitbook.io/merpi-by-syticks/api-reference/bus-ticketing-routes-terminals-schedules-buses-purchase-etc/get-schedules-endpoint-with-extra-information-v2.md).

# Get Schedules Endpoint With Extra Information (V2)

Before we get into the endpoint itself, let's talk about what this is actually for and when you should use it.

Most transport booking flows are multi-step: your customer picks a route, selects a date, chooses a schedule, picks a bus, selects seats, and then checks out. That works well if you want to build a full discovery experience. But a lot of merchants don't need all of that. They just want to show customers a clean list of available routes and let them book quickly.

That's exactly what this endpoint is for. It gives you a ready-made list of default routes and schedules. You show them to your customer, they tap one, and you go straight to the buy ticket endpoint. No multi-step flow, no unnecessary back and forth.

### Understanding Schedule Types

The response from this endpoint can include two types of schedules. How you present them to your customers should be different depending on which type you're working with.

#### **Timed Schedules**

These are for formal transport companies running fixed departures, usually on long-distance routes. Think GUO Transport running Lagos to Abuja: the bus leaves at 6:00 AM and that's that. Passengers need to be there on time or they miss it.

For timed schedules, the response gives you a single `bus` object and a fixed `time` with both departure and arrival times.

#### **Random Schedules**

These work more like how most motor parks operate. There's no fixed departure time. Buses fill up as passengers arrive, and once a bus is full, it goes. Think Star Sunny Motors running Awka to Nnewi: passengers can show up at any reasonable point during the day and find a bus leaving soon.

For random schedules, the response gives you a `buses` array with multiple options and an `operating_hours` window instead of a fixed time. A single random schedule might look like this in practice:

* Morning window (5 AM to 12 PM): Shuttle buses with 7 seats at ₦1,500 per person
* Evening window (12 PM to 10 PM): Hiace buses with 14 seats at ₦1,200 per person

### One Thing Some of Our Merchants Often Get Wrong

This is worth paying attention to before you build your integration.

When this endpoint returns schedules, each one comes back as its own object in the response array. If a transport company runs three buses on the same route at different times, say 5:30 AM, 6:00 AM, and 7:00 AM, you get three separate schedule objects. They share the same `route_id` because they are all on the same route, but each one has its own `id`, departure time, and bus details.

The mistake we see merchants make is treating these as completely separate, unrelated options. They list them individually in their UI and the customer ends up seeing something like this:

* Iyana-Ipaja to Kubwa
* Iyana-Ipaja to Kubwa
* Iyana-Ipaja to Kubwa

Three identical-looking cards with no context. That's confusing for your customers and makes your platform look broken.

What you should do instead is use the `route_id` to group schedules that belong to the same route, then present the departure times as selectable options under one route. Your customer should see something like this:

**Iyana-Ipaja to Kubwa**\
Choose a departure time: 5:30 AM / 6:00 AM / 7:00 AM

Each of those times maps to a different schedule `id`. When your customer picks one, that's the `schedule_id` you pass to the buy ticket endpoint. The grouping is a UI decision on your end; the API returns schedules individually and you group them before displaying.

Here's how to do that in code:

```javascript
const schedules = data.data.schedules;

// Group schedules by route_id
const groupedByRoute = schedules.reduce((acc, schedule) => {
  const routeId = schedule.route.id;
  if (!acc[routeId]) {
    acc[routeId] = {
      from: schedule.route.from.city.name,
      to: schedule.route.to.city.name,
      schedules: []
    };
  }
  acc[routeId].schedules.push(schedule);
  return acc;
}, {});

// Render each route group with its departure options
Object.values(groupedByRoute).forEach(route => {
  console.log(`${route.from} to ${route.to}`);
  route.schedules.forEach(schedule => {
    if (schedule.schedule_type === 'timed') {
      console.log(`  - ${schedule.time.departure} (${schedule.bus.name})`);
      console.log(`    Schedule ID: ${schedule.id}`);
    } else {
      console.log(`  - ${schedule.operating_hours.start} to ${schedule.operating_hours.end}`);
    }
  });
});
```

**Endpoint**

```
GET /api/v2/merpi/transport/schedules/packages
```

## Get bus schedules by route, departure and arrival city, terminal ID, business ID and departure date (V2 - Supports both Timed and Random schedules)

> This endpoint retrieves available bus schedules based on the provided route ID, departure and arrival city ID, terminal ID, business ID and departure date. This V2 endpoint supports both Timed Schedules (fixed departure times) and Random Schedules  (flexible timing with operating windows). The response structure varies based on the schedule\_type field.\
> \
> \*\*Seat Availability Note\*\*: For timed schedules, the \`available\_seats\` array in the bus object  is only included when the \`departure\_date\` parameter is provided. This array shows the real-time  seat layout and availability for the specified date, allowing users to see which seats are already  booked and select from available seats. Without the \`departure\_date\` parameter, seat availability  information is not included.<br>

```json
{"openapi":"3.0.3","info":{"title":"Syticks API","version":"2.0.0"},"paths":{"/api/v2/merpi/transport/schedules/packages":{"get":{"summary":"Get bus schedules by route, departure and arrival city, terminal ID, business ID and departure date (V2 - Supports both Timed and Random schedules)","description":"This endpoint retrieves available bus schedules based on the provided route ID, departure and arrival city ID, terminal ID, business ID and departure date. This V2 endpoint supports both Timed Schedules (fixed departure times) and Random Schedules  (flexible timing with operating windows). The response structure varies based on the schedule_type field.\n\n**Seat Availability Note**: For timed schedules, the `available_seats` array in the bus object  is only included when the `departure_date` parameter is provided. This array shows the real-time  seat layout and availability for the specified date, allowing users to see which seats are already  booked and select from available seats. Without the `departure_date` parameter, seat availability  information is not included.\n","operationId":"getBusSchedulesByRouteAndDateV2","parameters":[{"name":"route_id","in":"query","required":false,"schema":{"type":"integer"},"description":"ID of the route to fetch schedules for."},{"name":"departure_date","in":"query","required":false,"schema":{"type":"string","format":"date"},"description":"Departure date for which to retrieve schedules (format: YYYY-MM-DD).\n**Important**: When this parameter is provided for timed schedules, the response includes the `available_seats` array in the bus object, showing the seat layout with real-time availability for that specific date. This allows users to see which seats are already booked and select from available seats.\n"},{"name":"from_city_id","in":"query","required":false,"schema":{"type":"integer"},"description":"ID of the city where the trip originates."},{"name":"to_city_id","in":"query","required":false,"schema":{"type":"integer"},"description":"ID of the destination city."},{"name":"business_id","in":"query","required":false,"schema":{"type":"string"},"description":"ID of the business offering the schedule (UUID format)."},{"name":"terminal_id","in":"query","required":false,"schema":{"type":"integer"},"description":"ID of the terminal associated with the departure."}],"responses":{"200":{"description":"List of bus schedules (both Timed and Random).","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status":{"type":"integer"},"message":{"type":"string"},"data":{"type":"object","properties":{"schedules":{"type":"array","items":{"oneOf":[{"$ref":"#/components/schemas/TimedSchedule"},{"$ref":"#/components/schemas/RandomSchedule"}]}},"next_page":{"type":"string","nullable":true},"count":{"type":"integer"},"per_page":{"type":"integer"}}}}}}}},"400":{"description":"Bad request due to invalid input parameters.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status":{"type":"integer"},"message":{"type":"string"}}}}}},"404":{"description":"No schedules found matching the criteria.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status":{"type":"integer"},"message":{"type":"string"}}}}}},"500":{"description":"Internal server error.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status":{"type":"integer"},"message":{"type":"string"}}}}}}}}}},"components":{"schemas":{"TimedSchedule":{"type":"object","description":"Schedule with fixed departure times (Timed Schedule)","properties":{"id":{"type":"integer"},"name":{"type":"string"},"slug":{"type":"string"},"from":{"type":"string"},"to":{"type":"string"},"days":{"type":"object","properties":{"monday":{"type":"integer"},"tuesday":{"type":"integer"},"wednesday":{"type":"integer"},"thursday":{"type":"integer"},"friday":{"type":"integer"},"saturday":{"type":"integer"},"sunday":{"type":"integer"}}},"route":{"$ref":"#/components/schemas/Route"},"terminal":{"$ref":"#/components/schemas/Terminal"},"business":{"$ref":"#/components/schemas/Business"},"time":{"type":"object","properties":{"departure":{"type":"string"},"arrival":{"type":"string"}}},"bus":{"type":"object","description":"Bus information for the timed schedule. The `available_seats` array is only included when the `departure_date` query parameter is provided.","properties":{"id":{"type":"integer"},"name":{"type":"string"},"image":{"type":"string"},"seats":{"type":"integer","description":"Total number of seats in the bus."},"price":{"type":"string"},"available_seats":{"type":"array","description":"Multi-dimensional array representing the bus seat layout with real-time availability.\n**Only included when `departure_date` parameter is provided in the request.**\nEach outer array represents a row, and each inner object represents a seat position.\nThe `seat` field indicates if it's an actual seat (true) or empty space (false).\nThe `available` field shows if the seat can be booked for the specified date.\n","items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer","description":"Unique identifier for the seat."},"seat":{"type":"boolean","description":"Whether this position is an actual seat (true) or empty space (false)."},"row":{"type":"integer","description":"Row number in the bus layout."},"column":{"type":"integer","description":"Column number in the bus layout."},"available":{"type":"boolean","description":"Whether the seat is available for booking on the specified date."}}}}},"total_seats":{"type":"integer","description":"Total number of actual seats (excluding empty spaces)."}}},"amount":{"type":"string","description":"Total amount for the ticket including all fees."},"price_breakdown":{"type":"object","description":"Breakdown of pricing components.","properties":{"ticket_price":{"type":"number","description":"Base ticket price."},"convenience_fee":{"type":"number","description":"Convenience fee charged."},"merchant_commission":{"type":"number","description":"Commission paid to merchant."}}}}},"Route":{"type":"object","properties":{"id":{"type":"integer"},"price":{"type":"number"},"schedule_type":{"type":"string","enum":["timed","random"],"description":"Type of schedule - 'timed' for fixed departure times, 'random' for flexible schedules."},"from":{"type":"object","properties":{"address":{"type":"string","nullable":true},"city":{"$ref":"#/components/schemas/City"}}},"to":{"type":"object","properties":{"address":{"type":"string","nullable":true},"city":{"$ref":"#/components/schemas/City"}}}}},"City":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"state":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"}}}}},"Terminal":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"address":{"type":"string"},"location":{"type":"string"},"slug":{"type":"string","nullable":true}}},"Business":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"photo":{"type":"string","nullable":true},"slug":{"type":"string"},"type":{"type":"string"}}},"RandomSchedule":{"type":"object","description":"Schedule with flexible timing windows (Random Schedule)","properties":{"id":{"type":"integer"},"name":{"type":"string"},"slug":{"type":"string"},"from":{"type":"string"},"to":{"type":"string"},"days":{"type":"object","properties":{"monday":{"type":"integer"},"tuesday":{"type":"integer"},"wednesday":{"type":"integer"},"thursday":{"type":"integer"},"friday":{"type":"integer"},"saturday":{"type":"integer"},"sunday":{"type":"integer"}}},"route":{"$ref":"#/components/schemas/Route"},"terminal":{"$ref":"#/components/schemas/Terminal"},"business":{"$ref":"#/components/schemas/Business"},"amount":{"type":"string","description":"Total amount for the ticket including all fees."},"price_breakdown":{"type":"object","description":"Breakdown of pricing components.","properties":{"ticket_price":{"type":"number","description":"Base ticket price."},"convenience_fee":{"type":"number","description":"Convenience fee charged."},"merchant_commission":{"type":"number","description":"Commission paid to merchant."}}},"buses":{"type":"array","description":"Array of buses operating on this random schedule with their time windows.","items":{"type":"object","properties":{"bus_id":{"type":"integer"},"start_time":{"type":"string","format":"time","description":"Start time of the operating window."},"end_time":{"type":"string","format":"time","description":"End time of the operating window."},"bus_type":{"type":"string"},"price":{"type":"string"},"image":{"type":"string"}}}},"operating_hours":{"type":"object","properties":{"start":{"type":"string","format":"time"},"end":{"type":"string","format":"time"}}}}}}}}
```

**Query Parameters**

| Parameter        | Type          | Required    | Description                                                                                         | Example                              |
| ---------------- | ------------- | ----------- | --------------------------------------------------------------------------------------------------- | ------------------------------------ |
| `route_id`       | Integer       | No          | ID of the route to fetch schedules for                                                              | 11                                   |
| `departure_date` | String        | Recommended | Departure date in YYYY-MM-DD format. Highly recommended to avoid post-purchase date change requests | 2024-12-25                           |
| `from_city_id`   | Integer       | No          | ID of the city where the trip originates                                                            | 12                                   |
| `to_city_id`     | Integer       | No          | ID of the destination city                                                                          | 3                                    |
| `business_id`    | String (UUID) | No          | ID of the business offering the schedule                                                            | 6e9d9432-2a1d-4a8a-a5e4-3ddfaf832523 |
| `terminal_id`    | Integer       | No          | ID of the terminal associated with the departure                                                    | 7                                    |

#### Get bus schedules by route, departure and arrival city, terminal ID, business ID and departure date (V2 - Supports both Timed and Random schedules)

> This endpoint retrieves available bus schedules based on the provided route ID, departure and arrival city ID, terminal ID, business ID and departure date. This V2 endpoint supports both Timed Schedules (fixed departure times) and Random Schedules (flexible timing with operating windows). The response structure varies based on the schedule\_type field.
>
> **Seat Availability Note**: For timed schedules, the `available_seats` array in the bus object is only included when the `departure_date` parameter is provided. This array shows the real-time seat layout and availability for the specified date, allowing users to see which seats are already booked and select from available ones. Without the `departure_date` parameter, seat availability information is not included.

### Response Fields

**Common Fields (Both Schedule Types)**

| Field             | Type          | Description                                                                                            |
| ----------------- | ------------- | ------------------------------------------------------------------------------------------------------ |
| `schedules`       | Array         | List of available bus schedules (both Timed and Random)                                                |
| `id`              | Integer       | Schedule ID. This is what you pass to the buy ticket endpoint when a customer selects a departure      |
| `name`            | String        | Schedule name                                                                                          |
| `slug`            | String        | Unique schedule identifier                                                                             |
| `schedule_type`   | String        | Either `"timed"` or `"random"`. Always check this field before parsing the rest of the schedule object |
| `from` / `to`     | String        | Departure and arrival cities                                                                           |
| `days`            | Object        | Operating days where 1 means active and 0 means inactive                                               |
| `route`           | Object        | Complete route details including addresses and city information                                        |
| `route.id`        | Integer       | The route ID. Use this to group schedules that belong to the same route before displaying them         |
| `route.price`     | Number        | Base route price                                                                                       |
| `terminal`        | Object        | Departure terminal information                                                                         |
| `business`        | Object        | Transport company details                                                                              |
| `amount`          | String        | Total ticket amount including all fees                                                                 |
| `price_breakdown` | Object        | Breakdown of ticket price, convenience fee, and merchant commission                                    |
| `next_page`       | String / null | Pagination cursor for the next page of results                                                         |
| `count`           | Integer       | Total number of schedules returned                                                                     |
| `per_page`        | Integer       | Number of results per page (default: 20)                                                               |

**Timed Schedule Specific Fields**

| Field                 | Type    | Description                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| --------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `time`                | Object  | Fixed departure and arrival times for this schedule                                                                                                                                                                                                                                                                                                                                                                                                |
| `time.departure`      | String  | Fixed departure time (e.g., "07:00 AM")                                                                                                                                                                                                                                                                                                                                                                                                            |
| `time.arrival`        | String  | Expected arrival time (e.g., "03:00 PM")                                                                                                                                                                                                                                                                                                                                                                                                           |
| `bus`                 | Object  | The single bus assigned to this timed schedule                                                                                                                                                                                                                                                                                                                                                                                                     |
| `bus.id`              | Integer | Bus ID                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `bus.name`            | String  | Bus name or type                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| `bus.image`           | String  | Bus image URL                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `bus.seats`           | Integer | Total number of seats on the bus                                                                                                                                                                                                                                                                                                                                                                                                                   |
| `bus.price`           | String  | Additional bus-specific pricing if applicable                                                                                                                                                                                                                                                                                                                                                                                                      |
| `bus.available_seats` | Array   | Multi-dimensional array showing the seat layout with real-time availability. Only included in the response when `departure_date` is provided in the request. Each row in the outer array represents a physical row in the bus, and each object inside it represents a seat position with `id`, `seat` (true if it's an actual seat, false if it's an empty space), `row`, `column`, and `available` (whether it can still be booked for that date) |
| `bus.total_seats`     | Integer | Total number of bookable seats, excluding empty spaces in the layout                                                                                                                                                                                                                                                                                                                                                                               |

**Random Schedule Specific Fields**

| Field                   | Type    | Description                                                                                                                 |
| ----------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------- |
| `buses`                 | Array   | List of buses operating during different time windows on this schedule                                                      |
| `buses[].bus_id`        | Integer | Bus ID for this time window                                                                                                 |
| `buses[].start_time`    | String  | Start of the operating window for this bus (24-hour format, e.g., "06:00:00")                                               |
| `buses[].end_time`      | String  | End of the operating window for this bus (24-hour format, e.g., "23:36:00")                                                 |
| `buses[].bus_type`      | String  | Type or name of the bus                                                                                                     |
| `buses[].price`         | String  | Price for this bus. For random schedules, always use this field as the price for the route, not the top-level `route.price` |
| `buses[].image`         | String  | Bus image URL                                                                                                               |
| `operating_hours`       | Object  | The overall operating window for the schedule across all buses                                                              |
| `operating_hours.start` | String  | Earliest departure time (24-hour format)                                                                                    |
| `operating_hours.end`   | String  | Latest departure time (24-hour format)                                                                                      |

### Understanding the Response Structure

#### **How to Identify Schedule Type**

Always check the `schedule_type` field before you do anything else with a schedule object:

```javascript
if (schedule.schedule_type === 'timed') {
  // Use schedule.time.departure and schedule.time.arrival
  // Use schedule.bus for bus information
  // Show a fixed departure time to the customer
} else if (schedule.schedule_type === 'random') {
  // Use schedule.buses array for available buses
  // Use schedule.operating_hours for the time window
  // Show flexible timing to the customer
}
```

#### **For Timed Schedules**

Show your customer the fixed departure time from `time.departure` and the bus details from `bus`. Make it clear they need to arrive before that departure time.

#### **For Random Schedules**

Show the operating window from `operating_hours` and the available bus options from the `buses` array with their individual time windows. Let customers know they can arrive flexibly within that window.

### Integration Examples

**Handling Mixed Results with Route Grouping**

```javascript
const response = await fetch('/api/v2/merpi/transport/schedules/packages?...');
const data = await response.json();

// Step 1: Group schedules by route_id before displaying
const groupedByRoute = data.data.schedules.reduce((acc, schedule) => {
  const routeId = schedule.route.id;
  if (!acc[routeId]) {
    acc[routeId] = {
      from: schedule.route.from.city.name,
      to: schedule.route.to.city.name,
      schedules: []
    };
  }
  acc[routeId].schedules.push(schedule);
  return acc;
}, {});

// Step 2: Render each route with its departure options
Object.values(groupedByRoute).forEach(route => {
  console.log(`Route: ${route.from} to ${route.to}`);

  route.schedules.forEach(schedule => {
    if (schedule.schedule_type === 'timed') {
      console.log(`  Departs at: ${schedule.time.departure}`);
      console.log(`  Bus: ${schedule.bus.name} (${schedule.bus.seats} seats)`);
      console.log(`  Schedule ID: ${schedule.id}`);
    } else if (schedule.schedule_type === 'random') {
      console.log(`  Operating hours: ${schedule.operating_hours.start} - ${schedule.operating_hours.end}`);
      schedule.buses.forEach(bus => {
        console.log(`  - ${bus.bus_type}: ${bus.start_time} to ${bus.end_time} at ₦${bus.price}`);
      });
    }
  });
});
```

### Important Notes

**Always pass the departure date.** Include `departure_date` in your request. Without it, timed schedules won't return seat availability data, which means your customers can't see which seats are already taken. It also prevents situations where a customer purchases a ticket for the wrong date and has to reach out to change it.

**Check `schedule_type` before parsing.** Never assume all schedules in a response are the same type. Always read `schedule_type` first and branch your logic from there.

**Group schedules by `route_id` before displaying.** Schedules on the same route come back as individual objects. If you display them separately without grouping, your customers will see duplicate-looking route cards with no clear way to tell them apart. Group by `route_id` and show departure times as selectable options under a single route. This is one of the most common integration mistakes we see.

**Use `schedule.id` for purchase.** When a customer picks a departure time, the `id` of that schedule object is the `schedule_id` you pass to the buy ticket endpoint.

**For random schedules, use `buses[].price` not `route.price`.** Each bus on a random schedule can have its own pricing. Always read the price from the specific bus object the customer selects, not from the top-level route price.

**Check operating days.** Use the `days` object to filter out schedules that don't run on the customer's chosen day before displaying them. A schedule with `sunday: 0` should not appear as an option for a Sunday departure.

### Migration from V1

If you're migrating from the V1 endpoint, here's what needs to change:

1. **Update the endpoint URL**: Change `/api/v1/...` to `/api/v2/...`
2. **Add schedule type handling**: Read `schedule_type` on every schedule object and implement separate rendering logic for each
3. **Update how you read the response**:
   * Timed schedules use `bus` (single object) and `time`
   * Random schedules use `buses` (array) and `operating_hours`
4. **Group by route before displaying**: Add the grouping logic described above so customers see route cards with departure options, not a flat list of duplicate-looking schedules
5. **Update pricing logic for random schedules**: Read price from `buses[].price` not `route.price`
6. **Test both types**: Make sure your integration handles both timed and random schedules correctly before going live
