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.

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