Hurl is a command line tool that runs HTTP requests defined in a simple plain text format.
It can chain requests, capture values and evaluate queries on headers and body response. Hurl is very
versatile: it can be used for both fetching data and testing HTTP sessions.
Hurl makes it easy to work with HTML content, REST / SOAP / GraphQL APIs, or any other XML / JSON based APIs.
# Get home:GEThttps://example.orgHTTP200[Captures]csrf_token: xpath"string(//meta[@name='_csrf_token']/@content)"# Do login!POSThttps://example.org/login?user=toto&password=1234X-CSRF-TOKEN: {{csrf_token}}HTTP302
# A simple GET with headers GEThttps://example.org/newsUser-Agent: Mozilla/5.0Accept: */*Accept-Language: en-US,en;q=0.5Accept-Encoding: gzip, deflate, brConnection: keep-alive
# Chaining requests is easy, responses are optionalGEThttps://example.org/api/healthGEThttps://example.org/api/step1HTTP200GEThttps://example.org/api/step2GEThttps://example.org/api/step3
# Use query params in the URL:GEThttps://example.org/news?order=newest&search=something%20to%20search&count=100# Or with a query param section: GEThttps://example.org/news[QueryStringParams]order: newestsearch: something to searchcount: 100
GEThttps://example.org/protected[BasicAuth]bob: secret# One can also use an Authorization header# Authorization header value can be computed with `echo -n 'bob:secret' | base64`GEThttps://example.org/protectedAuthorization: Basic Ym9iOnNlY3JldA==
POSThttps://example.org/upload[MultipartFormData]field1: value1field2: file,example.txt;# One can specify the file content type:field3: file,example.zip; application/zip
# Requests in the same Hurl file share the cookie storage# Cookies can also be set per request GEThttp://localhost:8000/cookies/set-multiple-request-cookies[Cookies]user1: Bobuser2: Billuser3: {{name}}HTTP200# Or we can simply use a Cookie headerGEThttps://example.org/index.htmlCookie: theme=light; sessionToken=abc123
# Get home:GEThttps://example.orgHTTP200[Captures]csrf_token: xpath"string(//meta[@name='_csrf_token']/@content)"# Do login!POSThttps://example.org/login?user=toto&password=1234X-CSRF-TOKEN: {{csrf_token}}HTTP302
# Create a new doggy thing with JSON body:POSThttps://example.org/api/dogs{ "id": 0, "name": "Frieda", "picture": "images/scottish-terrier.jpeg", "age": 3, "breed": "Scottish Terrier", "location": "Lisco, Alabama"}
# GraphQL queries are supported, even with variables POSThttps://example.org/starwars/graphql```graphql{ human(id: "1000") { name appearsIn height(unit: FOOT) }}```
POSThttps://example.org# Some random comments before bodybase64,TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIG
FkaXBpc2NpbmcgZWxpdC4gSW4gbWFsZXN1YWRhLCBuaXNsIHZlbCBkaWN0dW0g
aGVuZHJlcml0LCBlc3QganVzdG8gYmliZW5kdW0gbWV0dXMsIG5lYyBydXRydW
0gdG9ydG9yIG1hc3NhIGlkIG1ldHVzLiA=;# Or use a filePOSThttps://example.orgfile,data.bin;
# Using implicit response asserts GEThttps://example.org/index.htmlHTTP200Set-Cookie: theme=lightSet-Cookie: sessionToken=abc123; Expires=Wed, 09 Jun 2021 10:18:14 GMT# Or explicit response assertsGEThttps://example.orgHTTP302[Asserts]header"Location"contains"www.example.net"
# Testing 200 OKGEThttps://example.org/order/435HTTP200# Testing status code is in a 200-300 rangeGEThttps://example.org/order/435HTTP*[Asserts]status>=200status<300
# You can explicitly test HTTP version 1.0, 1.1, 2 or 3:GEThttps://example.org/http3HTTP/3200GEThttps://example.org/http2HTTP/2200# Or simply use HTTP to not test version!GEThttps://example.org/http2HTTP200
# XPath asserts can be used to check HTML content GEThttps://example.orgHTTP200Content-Type: text/html; charset=UTF-8[Asserts]xpath"string(/html/head/title)"contains"Example"# Check titlexpath"count(//p)"==2# Check the number of pxpath"//p"count==2# Similar assert for pxpath"boolean(count(//h2))"==false# Check there is no h2 xpath"//h2"notexists# Similar assert for h2xpath"string(//div[1])"matches/Hello.*/
# Testing a JSON response with JSONPathGEThttps://example.org/api/tests/4567HTTP200[Asserts]jsonpath"$.status"=="RUNNING"# Check the status codejsonpath"$.tests"count==25# Check the number of itemsjsonpath"$.id"matches/\d{4}/# Check the format of the id
GEThttp://myserver.com/homeHTTP200[Asserts]cookie"JSESSIONID"=="8400BAFE2F66443613DC38AE3D9D6239"cookie"JSESSIONID[Value]"=="8400BAFE2F66443613DC38AE3D9D6239"cookie"JSESSIONID[Expires]"contains"Wed, 13 Jan 2021"cookie"JSESSIONID[Secure]"existscookie"JSESSIONID[HttpOnly]"existscookie"JSESSIONID[SameSite]"=="Lax"
# Check the SHA-256 response body hash: GEThttps://example.org/data.tar.gzHTTP200[Asserts]sha256== hex,039058c6f2c0cb492c533b0a4d14ef77cc0f78abccced5287d84a1a2011cfb81;# Checking Byte Order Mark (BOM) in Response BodyGEThttps://example.org/data.binHTTP200[Asserts]bytesstartsWith hex,efbbbf;
# Check attributes of the SSL certificate GEThttps://example.orgHTTP200[Asserts]certificate"Subject"=="CN=example.org"certificate"Issuer"=="C=US, O=Let's Encrypt, CN=R3"certificate"Expire-Date"daysAfterNow>15certificate"Serial-Number"matches/[\da-f]+/
# Pull job status until it is completedGEThttps://api.example.org/jobs/{{job_id}}[Options]retry: 10# maximum number of retry, -1 for unlimitedretry-interval: 300msHTTP200[Asserts]jsonpath"$.state"=="COMPLETED"
Hurl can run HTTP requests but can also be used to test HTTP responses.
Different types of queries and predicates are supported, from XPath and JSONPath on body response,
to assert on status code and response headers.
It is well adapted for REST / JSON APIs
POSThttps://example.org/api/tests{ "id": "4568", "evaluate": true}HTTP200[Asserts]header"X-Frame-Options"=="SAMEORIGIN"jsonpath"$.status"=="RUNNING"# Check the status codejsonpath"$.tests"count==25# Check the number of itemsjsonpath"$.id"matches/\d{4}/# Check the format of the id
Hurl is a lightweight binary written in Rust. Under the hood, Hurl HTTP engine is
powered by libcurl, one of the most powerful and reliable file transfer libraries.
With its text file format, Hurl adds syntactic sugar to run and test HTTP requests,
but it’s still the curl that we love: fast, efficient and HTTP/3 ready.