How to Read HTTP Responses with UTL_HTTP.BEGIN_REQUEST

Call web services with full control: request headers, status codes, response headers, bodies, authentication, and redirects.

UTL_HTTP.REQUEST returns only a body. For real web service calls you need more: headers on the request, the status code and headers of the response, a body of any length, authentication, and redirects. BEGIN_REQUEST and GET_RESPONSE give that control.

Code for This Guide

The main examples are in the examples/pkg-files-network folder of the Oracle Database 26ai code repository on GitHub, each with its output. They use NIMBUS, the sample schema of a fictional airline, which you can install with the scripts in the setup/nimbus folder.

They come from Oracle Database 26ai SQL and PL/SQL Book.

Syntax

req  := utl_http.begin_request(url, method => 'GET');
utl_http.set_header(req, name, value);
utl_http.set_authentication(req, username, password);
resp := utl_http.get_response(req);
-- resp.status_code, resp.reason_phrase
utl_http.get_header(resp, n, name_out, value_out);
utl_http.read_line(resp, line_out, remove_crlf => true);   -- or read_text
utl_http.end_response(resp);

Reading past the end of the body raises UTL_HTTP.END_OF_BODY. Always call END_RESPONSE, or the connection stays open.

The examples call the local test server, setup/netlab/server.mjs in the repository.

Status, Headers, and Body

Example:

declare
  v_req   utl_http.req;
  v_resp  utl_http.resp;
  v_name  varchar2(256);
  v_value varchar2(1024);
  v_line  varchar2(1024);
begin
  v_req := utl_http.begin_request('http://host.docker.internal:8099/notices');
  utl_http.set_header(v_req, 'User-Agent', 'Nimbus-PLSQL/1.0');
  v_resp := utl_http.get_response(v_req);
  dbms_output.put_line('status: ' || v_resp.status_code || ' ' || v_resp.reason_phrase);
  for i in 1 .. utl_http.get_header_count(v_resp) loop
    utl_http.get_header(v_resp, i, v_name, v_value);
    if v_name like 'Content%' then
      dbms_output.put_line(v_name || ': ' || v_value);
    end if;
  end loop;
  begin
    loop
      utl_http.read_line(v_resp, v_line, remove_crlf => true);
      dbms_output.put_line('> ' || v_line);
    end loop;
  exception
    when utl_http.end_of_body then
      utl_http.end_response(v_resp);
  end;
end;
/

Output:

status: 200 OK
Content-Type: text/plain; charset=utf-8
> Gate B12: boarding from 21:30
> Lounge: open until 23:00
> Wi-Fi: NimbusGuest

PL/SQL procedure successfully completed.

The request sends a User-Agent header. The response has status 200 OK and a plain text content type, and READ_LINE returns the body line by line until END_OF_BODY, where the handler ends the response.

Authentication, Redirects, and Errors

Example:

declare
  v_req  utl_http.req;
  v_resp utl_http.resp;
  v_text varchar2(4000);
begin
  v_req := utl_http.begin_request('http://host.docker.internal:8099/reports/daily');
  utl_http.set_authentication(v_req, 'ops', 'ops-demo');            -- basic authentication
  v_resp := utl_http.get_response(v_req);
  utl_http.read_text(v_resp, v_text);
  utl_http.end_response(v_resp);
  dbms_output.put_line(v_resp.status_code || ': ' || v_text);

  v_req := utl_http.begin_request('http://host.docker.internal:8099/old-status');
  v_resp := utl_http.get_response(v_req);                     -- follows the redirect
  utl_http.read_text(v_resp, v_text);
  utl_http.end_response(v_resp);
  dbms_output.put_line('after redirect: ' || v_text);

  utl_http.set_detailed_excp_support(true);
  v_text := utl_http.request('http://host.docker.internal:8099/reports/daily');  -- 401 body
  dbms_output.put_line('without credentials: ' || v_text);
  v_text := utl_http.request('http://host.docker.internal:8100/');   -- no ACL for port 8100
exception
  when others then
    dbms_output.put_line(sqlerrm);
end;
/

Output:

200: {"date":"2026-03-15","flights":31,"on_time_pct":87.1}
after redirect: {"flight":"NM150","status":"ON TIME","gate":"B12","departs":"2026-03-15T22:10:00+13:00"}
without credentials: {"error":"authentication required"}
ORA-24247: network access denied by access control list (ACL)

PL/SQL procedure successfully completed.
  • SET_AUTHENTICATION sends basic authentication, and the protected report returns 200.
  • /old-status redirects, and GET_RESPONSE follows it to the flight status.
  • Without credentials, the server answers 401 with an error body.
  • Port 8100 has no access control entry, so the call fails with ORA-24247.

Things to Know

  • Check STATUS_CODE before using the body: 4xx and 5xx responses do not raise errors.
  • Keep passwords out of code: store them in a wallet and use SET_AUTHENTICATION_FROM_WALLET.
  • SET_TRANSFER_TIMEOUT limits how long a slow server can keep your session waiting.

Related Guides

Conclusion

BEGIN_REQUEST, GET_RESPONSE, and the read procedures give full control over HTTP calls: headers, status codes, bodies of any length, authentication, and redirects. Check the status, read until END_OF_BODY, and always end the response.

Get more Vinish.dev tutorials on Google

See my articles more often when you search Google.

Vinish Kapoor
Vinish Kapoor

An Oracle ACE, author of four books on Oracle APEX, SQL and PL/SQL, and Oracle Forms, and a software developer building Oracle database applications since 2001.

guest

0 Comments
Oldest
Newest Most Voted