> 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.md).

# Get Schedules Endpoint

This endpoint is what you call when your customer selects a route and a date and you need to fetch the available schedules for that journey. Pass the route, terminal, and date, and it returns everything you need to present departure options to your customer and proceed to purchase.

Before you build your integration though, there are two things worth understanding: how schedule types work, and a mistake some of our merchants make when displaying results.

### Understanding Schedule Types

The response can include two types of schedules, and how you handle them in your UI should be different for each.

#### **Timed Schedules**

These are for transport companies running fixed departures, usually on longer routes. The bus leaves at a specific time and that's it. Passengers need to be there before the departure time or they miss it. For timed schedules, the response gives you a `time` object with a fixed `departure` and `arrival` time.

#### **Random Schedules**

These work the way most motor parks operate. There's no fixed departure time. Buses fill up as passengers arrive at the terminal, and when a bus is full, it leaves. Your customer picks a preferred time within an operating window rather than a fixed slot. For random schedules, the response gives you an `operating_hours` object instead of a fixed `time`.

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

This is important, so pay attention to it before you start building.

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 6:00 AM, 8:00 AM, and 10:00 AM, those come back as three separate schedule objects. They all share the same `route.id` because they are on the same route, but each has its own `id` and departure time.

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

* Awka to Iyana-Ipaja - 6:00 AM
* Awka to Iyana-Ipaja - 8:00 AM
* Awka to Iyana-Ipaja - 10:00 AM

Three identical-looking cards with no context. That's confusing and could make your platform look broken.

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

**Awka to Iyana-Ipaja**\
Choose a departure time: 6:00 AM / 8:00 AM / 10: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. Here's how to do that grouping in code:

```javascript
const grouped = 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;
}, {});

Object.values(grouped).forEach(route => {
  console.log(`${route.from} to ${route.to}`);
  route.schedules.forEach(schedule => {
    if (schedule.schedule_type === 'timed') {
      console.log(`  - Departs ${schedule.time.departure} (Schedule ID: ${schedule.id})`);
    } else {
      console.log(`  - Runs ${schedule.operating_hours.start} to ${schedule.operating_hours.end} (Schedule ID: ${schedule.id})`);
    }
  });
});
```

{% hint style="info" %}
Always pass both `route_id` and the `date` your customer selects. Without the date, the endpoint cannot determine availability or return the right schedules.
{% endhint %}

**Endpoint**

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

## Fetch Schedules Based on Route, Terminal, and Date

> This endpoint fetches a list of available schedules for a specific route, terminal, and date.\
> The response supports both Timed Schedules (fixed departure times) and Random Schedules (flexible \
> timing with operating windows). Always check the schedule\_type field to determine how to parse \
> each schedule object.<br>

```json
{"openapi":"3.0.3","info":{"title":"Syticks API","version":"2.0.0"},"paths":{"/api/v2/merpi/transport/schedules":{"get":{"summary":"Fetch Schedules Based on Route, Terminal, and Date","description":"This endpoint fetches a list of available schedules for a specific route, terminal, and date.\nThe response supports both Timed Schedules (fixed departure times) and Random Schedules (flexible \ntiming with operating windows). Always check the schedule_type field to determine how to parse \neach schedule object.\n","operationId":"getSchedules","parameters":[{"name":"route_id","in":"query","required":true,"schema":{"type":"integer"},"description":"ID of the route to fetch schedules for."},{"name":"terminal_id","in":"query","required":true,"schema":{"type":"integer"},"description":"ID of the terminal associated with the departure."},{"name":"date","in":"query","required":true,"schema":{"type":"string"},"description":"The date your customer wants to travel (format DD-MM-YYYY)."}],"responses":{"200":{"description":"List of bus schedules (both Timed and Random).","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Indicates whether the request was successful."},"status":{"type":"integer","description":"HTTP status code."},"message":{"type":"string","description":"Human-readable description of the response."},"data":{"type":"object","properties":{"schedules":{"type":"array","description":"A collection of schedule objects. Each object can be either a timed or random  schedule depending on the schedule_type field. Always check schedule_type before  parsing the rest of the object.\n","items":{"type":"object","properties":{"id":{"type":"integer","description":"Unique identifier for the schedule. Pass this as schedule_id when  calling the buy ticket endpoint.\n"},"name":{"type":"string","description":"Name of the schedule."},"from":{"type":"string","nullable":true,"description":"Departure city name."},"to":{"type":"string","nullable":true,"description":"Arrival city name."},"slug":{"type":"string","nullable":true,"description":"URL-friendly identifier for the schedule."},"schedule_type":{"type":"string","enum":["timed","random"],"description":"Type of schedule. timed means a fixed departure time. random means  flexible timing with an operating window. Always check this field  before parsing the rest of the schedule object.\n"},"time":{"type":"object","description":"Departure and arrival times. Only present for timed schedules.  Both fields can be null if not yet set by the transport company.\n","properties":{"departure":{"type":"string","nullable":true,"description":"Fixed departure time. Null if not set."},"arrival":{"type":"string","nullable":true,"description":"Expected arrival time. Null if not set."}}},"operating_hours":{"type":"object","description":"Operating window for random schedules. Only present for random schedules.\n","properties":{"start":{"type":"string","description":"Start of the operating window (24-hour format)."},"end":{"type":"string","description":"End of the operating window (24-hour format)."}}},"days":{"type":"object","description":"Days of the week the schedule operates. 1 means active, 0 means inactive.","properties":{"monday":{"type":"integer"},"tuesday":{"type":"integer"},"wednesday":{"type":"integer"},"thursday":{"type":"integer"},"friday":{"type":"integer"},"saturday":{"type":"integer"},"sunday":{"type":"integer"}}},"amount":{"type":"string","description":"Total ticket amount including all fees."},"price_breakdown":{"type":"object","description":"Breakdown of the ticket 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","description":"Unique identifier for the route. Use this to group schedules  that belong to the same route before displaying them to your customer.\n"},"price":{"type":"number","description":"Total price of the ticket for the route."},"schedule_type":{"type":"string","enum":["timed","random"],"description":"Type of schedule for this route."},"from":{"type":"object","properties":{"address":{"type":"string","nullable":true,"description":"Address of the departure location."},"city":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"state":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"}}}}}}},"to":{"type":"object","properties":{"address":{"type":"string","nullable":true,"description":"Address of the arrival location."},"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","nullable":true,"description":"Information about the transport business. Can be null.","properties":{"id":{"type":"string","description":"UUID of the business."},"name":{"type":"string"},"photo":{"type":"string"},"slug":{"type":"string"},"type":{"type":"string"}}}}}},"next_page":{"type":"string","nullable":true,"description":"Pagination cursor for the next page of results. Null if there are no more pages."},"count":{"type":"integer","description":"Total number of schedules available."},"per_page":{"type":"integer","description":"Number of schedules returned per page."}}}}}}}},"400":{"description":"Bad request due to invalid or missing parameters.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status":{"type":"integer"},"message":{"type":"string"}}}}}},"404":{"description":"No schedules found for the provided route and date.","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"}}}}}}}}}}}
```

#### **Query Parameters**

| Parameter     | Type    | Required | Description                                                    | Example    |
| ------------- | ------- | -------- | -------------------------------------------------------------- | ---------- |
| `route_id`    | Integer | Yes      | ID of the route to fetch schedules for                         | 17         |
| `terminal_id` | Integer | Yes      | ID of the terminal associated with the departure               | 11         |
| `date`        | String  | Yes      | The date your customer wants to travel, in `DD-MM-YYYY` format | 25-08-2024 |

#### Fetch Schedules Based on Route, Terminal, and Date

> This endpoint fetches a list of available schedules for a specific route, terminal, and date. The response supports both Timed Schedules (fixed departure times) and Random Schedules (flexible timing with operating windows). Always check the `schedule_type` field to determine how to parse each schedule object.

### Response Fields

**Common Fields (Both Schedule Types)**

| Field             | Type          | Description                                                                                                 |
| ----------------- | ------------- | ----------------------------------------------------------------------------------------------------------- |
| `schedules`       | Array         | List of available schedules. Each object can be either timed or random depending on `schedule_type`         |
| `id`              | Integer       | Schedule ID. Pass this as `schedule_id` when calling the buy ticket endpoint                                |
| `name`            | String        | Name of the schedule                                                                                        |
| `slug`            | String / null | URL-friendly identifier for the schedule                                                                    |
| `schedule_type`   | String        | Either `"timed"` or `"random"`. Always read this field first before parsing the rest of the schedule object |
| `from` / `to`     | String        | Departure and arrival city names                                                                            |
| `days`            | Object        | Days the schedule operates. `1` means active, `0` means inactive                                            |
| `route`           | Object        | Full route details including city and address information                                                   |
| `route.id`        | Integer       | Route ID. Use this to group schedules that belong to the same route before displaying them to your customer |
| `route.price`     | Number        | Base route price                                                                                            |
| `terminal`        | Object        | Departure terminal details including name, address, and location                                            |
| `business`        | Object / null | Transport company details. Can be null if no business is assigned                                           |
| `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. Null if there are no more results                                      |
| `count`           | Integer       | Total number of schedules available across all pages                                                        |
| `per_page`        | Integer       | Number of schedules returned per page                                                                       |

**Timed Schedule Specific Fields**

| Field            | Type          | Description                                                                                  |
| ---------------- | ------------- | -------------------------------------------------------------------------------------------- |
| `time`           | Object        | Contains the fixed departure and arrival times for this schedule                             |
| `time.departure` | String / null | Fixed departure time (e.g., "07:00 AM"). Can be null if not yet set by the transport company |
| `time.arrival`   | String / null | Expected arrival time (e.g., "03:00 PM"). Can be null if not yet set                         |

**Random Schedule Specific Fields**

| Field                   | Type   | Description                                                      |
| ----------------------- | ------ | ---------------------------------------------------------------- |
| `operating_hours`       | Object | The time window during which buses on this schedule are running  |
| `operating_hours.start` | String | Start of the operating window (24-hour format, e.g., "06:45:00") |
| `operating_hours.end`   | String | End of the operating window (24-hour format, e.g., "21:45:00")   |

### Important Notes

**Check `schedule_type` before parsing.** Timed schedules have a `time` object. Random schedules have `operating_hours`. Always read `schedule_type` first and branch your logic from there.

**Group by `route.id` before displaying.** Multiple schedules can share the same route. Display them as departure time options under one route card, not as separate route listings. This is the most common integration mistake we see and it directly affects how your customers experience your platform.

**Check operating days before displaying.** Use the `days` object to filter out schedules that don't run on your customer's chosen day before showing them as options.

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