Версия 6.0 написана заново под новое API ticket.rzd.ru. Прежний протокол
pass.rzd.ru с обменом идентификатором запроса больше не используется, поэтому
совместимости с 5.x нет: изменились имена классов, способ вызова и формат
ответов.
Запрос стал один вместо двух. Прежний протокол требовал сначала получить
идентификатор RID и куки, выждать паузу и повторить запрос с ними. Новое API
отдаёт данные сразу, без сессии и повторов. Отсюда исчезли настройки
setRetryDelay и setRetryLimit
Методы возвращают объекты, а не строки JSON. Раньше каждый метод отдавал
строку, которую вызывающий код разбирал сам. Теперь возвращаются типизированные
модели, а полный ответ сайта доступен через raw и get().
HTTP-клиент передаётся снаружи. Библиотека работает с любой реализацией PSR-18 и больше не тянет Guzzle в зависимости. Таймаут и прокси настраиваются в клиенте.
Появились данные, которых прежнее API не давало: номера свободных мест по купе, схемы вагонов в SVG, фотографии салона, календарь цен по датам, справочник тарифов.
| 5.x | 6.0 |
|---|---|
$api->trainRoutes($params) |
$client->trains->search(new TrainSearch(...)) |
$api->trainRoutesReturn($params) |
$client->trains->searchReturn(new TrainSearch(...), $returnDate) |
$api->trainRoutes(['md' => 1]) |
$client->transfers->search(new TransferSearch(...)) |
$api->trainCarriages($params) |
$client->cars->search(CarSearch::forTrain($train)) |
$api->trainStationList($params) |
$client->routes->search(RouteSearch::forTrain($train)) |
$api->stationCode($params) |
$client->stations->suggest('ЧЕБ') |
| — | $client->cars->scheme(), schemeImage(), images() |
| — | $client->prices->availability(), calendar() |
| — | $client->references->tariffs(), appConfig() |
| — | $client->stations->popular(), byNodeId() |
Поиск туда-обратно устроен иначе. Прежний протокол искал пару маршрутов одним
запросом с параметром направления, новое API так не умеет: его собственная
страница туда-обратно отправляет два независимых запроса, второй с
переставленными станциями. Проверено — параметры второй даты (returnDate,
dateBack, departureDateBack) эндпоинт молча игнорирует, ответ не меняется.
searchReturn() делает эти два запроса за один вызов и возвращает RoundTrip:
$trip = $client->trains->searchReturn(
new Rzd\Request\TrainSearch(
origin: '2000000',
destination: '2004000',
date: new DateTimeImmutable('2026-08-01'),
adults: 2,
),
new DateTimeImmutable('2026-08-05'),
);
$trip->forward; // SearchResult туда
$trip->back; // SearchResult обратно
$trip->hasSeats(); // есть места в обе стороны
$trip->minPrice(); // минимальная стоимость поездки целикомПараметры обратного плеча — число пассажиров, поезда из расписания, группировка вагонов — повторяют первое, меняются только станции и дата.
// 5.x
$api = new Rzd\Api($config);
$routes = json_decode($api->trainRoutes([
'dir' => 0,
'tfl' => 3,
'checkSeats' => 1,
'code0' => 2000000,
'code1' => 2004000,
'dt0' => '01.08.2026',
]));
foreach ($routes->list as $train) {
echo $train->number, $train->time0, $train->date0;
}// 6.0
$client = new Rzd\Client();
$result = $client->trains->search(new Rzd\Request\TrainSearch(
origin: '2000000',
destination: '2004000',
date: new DateTimeImmutable('2026-08-01'),
));
foreach ($result as $train) {
echo $train->number, $train->departure->format('H:i'), $train->departure->format('d.m.Y');
}Параметры dir, tfl и checkSeats больше не нужны: они относились к прежнему
протоколу. Дата передаётся объектом DateTimeInterface, а не строкой в
формате сайта.
В 5.x пересадки включались параметром md того же запроса, а ответ приходил
в отдельном формате state: Transfers со списком cases, который библиотека
не разбирала.
// 5.x
$routes = json_decode($api->trainRoutes([
'dir' => 0,
'code0' => 2030319,
'code1' => 2038230,
'dt0' => '20.08.2026',
'md' => 1,
]));
foreach ($routes->tp[0]->list as $variant) {
echo $variant->midPt, $variant->totalTravelTime;
}// 6.0
$result = $client->transfers->search(new Rzd\Request\TransferSearch(
origin: '5a13bdc3340c745ca1e8aa54', // Новый Уренгой
destination: '5a13baab340c745ca1e7f31c', // Абакан
date: new DateTimeImmutable('2026-08-20'),
));
foreach ($result as $route) {
echo $route->changes(), $route->duration(), $route->minPrice;
}Города здесь задаются идентификаторами узлов сайта, а не кодами станций.
Готовый запрос собирается из подсказок:
TransferSearch::forStations($from, $to, $date).
Прежде коды станций и время отправления нужно было переносить из результатов поиска руками, причём коды требовались другие — конкретных вокзалов, а не города. Теперь запрос собирается из найденного поезда:
// 5.x
$carriages = json_decode($api->trainCarriages([
'dir' => 0,
'code0' => 2000003,
'code1' => 2060500,
'dt0' => $train->date0,
'time0' => $train->time0,
'tnum0' => $train->number,
]));
foreach ($carriages->cars as $car) {
echo $car->cnumber, $car->type, $car->freeSeats;
}// 6.0
$cars = $client->cars->search(Rzd\Request\CarSearch::forTrain($train));
foreach ($cars as $car) {
echo $car->number, $car->typeName, $car->places;
echo implode(', ', $car->placeNumbers()); // номеров мест раньше не было
}// 5.x
$stations = json_decode($api->trainStationList([
'trainNumber' => $train->number,
'depDate' => $train->date0,
]));
foreach ($stations->routes as $stop) { ... }// 6.0
$route = $client->routes->search(Rzd\Request\RouteSearch::forTrain($train));
foreach ($route as $stop) {
echo $stop->stationName, $stop->arrival?->format('H:i'), $stop->stopDuration;
}// 5.x
$stations = json_decode($api->stationCode(['stationNamePart' => 'ЧЕБ']), true);
echo $stations[0]['station'], $stations[0]['code'];// 6.0
$stations = $client->stations->suggest('ЧЕБ');
echo $stations[0]->name, $stations[0]->code;Поля переименованы: station стало name, codes и timezone сохранились,
добавился nodeId.
// 5.x
$config = new Rzd\Config();
$config->setProxy('socks5://127.0.0.1:1080');
$config->setTimeout(30);
$config->setRetryDelay(2);
$config->setUserAgent('MyApp/1.0');
$config->setLanguage('en');
$api = new Rzd\Api($config);// 6.0: сетевое — в HTTP-клиенте, остальное — в Config
$factory = new GuzzleHttp\Psr7\HttpFactory();
$client = new Rzd\Client(
config: new Rzd\Config(
language: Rzd\Enum\Language::English,
userAgent: 'MyApp/1.0',
),
httpClient: new GuzzleHttp\Client([
'proxy' => 'socks5://127.0.0.1:1080',
'timeout' => 30,
]),
requestFactory: $factory,
streamFactory: $factory,
);setRetryDelay и setRetryLimit удалены вместе с обменом идентификаторами.
setHandler тоже: подмена транспорта для тестов теперь делается передачей
подставного PSR-18 клиента.
// 5.x: любая ошибка приходила как RuntimeException
try {
$api->trainRoutes($params);
} catch (RuntimeException $e) {
echo $e->getMessage();
}// 6.0: недоступность сайта, блокировка и ошибка API различаются
try {
$client->trains->search($search);
} catch (Rzd\Exception\TransportException $e) {
// сайт недоступен, вне РФ это обычное дело
} catch (Rzd\Exception\ApiException $e) {
echo $e->statusCode(), $e->errorCode();
} catch (Rzd\Exception\RzdException $e) {
// всё остальное из библиотеки
}PHP поднят с 8.0 до 8.2. Guzzle из обязательных зависимостей удалён, вместо
него psr/http-client и psr/http-factory.
Ветка pass.rzd.ru работала на момент выпуска 6.0 и остаётся доступной в
версии 5.0.0:
composer require visavi/rzd-api:^5.0Новых данных туда не добавляется: сайт постепенно переносит функциональность
на ticket.rzd.ru.