Думаю, вы правильно мыслите, то есть надо просто добавить пару проверок
Я тоже предпочитаю curl, а не Guzzle и им подобные.
Моя проверка как правило так выглядит:
// двойная инверсия как раз про "если хоть одно не выполнилось"
if (! (! curl_errno($ch)
&& 200 === curl_info($ch, CURLINFO_HTTP_CODE)
&& strlen(curlmulti_get_content($ch)) // для wordpress вместо strlen будет empty() т.к. их апишка ещё цифру 0 возвращает, когда отработало, но плохо
)) {
// ... retry
}
И я оставил только CURLOPT_CONNECT_TIMEOUT, т.к. CURLOPT_TIMEOUT - это время на выполнение функции полностью (сломало меня, когда я много запросов запустил и поставил тайаут 5, так все курлы не успевали выполнится в мульти), вне зависимости от сети.
Так же у меня тоже была проблема, что курл единожды использованный нельзя просто "запустить еще раз", надо клонировать, а ресурс - не клонируется. Решил её через свою обертку curlBlueprint куда я напихиваю опции, а потом создаю ресурсы при помощи широко-понятных методов get/post/put... в итоге запросить повторно для меня это $chNew = $cbp->{get|post}(curl_getinfo($chUsed, CURLINFO_EFFECTIVE_URL));
https://github.com/6562680/support посмотрите реализацию XCurl.
При этом единственное, что отличает "юзаный курл" от "не юзаного" - это total_time, который у свежего строго 0.0. Ну и да, код ошибки тоже у свежего 0.