Skip to content

How to call a REST API from ABAP

Make GET and POST requests to external HTTP/HTTPS endpoints using CL_HTTP_CLIENT.

Introduction

ABAP programs frequently need to integrate with external services:

  • Third-party APIs — fetch exchange rates, validate addresses, call payment gateways.
  • Microservices — call a lightweight service that performs business logic outside SAP.
  • Webhooks and notifications — POST event data to an external system when a SAP document is created or changed.

The standard class for outbound HTTP calls is CL_HTTP_CLIENT. It is available from SAP Basis 6.20 onwards and supports HTTP and HTTPS. For managed connections (with a stored URL, credentials, and SSL settings) use an SM59 RFC destination and CREATE_BY_DESTINATION. For ad-hoc URLs use CREATE_BY_URL.

SSL certificates must be imported before HTTPS calls work

If the target endpoint uses HTTPS, import its root CA certificate into transaction STRUST (SSL client identity ANONYM). Without this, every HTTPS call raises an SSL handshake error. See the Troubleshooting section.


Prerequisites

  • RFC destination configured in SM59 (type G — HTTP connection to external server) — recommended for all non-trivial integrations.
  • Root CA certificate of the target server imported in STRUST (for HTTPS).
  • Outbound firewall rule allowing the SAP application server to reach the target host and port.
  • /UI2/CL_JSON available (standard in ECC 7.40+ and all S/4HANA releases) if you need JSON serialisation.

Step 1 — Create the HTTP client

Create a client object that holds the connection parameters. Prefer CREATE_BY_DESTINATION so that the URL, port, and credentials are managed in SM59 without code changes.

DATA lo_client TYPE REF TO if_http_client.

" Option A: SM59 destination (recommended)
cl_http_client=>create_by_destination(
  EXPORTING
    destination              = 'MY_API_DEST'   " SM59 destination name
  IMPORTING
    client                   = lo_client
  EXCEPTIONS
    argument_not_found       = 1
    destination_not_found    = 2
    destination_no_authority = 3
    OTHERS                   = 4 ).

IF sy-subrc <> 0.
  MESSAGE 'HTTP client creation failed — check SM59 destination.' TYPE 'E'.
ENDIF.

" Option B: direct URL (quick tests, no SM59 needed)
cl_http_client=>create_by_url(
  EXPORTING
    url    = 'https://api.example.com/data'
  IMPORTING
    client = lo_client ).

Always prefer SM59 destinations in production

Hard-coding URLs in ABAP makes environment switches (dev → QA → prod) error-prone. Store the base URL in SM59 and change it per system without transport.


Step 2 — Set the HTTP method and request headers

Configure the HTTP method and any required headers before sending. The most common headers are Accept (what format you expect back) and Content-Type (what format you are sending).

" Set HTTP method
lo_client->request->set_method( if_http_request=>co_request_method_get ).

" Set request headers
lo_client->request->set_header_field(
  name  = 'Accept'
  value = 'application/json' ).

lo_client->request->set_header_field(
  name  = 'Authorization'
  value = 'Bearer eyJhbGciOi...' ).    " replace with actual token

Step 3 — For POST: set the request body

For POST, PATCH, or PUT requests, set the Content-Type header and write the request body using set_cdata (for text/JSON) or set_data (for binary).

DATA lv_json_body TYPE string.

" Build a simple JSON payload — use /UI2/CL_JSON for complex structures
lv_json_body = '{"orderId":"12345","status":"confirmed"}'.

lo_client->request->set_method( if_http_request=>co_request_method_post ).

lo_client->request->set_header_field(
  name  = 'Content-Type'
  value = 'application/json' ).

lo_client->request->set_cdata( lv_json_body ).

Use /UI2/CL_JSON for JSON serialisation and deserialisation

/UI2/CL_JSON=>serialize( data = ls_my_structure ) converts any ABAP structure or table to a JSON string. /UI2/CL_JSON=>deserialize( json = lv_json changing data = ls_result ) does the reverse. Available in ECC 7.40 SP08+ and all S/4HANA systems.


Step 4 — Send the request and receive the response

SEND writes the request to the network socket; RECEIVE blocks until the full response is available.

lo_client->send(
  EXCEPTIONS
    http_communication_failure = 1
    http_invalid_state         = 2
    OTHERS                     = 3 ).

IF sy-subrc <> 0.
  lo_client->close( ).
  MESSAGE 'HTTP send failed — check network connectivity.' TYPE 'E'.
ENDIF.

lo_client->receive(
  EXCEPTIONS
    http_communication_failure = 1
    http_invalid_state         = 2
    http_processing_failed     = 3
    OTHERS                     = 4 ).

IF sy-subrc <> 0.
  lo_client->close( ).
  MESSAGE 'HTTP receive failed.' TYPE 'E'.
ENDIF.

Step 5 — Read the HTTP status and response body

Check the status code before trusting the body — a 4xx or 5xx response may still have a body with an error message.

DATA: lv_http_code TYPE i,
      lv_reason    TYPE string,
      lv_body      TYPE string.

lo_client->response->get_status(
  IMPORTING
    code   = lv_http_code
    reason = lv_reason ).

lv_body = lo_client->response->get_cdata( ).

IF lv_http_code <> 200.
  WRITE: / |HTTP { lv_http_code } { lv_reason }|.
  WRITE: / lv_body.
ENDIF.

Step 6 — Close the connection

Always close the client — even on error. A FINALLY block is the safest place.

lo_client->close( ).

Leaking HTTP client objects exhausts connection pools

Each open CL_HTTP_CLIENT holds a network socket. If close( ) is never called (e.g. because an exception exits the method early), the connection leaks. Always call close( ) in a CLEANUP or FINALLY block.


Full working example

REPORT z_http_get_demo.

PARAMETERS p_url TYPE string DEFAULT 'https://jsonplaceholder.typicode.com/todos/1'.

START-OF-SELECTION.

  DATA: lo_client    TYPE REF TO if_http_client,
        lv_http_code TYPE i,
        lv_reason    TYPE string,
        lv_body      TYPE string.

  TRY.
      cl_http_client=>create_by_url(
        EXPORTING url    = p_url
        IMPORTING client = lo_client ).

      lo_client->request->set_method(
        if_http_request=>co_request_method_get ).

      lo_client->request->set_header_field(
        name  = 'Accept'
        value = 'application/json' ).

      lo_client->send(
        EXCEPTIONS
          http_communication_failure = 1
          OTHERS                     = 2 ).
      IF sy-subrc <> 0. RAISE EXCEPTION TYPE cx_sy_no_handler. ENDIF.

      lo_client->receive(
        EXCEPTIONS
          http_communication_failure = 1
          http_processing_failed     = 2
          OTHERS                     = 3 ).
      IF sy-subrc <> 0. RAISE EXCEPTION TYPE cx_sy_no_handler. ENDIF.

      lo_client->response->get_status(
        IMPORTING code = lv_http_code reason = lv_reason ).

      lv_body = lo_client->response->get_cdata( ).

      WRITE: / |Status: { lv_http_code } { lv_reason }|.
      WRITE: / 'Response body:'.
      WRITE: / lv_body.

    CATCH cx_root INTO DATA(lx).
      WRITE: / 'Error:', lx->get_text( ).
    CLEANUP.
      IF lo_client IS BOUND.
        lo_client->close( ).
      ENDIF.
  ENDTRY.
REPORT z_http_post_demo.

START-OF-SELECTION.

  DATA: lo_client    TYPE REF TO if_http_client,
        lv_http_code TYPE i,
        lv_reason    TYPE string,
        lv_body      TYPE string,
        lv_payload   TYPE string.

  " Build JSON payload — for complex structures use /UI2/CL_JSON=>serialize
  lv_payload = '{"title":"Buy milk","completed":false,"userId":1}'.

  TRY.
      cl_http_client=>create_by_url(
        EXPORTING url    = 'https://jsonplaceholder.typicode.com/todos'
        IMPORTING client = lo_client ).

      lo_client->request->set_method(
        if_http_request=>co_request_method_post ).

      lo_client->request->set_header_field(
        name  = 'Content-Type' value = 'application/json' ).
      lo_client->request->set_header_field(
        name  = 'Accept'       value = 'application/json' ).

      lo_client->request->set_cdata( lv_payload ).

      lo_client->send(
        EXCEPTIONS http_communication_failure = 1 OTHERS = 2 ).
      IF sy-subrc <> 0. RAISE EXCEPTION TYPE cx_sy_no_handler. ENDIF.

      lo_client->receive(
        EXCEPTIONS
          http_communication_failure = 1
          http_processing_failed     = 2
          OTHERS                     = 3 ).
      IF sy-subrc <> 0. RAISE EXCEPTION TYPE cx_sy_no_handler. ENDIF.

      lo_client->response->get_status(
        IMPORTING code = lv_http_code reason = lv_reason ).

      lv_body = lo_client->response->get_cdata( ).

      WRITE: / |Status: { lv_http_code } { lv_reason }|.
      IF lv_http_code = 201.
        WRITE: / 'Resource created successfully.'.
      ENDIF.
      WRITE: / lv_body.

    CATCH cx_root INTO DATA(lx).
      WRITE: / 'Error:', lx->get_text( ).
    CLEANUP.
      IF lo_client IS BOUND.
        lo_client->close( ).
      ENDIF.
  ENDTRY.

Troubleshooting

Symptom Likely cause Fix
SSSLERR_PEER_CERT_UNTRUSTED / SSL handshake error Root CA certificate not imported Import the server's root CA in STRUST → SSL client (Anonymous); restart ICM if needed
HTTP 401 Unauthorized Missing or wrong Authorization header Add Authorization: Bearer <token> or Basic <base64> header; encode user:pass with SCMS_BASE64_ENCODE_STR
http_communication_failure on send( ) Network unreachable, wrong host/port, or firewall block Check SM59 destination settings; verify the SAP app server can reach the host (ask basis for network trace)
Empty get_cdata( ) for binary response Response body is binary (e.g. a PDF or image) Use lo_client->response->get_data( ) which returns XSTRING instead of STRING
Connection refused Wrong port or service not running on target Verify port in SM59; test with PING or telnet from the OS level
Response body truncated Default response buffer too small Call lo_client->response->set_cdata_size( 1048576 ) before receive( ) to increase the buffer

Base64 encoding for Basic Auth

DATA: lv_plain  TYPE string,
      lv_b64    TYPE string.
lv_plain = 'myuser:mypassword'.
CALL FUNCTION 'SCMS_BASE64_ENCODE_STR'
  EXPORTING input  = lv_plain
  IMPORTING output = lv_b64.
lo_client->request->set_header_field(
  name  = 'Authorization'
  value = |Basic { lv_b64 }| ).

See also

  • CL_HTTP_CLIENT — full class reference and advanced options
  • Transaction SM59 — manage RFC/HTTP destinations and test connectivity
  • Transaction STRUST — import SSL certificates for HTTPS connections
  • Transaction SMICM — ICM monitor; restart required after STRUST changes on some systems

Comments