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_JSONavailable (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.
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
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