Cohttp для HTTP клиентов и серверов 
Cohttp — решение для работы с HTTP: создание сетевых демонов, клиентов и сервер. Заточен под асинхронную работу, имеет для этого разные бекенды: Lwt, Async, Eio, libcurl (даже) и другие.
Это относительно "низкоуровневый" инструмент для веб-сервисов. Если вам интересна веб-разработка, то обратите внимание на Opium или Dream.
Пример использования 
Пример из документации по использованию Cohttp с бекендом на lwt_unix.
open Lwt
open Cohttp
open Cohttp_lwt_unix
let body =
  Client.get (Uri.of_string "https://www.reddit.com/") >>= fun (resp, body) ->
  let code = resp |> Response.status |> Code.code_of_status in
  Printf.printf "Response code: %d\n" code;
  Printf.printf "Headers: %s\n" (resp |> Response.headers |> Header.to_string);
  body |> Cohttp_lwt.Body.to_string >|= fun body ->
  Printf.printf "Body of length: %d\n" (String.length body);
  body
let () =
  let body = Lwt_main.run body in
  print_endline ("Received body\n" ^ body)Более комплексный пример можно найти всё в том же RWO, Searching Definitions with DuckDuckGo.
Lwt-бекенд 
Рекомендации по использованию 
Старайтесь использоваться cohttp-lwt в своих модулях, руководствуясь принципом инверсии зависимостей! Внедрять зависимости можно посредством функторов.
Библиотека-враппер API сервиса
module ServiceApiWrapper = struct
  module type S = sig
    val get_user_by_id : _ -> (_, _) result
  end
  module type Auth = sig
    val token : string
  end
  module Make (A : Auth) (C : Cohttp_lwt.S.Client) : S = struct
    let get_user_by_id _ = failwith "todo"
  end
  let token s =
    (module struct
      let token = s
    end : Auth)
endПользовательский код.
module ServiceApi =
  ServiceApiWrapper.Make
    (val ServiceApiWrapper.token "...")
    (Cohttp_lwt_unix.Client)Как вы видите, разработчик сам выбирает какую реализацию использовать, что актуально для желающих засунуть свой код в MirageOS.
Это актуально не только для библиотек, но и при разработки приложений.
HTTPS 
Для работы шифрованного обмена трафика у вас должен быть установлен один из пакетов: lwt_ssl или tls-lwt. Первый является биндингом к OpenSSL, второй же полностью реализован на OCaml, что добавляет в переносимости, но существенно увеличивает размер бинарника и возможно несколько снижает производительность.
Смотрите также
Подобная вариация существует благодаря библиотеке conduit, которая обеспечивает некоторую степень абстракции от конкретной используемой библиотеки SSL.
К сожалению, для multcore (aka Eio, Miou и т.д..) пока подобного нет.
Eio-бекенд 
Всем новичкам посвящается
Как выглядит любой, кто захотел уйти с Lwt на современный мульткор на базе эффектов.

Использовать ли в продакшене?
Вполне стабильно, но какой-либо документации по использованию Eio-бекенда нету. На GitHub не так много issue связанных с ним. Разбираться со всем придётся самостоятельно, либо просить помощи на форуме.
Примеры использования можно найти тут: https://github.com/mirage/ocaml-cohttp/tree/master/cohttp-eio/examples.
HTTPS 
Для настройки шифрования потребуется дополнительные телодвижение в отличие от стандартаного unix-бекенда, где всё работает из коробки.
Зависимости
- tls-eio— OCaml-реализация TLS
- mirage-crypto-rng-eio
- ca-certs— для загрузки системного сертификата
Пример HTTP-клиента
open Cohttp_eio
let https =
  (* Загрузка системного (корневого) сертификата.  *)
  let authenticator = Ca_certs.authenticator () |> Result.get_ok in
  let tls_config = Tls.Config.client ~authenticator () in
  fun uri raw ->
    let host =
      Uri.host uri
      |> Option.map (fun x -> Domain_name.(host_exn (of_string_exn x)))
    in
    Tls_eio.client_of_flow ?host tls_config raw
let main env =
  let client = Client.make ~https:(Some https) env#net in
  (* Switch управляет ресурсами, поэтому каждый запрос должен использовать новый switch. *)
  Eio.Switch.run @@ fun sw ->
  let resp, body =
    Client.get ~sw client (Uri.of_string "https://example.com")
  in
  (* ... *)
  if Http.Status.compare resp.status `OK = 0 then
    (* Чтение тела из потока. *)
    print_string @@ Eio.Buf_read.(parse_exn take_all) body ~max_size:max_int
  else Fmt.epr "Unexpected HTTP status: %a" Http.Status.pp resp.status
let () =
  Eio_main.run @@ fun env ->
  Mirage_crypto_rng_eio.run (module Mirage_crypto_rng.Fortuna) env @@ fun () ->
  main envЕсли вы планируете обращаться только к одному сервису, то можно убрать парсинг хоста и передавать в Tls_eio.client_of_flow просто константу.
Related 
Хотелось бы иметь иметь потоковый JSON-парсер для Lwt_stream, но оного я не нашёл в экосистеме (по крайней мере в меру живого). В примерах используют хороший Yojson, но он читает строки, хотя может большее.
Альтернативы 
Из треда Simple, modern HTTP client library?