HTTP clients with HTTP::Tiny

Contents
A GET request
POST and forms
Reading the response
Timeouts, redirects and TLS

A GET request

HTTP::Tiny is the HTTP client that ships with Perl. It has no dependencies for plain HTTP, handles redirects, and returns a small hash instead of an object graph.

use strict; use warnings; use HTTP::Tiny; use JSON::PP; my $ua = HTTP::Tiny->new( agent => "perl-course/1.0", timeout => 10, ); my $res = $ua->get("https://httpbin.org/json"); die "request failed: $res->{status} $res->{reason}\n" unless $res->{success}; my $data = JSON::PP->new->utf8->decode($res->{content}); print "keys: ", join(", ", sort keys %$data), "\n";

keys: slideshow

The response is a hash reference with status, reason, content, headers and a boolean success. Check success before reading content: an error page is still valid text, and decoding HTML as JSON produces a confusing message.

Pass a second hash reference to add request headers. The header names are case-insensitive, and values are joined with commas when repeated:

my $res = $ua->get($url, { headers => { Accept => "application/json", Authorization => "Bearer $token", }, });

POST and forms

post takes the same second argument plus a content key for the raw body. JSON APIs want an explicit content type, otherwise the server may reject or misread the payload:

my $body = JSON::PP->encode_json({ title => "Ship the course", done => JSON::PP::false, }); my $res = $ua->post("https://api.example.com/notes", { headers => { "Content-Type" => "application/json" }, content => $body, });

HTML forms use a different encoding: keys and values joined with ampersands and escaped. post_form does that encoding for you, so you do not have to remember the rules:

my $res = $ua->post_form("https://example.com/login", { user => "ada", pass => $secret, });

Every method has a generic form as well: $ua->request("DELETE", $url), or "PATCH" with a body. Use it for methods that have no named helper.

Reading the response

Status codes tell you what happened, and they are worth handling by class rather than one by one. A four-hundred series means the request was wrong; a five-hundred series means the server failed; three-hundred series are redirects that HTTP::Tiny follows for you.

if ($res->{status} == 429) { die "rate limited, retry later\n"; } elsif ($res->{status} >= 500) { die "server error: $res->{status} $res->{reason}\n"; }

Headers arrive lowercased, which saves you from guessing the server's capitalisation. Content type tells you how to parse the body:

my $type = $res->{headers}{"content-type"} // ""; if ($type =~ m{application/json}) { $data = JSON::PP->new->utf8->decode($res->{content}); }

When the request never reaches the server — a bad host name, a connection refused, a timeout — the status is 599 and the reason explains why. That is not an HTTP status, it is HTTP::Tiny's way of reporting a transport failure, so test for it explicitly.

Timeouts, redirects and TLS

The default timeout is sixty seconds, which is far too long for a script that talks to an API in a loop. Set a short timeout and decide what a failure means for your program.

HTTPS needs IO::Socket::SSL and Net::SSLeay. They are available from CPAN, and some distributions ship them by default. Certificate verification is on unless you turn it off, and you should not turn it off.

cpanm IO::Socket::SSL

my $ua = HTTP::Tiny->new( timeout => 5, max_redirect => 0, verify_SSL => 1, );

Setting max_redirect => 0 makes a redirect visible instead of following it, which is useful when you need the Location header. Proxies are read from http_proxy, https_proxy and no_proxy in the environment, so a script that works on your laptop also works behind the office proxy.

For cookies, multipart uploads or connection reuse, move to LWP::UserAgent or Mojo::UserAgent. HTTP::Tiny is deliberately small: it covers simple JSON and form APIs, and it covers them well.

Next: Sockets and IO::Socket

Article author: Arthur Isaev

Related articles
Perl
Perl course: zero to hero in 50 lessons
What is Perl and where it runs
Install Perl and check the version
Your first Perl script
The debugger as a REPL
Scalars and sigils
Numbers
Strings
Interpolation and quoting
Arrays
push, pop, shift and splice
Hashes
keys, values, exists and delete
Scalar and list context
Conditions and comparison
while, for, next and last
foreach and the default variable
Subroutines
Arguments in @_
References
Nested data structures
Regular expressions: match and anchors
Captures and named groups
Substitution with s///
split and join
grep and map
Sort and the Schwartzian transform
Read and write files
File tests and metadata
Directories and paths
die, warn and eval
Modules and Exporter
CPAN and cpanm
Objects with bless
Modern objects with Moo
Testing with Test::More
strict, warnings and diagnostics
Options with Getopt::Long
Environment and %ENV
system, backticks and pipes
One-liners with -n and -p
CSV and TSV processing
JSON with JSON::PP
HTTP clients with HTTP::Tiny
Sockets and IO::Socket
Databases with DBI
Capstone: todo CLI
Capstone: log report
Performance and profiling
Modern Perl tour
Hero roadmap: the whole course on one page
Install Perl on Windows 11
Install Perl on Ubuntu
Install Perl on Rocky Linux
Install Perl on macOS
Install Perl on FreeBSD
Perl in Docker

Search this site

Channel @aofeed Chat @aofeedchat

Contacts and cooperation:
I recommend our hosting beget.com
Write to info@aredel.com if you:
1. Want to write an article for our site or translate an article into your native language.
2. Want to place thematically relevant ads on the site.
3. Ads on my site pass maximum censorship. If you see an ad block unsuitable for school-age children, shocking or misleading - please contact us by e-mail
4. Found a mistake, inaccuracy, bug, etc. on the site. ... .......
5. Articles can be shared on social media by clicking a network icon: