Skip to content

Your first working request

Use Protobuf for your application. Compact binary messages reduce data transfer and support efficient encoding and decoding. JSON is useful for inspecting requests and trying recipes.

Connect to the public Infoplaza WebSocket API and pass your Infoplaza API key in the api_key query parameter:

wss://api.infoplaza.com/v1/transit/implanner?api_key=YOUR_API_KEY
FormatQuery parametersRequest
Protobuf (default)api_key=YOUR_API_KEYEncoded planner.PlanRequest
Protobuf (explicit)api_key=YOUR_API_KEY&mode=protoEncoded planner.PlanRequest
JSONapi_key=YOUR_API_KEY&mode=jsonText envelope with type and payload

The mode parameter defaults to proto. Both formats provide the same planning features.

For a server application, store your key in the INFOPLAZA_API_KEY environment variable. The Node.js example reads it directly. For browser integrations, use your application’s approved API-key configuration.

Follow Generate TypeScript types to prepare the imports below.

These coordinates are public places near Utrecht Centraal and Utrecht Science Park. The JavaScript timestamp stays current when you run it.

Browser application module
import { planner } from "./generated/planner";
const endpoint = new URL("wss://api.infoplaza.com/v1/transit/implanner");
endpoint.searchParams.set("api_key", INFOPLAZA_API_KEY); // From your app configuration
const socket = new WebSocket(endpoint);
socket.binaryType = "arraybuffer";
socket.addEventListener("open", () => {
const request = planner.PlanRequest.create({
fromPlace: "52.0894,5.1102",
toPlace: "52.0925,5.1813",
timestamp: new Date().toISOString(),
arriveBy: false,
timeSlack: 0,
includePricing: true,
});
const bytes = planner.PlanRequest.encode(request).finish();
socket.send(new Uint8Array(bytes).buffer);
});
socket.addEventListener("message", (event) => {
if (typeof event.data === "string") {
console.error("Server message", JSON.parse(event.data));
return;
}
const result = planner.model.PlanResult.decode(new Uint8Array(event.data));
for (const trip of result.trips) {
console.log(
trip.expectedStartTime || trip.aimedStartTime,
trip.duration,
"minutes",
trip.transfers,
"transfers",
);
}
});
socket.addEventListener("error", () => console.error("Connection failed"));

One frame is one PlanResult, which can contain several trips. The first frame is not necessarily the final set of options. A successful search ends with a normal close (code 1000). A new search after that needs a new connection.

A normal close can still accompany no trips. Keep transport failure, routing errors and an empty search as separate UI states. See the lifecycle.

Journey options from Utrecht Centraal to Science Park.
Utrecht Centraal to Science Park

Journey options from Utrecht Centraal to Science Park.

  • Change arriveBy to true for an arrival deadline.
  • Add excludeModes: [planner.model.enumerations.TransitMode.RAIL] to search without trains.
  • Add useFlexibleRouting: true where flex services operate.
  • Set lessWalking: true to prefer less walking.
  • Use the recipe explorer for copyable combinations.