コンテンツにスキップ

Hurl Cheat Sheet

Overview

Hurl is a command-line tool that runs HTTP requests defined in a simple plain-text format. It can perform requests, capture values, evaluate queries on headers and body, and chain requests together. Hurl is excellent for testing HTTP APIs, validating responses, and creating integration test suites that are easy to read, write, and version control.

Built on top of libcurl, Hurl supports HTTP/1.1, HTTP/2, HTTP/3, TLS, cookies, redirects, multipart forms, and more. It provides a rich set of assertions for status codes, headers, body content, JSONPath, XPath, regex, and duration checks. Hurl files are plain text, making them ideal for documentation and CI/CD integration.

Installation

# macOS
brew install hurl

# Ubuntu/Debian
curl -LO https://github.com/Orange-OpenSource/hurl/releases/download/5.0.1/hurl_5.0.1_amd64.deb
sudo dpkg -i hurl_5.0.1_amd64.deb

# Arch Linux
pacman -S hurl

# Cargo (Rust)
cargo install hurl

# npm
npm install -g @orangeopensource/hurl

# Docker
docker pull ghcr.io/orange-opensource/hurl

# Verify installation
hurl --version

Core Commands

FlagDescription
hurl file.hurlExecute a Hurl file
--testRun in test mode (summary output)
--report-html dirGenerate HTML report
--report-junit file.xmlGenerate JUnit report
--variable name=valueSet a variable
--variables-file fileLoad variables from file
--verboseShow request/response details
--very-verboseShow even more details including body
--jsonOutput results as JSON
--output fileWrite response body to file
--retry NRetry failed requests N times
--retry-interval MSWait MS between retries
--max-time SECSMaximum time for entire run
--connect-timeout SECSConnection timeout
--colorForce colored output

Basic Request Syntax

# Simple GET request
GET https://api.example.com/health

# GET with headers
GET https://api.example.com/users
Authorization: Bearer token123
Accept: application/json

# POST with JSON body
POST https://api.example.com/users
Content-Type: application/json
{
  "name": "John Doe",
  "email": "john@example.com"
}

# PUT request
PUT https://api.example.com/users/123
Content-Type: application/json
{
  "name": "Jane Doe"
}

# DELETE request
DELETE https://api.example.com/users/123
Authorization: Bearer token123

Assertions

# Status code assertion
GET https://api.example.com/health
HTTP 200

# Header assertions
GET https://api.example.com/users
HTTP 200
Content-Type: application/json
Cache-Control: no-cache

# Body assertions
GET https://api.example.com/health
HTTP 200
[Asserts]
status == 200
header "Content-Type" == "application/json"
body contains "healthy"
jsonpath "$.status" == "ok"
jsonpath "$.version" isString
jsonpath "$.uptime" > 0
duration < 500

JSONPath Assertions

GET https://api.example.com/users/123
HTTP 200
[Asserts]
jsonpath "$.id" == 123
jsonpath "$.name" == "John Doe"
jsonpath "$.email" matches "^[\\w.]+@[\\w.]+$"
jsonpath "$.active" == true
jsonpath "$.roles" count == 3
jsonpath "$.roles" includes "admin"
jsonpath "$.address.city" exists
jsonpath "$.deleted_at" not exists
jsonpath "$.tags[0]" == "premium"
jsonpath "$.metadata" isCollection

Capturing Values

# Login and capture token
POST https://api.example.com/auth/login
Content-Type: application/json
{
  "username": "admin",
  "password": "secret"
}
HTTP 200
[Captures]
auth_token: jsonpath "$.token"
user_id: jsonpath "$.user.id"

# Use captured values in subsequent requests
GET https://api.example.com/users/{{user_id}}
Authorization: Bearer {{auth_token}}
HTTP 200
[Asserts]
jsonpath "$.id" == {{user_id}}

Variables

# Using variables in requests
GET https://{{host}}/api/users/{{user_id}}
Authorization: Bearer {{api_token}}
HTTP 200
# Pass variables via command line
hurl --variable host=api.example.com \
     --variable user_id=123 \
     --variable api_token=mytoken \
     test.hurl

# Variables file (vars.env)
# host=api.example.com
# user_id=123
# api_token=mytoken
hurl --variables-file vars.env test.hurl

Form and File Uploads

# URL-encoded form
POST https://api.example.com/login
[FormParams]
username: admin
password: secret123

# Multipart form with file upload
POST https://api.example.com/upload
[MultipartFormData]
file: file,avatar.png; image/png
description: Profile photo
user_id: 123

Cookies

# Send cookies
GET https://api.example.com/dashboard
[Cookies]
session_id: abc123
theme: dark

# Assert on response cookies
GET https://api.example.com/login
HTTP 200
[Asserts]
cookie "session_id" exists
cookie "session_id[Secure]" exists
cookie "session_id[HttpOnly]" exists
cookie "session_id[Max-Age]" > 3600

Chaining Requests

# Create a resource
POST https://api.example.com/products
Content-Type: application/json
{
  "name": "Widget",
  "price": 9.99
}
HTTP 201
[Captures]
product_id: jsonpath "$.id"

# Verify the resource exists
GET https://api.example.com/products/{{product_id}}
HTTP 200
[Asserts]
jsonpath "$.name" == "Widget"
jsonpath "$.price" == 9.99

# Update the resource
PUT https://api.example.com/products/{{product_id}}
Content-Type: application/json
{
  "price": 12.99
}
HTTP 200

# Delete the resource
DELETE https://api.example.com/products/{{product_id}}
HTTP 204

# Verify deletion
GET https://api.example.com/products/{{product_id}}
HTTP 404

Advanced Usage

Retry and Polling

# Retry until condition is met
GET https://api.example.com/jobs/{{job_id}}/status
[Options]
retry: 10
retry-interval: 2000
HTTP 200
[Asserts]
jsonpath "$.status" == "completed"

XPath Assertions (HTML/XML)

GET https://example.com/page
HTTP 200
[Asserts]
xpath "//title" == "Welcome"
xpath "count(//div[@class='item'])" >= 5
xpath "//meta[@name='description']/@content" exists

Regex Assertions

GET https://api.example.com/version
HTTP 200
[Asserts]
body matches "^\\d+\\.\\d+\\.\\d+$"
header "X-Request-Id" matches "^[a-f0-9-]{36}$"

SSL/TLS Options

GET https://self-signed.example.com/api
[Options]
insecure: true
cacert: /path/to/ca-bundle.crt

Configuration

# Run in test mode with HTML report
hurl --test --report-html ./report tests/*.hurl

# JUnit report for CI
hurl --test --report-junit results.xml tests/*.hurl

# Glob pattern execution
hurl --test tests/**/*.hurl

# With timeout settings
hurl --connect-timeout 10 --max-time 120 test.hurl

Troubleshooting

IssueSolution
Could not resolve hostCheck URL and DNS; verify network connectivity
Assertion failedUse --verbose to see actual response data
Variable not foundCheck variable name spelling; ensure capture happens before use
SSL certificate errorUse --insecure for self-signed certs or provide CA with --cacert
Timeout errorsIncrease --connect-timeout or --max-time values
JSON parsing errorVerify response is valid JSON; check Content-Type header
Retry not workingEnsure [Options] section with retry is correctly indented
File upload failsCheck file path is relative to current directory