Skip to content


Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?

Latest commit


Git stats


Failed to load latest commit information.
Latest commit message
Commit time


Clojars Project

A tiny curl wrapper via idiomatic Clojure, inspired by clj-http, Ring and friends.

NOTE: This library is mostly replaced by babashka.http-client which is mostly API-compatible with babashka.curl. The babashka.http-client library is built-in as of babashka version 1.1.171.


This library is part of babashka but can also be used with JVM Clojure. Check before upgrading as the API may still undergo some changes. Contributions welcome.


(require '[babashka.curl :as curl])
(require '[ :as io]) ;; optional
(require '[cheshire.core :as json]) ;; optional


Simple GET request:

(curl/get "")
;;=> {:status 200, :body "200 OK", :headers { ... }}


Passing headers:

(def resp (curl/get "" {:headers {"Accept" "application/json"}}))
(json/parse-string (:body resp)) ;;=> {"code" 200, "description" "OK"}

Query parameters

Query parameters:

  (curl/get "" {:query-params {"q" "clojure"}})
  (json/parse-string true)
;;=> {:q "clojure"}

To send multiple params to the same key:

(curl/get "" {:query-params [[:q "clojure"] [:q "curl"]]})


A POST request with a :body:

(def resp (curl/post "" {:body "From Clojure"}))
(json/parse-string (:body resp)) ;;=> {"args" {}, "data" "", ...}

Posting a file as a POST body:

(:status (curl/post "" {:body (io/file "")}))
;; => 200

Posting a stream as a POST body:

(:status (curl/post "" {:body (io/input-stream "")}))
;; => 200

Posting form params:

(:status (curl/post "" {:form-params {"name" "Michiel"}}))
;; => 200

Basic auth

Basic auth:

(:body (curl/get "" {:basic-auth ["postman" "password"]}))
;; => "{\"authenticated\":true}"

Download binary

Download a binary file:

  (:body (curl/get ""
    {:as :bytes}))
  (io/file "icon.png"))
(.length (io/file "icon.png"))
;;=> 7748


With :as :stream

(:body (curl/get ""
    {:as :stream}))

will return the raw input stream.

Passing through arguments

Passing raw arguments to curl can be done with :raw-args:

(:status (curl/get "" {:raw-args ["--max-redirs" "0"] :throw false}))
;;=> 301

Unix sockets

Talking to a UNIX socket:

(-> (curl/get "http://localhost/images/json"
              {:raw-args ["--unix-socket"
    (json/parse-string true)
;;=> ["babashka/babashka:0.0.79-SNAPSHOT"]

URL construction

Using the low-level API for fine grained(and safer) URL construction:

(-> (curl/request {:url {:scheme "https"
                         :host   ""
                         :port   443
                         :path   "/get"
                         :query  "q=test"}})
    (json/parse-string true))
{:args {:q "test"},
 {:Accept "*/*",
  :Host "",
  :User-Agent "curl/7.64.1",
 :origin "",
 :url ""}


Redirects are automatically followed. To opt out of this behaviour, set :follow-redirects to false.

(curl/get "" {:follow-redirects false})


An ExceptionInfo will be thrown for all HTTP response status codes other than #{200 201 202 203 204 205 206 207 300 301 302 303 304 307} or if curl exited with a non-zero exit code. The response map is the exception data.

(curl/get "")
;;=> Execution error (ExceptionInfo) at babashka.curl/request (curl.clj:228).
     status 404

(:status (ex-data *e))
;;=> 404

To opt out of an exception being thrown, set :throw to false.

(:status (curl/get "" {:throw false}))
;;=> 404

If the body is being returned as a stream then exceptions are never thrown and the :exit value is wrapped in a Delay.

(:exit (curl/get "" {:as :stream}))
;;=> #object[clojure.lang.Delay 0x75769ab0 {:status :pending, :val nil}]
(force *1)
;;=> 0

Error output

Error output can be found under the :err key:

(:err (curl/get "httpx://" {:throw false}))
;;=> "curl: (1) Protocol \"httpx\" not supported or disabled in libcurl\n"


From babashka 0.2.4 onwards, this library will call curl with --compressed by default. To opt out, pass :compressed false in the options. On Windows 10 the default installed curl does not support this option. You can either upgrade curl or perform all requests using :compressed false.

Debugging requests

Set :debug to true to get debugging information along with the response. The :command value contains the command that was executed to obtain the response. The :options value contains options that were used to construct the command. Note that all of these values are for debugging only and contain implementation details that may change in the future.

(def resp (curl/head "" {:debug true}))
(:command resp)
;;=> ["curl" "--silent" "--show-error" "--location" "--dump-header" "/var/folders/2m/h3cvrr1x4296p315vbk7m32c0000gp/T/babashka.curl16567082489957878064.headers" "--head" ""]
(:options resp)
;;=> {:debug true, :url "", :method :head, :header-file #object[ 0x61d34b4 "/var/folders/2m/h3cvrr1x4296p315vbk7m32c0000gp/T/babashka.curl16567082489957878064.headers"]}


$ clojure -A:test


Copyright © 2020 Michiel Borkent

Distributed under the EPL License, same as Clojure. See LICENSE.