Конечно контроллер - не место для подобных действий.
Как правильно указал
Владимир Коротенко - перенос кода, отвечающего за работу с внешним API в отдельный сервис - это первое что вам нужно сделать. Это действие открывает для вас массу возможностей: подмена сервиса для разных сценариев, использование заглушек для тестов, вызовы вне контроллеров и т.д. Кроме того сам код при этом становится более простым и поддерживаемым, а также лучше соответствует принципам SOLID.
Если после выделения сервиса вы поймёте что для вас необходима возможность изменения его реализации - важно выделить ключевые методы в интерфейс и использовать именно интерфейс для внедрения зависимостей в те же контроллеры.
Для реализации самих запросов внутри сервиса логично использовать
Symfony HTTP Client.
После этого у вас возможны различные сценарии в зависимости от того что же этот API делает.
Если вы забираете из API данные для подготовки своего ответа и эти данные меняются часто и должны быть всегда актуальны (к примеру какое-нибудь значение котировки акции или курса валюты) - вы вызываете ваш сервис напрямую, а он делает запрос и возвращает ответ.
Если вы забираете из API данные для подготовки своего ответа, но сами эти данные меняются не постоянно - логично будет добавить в ваш сервис поддержку кэширования с использованием
Symfony Cache.
Если вы забираете данные из API или отправляете данные туда, но они не зависят от контекста текущего запроса - этот процесс логичнее всего вынести в консольную команду, реализованную через
Symfony Console и дёргать её по cron'у.
Альтернативно, если вы используете Symfony 4.4 или 5.x - вы можете организовать этот процесс через новый
Symfony Messenger. Он же будет лучшим вариантом если, к примеру, запросов много или они тяжёлые и вам нужно организовать их распределение.