SQL Server 2025 REST API Example

Problem

You have an online store that uses SQL Server. One of the things that is often required is to determine shipping costs. In this article, we will look at how you can use an API in SQL Server to pass a few parameter values from SQL Server and return data from the API.

Solution

Microsoft has introduced native REST API endpoints with SQL Server 2025; this enables you to interact with external APIs directly on your SQL Server.

There is a stored procedure named sp_invoke_external_rest_endpoint that invokes an HTTPS REST endpoint provided as an input argument to the procedure. We will use this procedure and others to automatically calculate rates and delivery ETA then store the data for consumption for your application-level logic.

Create Database and Tables

Assuming you have your SQL Server 2025 installed and running, open SSMS and connect to your database engine.

Then, click on the New Query button on the SSMS home ribbon and add the following code to add a create a database and table to simulate a sales orders table:

--MSSQLTIPS CODE
 
CREATE DATABASE SalesDB;
GO
USE SalesDB;
GO
 
CREATE TABLE dbo.SalesOrder 
( 
   OrderID INT IDENTITY PRIMARY KEY, 
   CustomerID INT NOT NULL, 
   FromPostalCode NVARCHAR(20) NOT NULL, 
   ToPostalCode NVARCHAR(20) NOT NULL, 
   WeightKg DECIMAL(10,2) NOT NULL, 
   ChosenService NVARCHAR(50) NOT NULL, -- e.g. STANDARD / EXPRESS 
   ShippingRate DECIMAL(10,2) NOT NULL, -- Best rate chosen 
   ETA_Days INT, 
   CreatedAt DATETIME NOT NULL DEFAULT SYSDATETIME(),
   CONSTRAINT FK_SalesOrder_Customer FOREIGN KEY (CustomerID) REFERENCES dbo.Customer(CustomerID)
);

Then, create another table to store the history of the shipping prices for future analysis:

CREATE TABLE dbo.ShippingRateHistory
(
   RateID INT IDENTITY PRIMARY KEY,
   OrderID INT NULL,                        -- Only linked if order is finalized
   RequestJson NVARCHAR(MAX) NOT NULL,
   ResponseJson NVARCHAR(MAX) NOT NULL,
   LoggedAt DATETIME NOT NULL DEFAULT SYSDATETIME()
);

If delivery for the all shipments will be from the same warehouse address, you can set this address value in your database:

CREATE TABLE dbo.WarehouseConfig
(
   WarehouseID INT IDENTITY PRIMARY KEY,
   WarehouseName NVARCHAR(100) NOT NULL,
   PostalCode NVARCHAR(20) NOT NULL,
   CountryCode CHAR(2) NOT NULL DEFAULT 'ZA',
   IsDefault BIT NOT NULL DEFAULT 1
);
 
INSERT INTO dbo.WarehouseConfig (WarehouseName, PostalCode)
VALUES ('Main Warehouse', '7441');

Enable RESTful API on SQL Server

You can check if your RESTful API feature is enabled by running this query:

SELECT name, value, value_in_use, description 
FROM sys.configurations 
WHERE name LIKE '%REST%';

This query will give you results of ‘external rest endpoint enabled’ being 0 if disabled or 1 if enabled.

If it’s not enabled, run the following query to enable it:

EXEC sp_configure 'external rest endpoint enabled', 1;
RECONFIGURE WITH OVERRIDE;

This should give you a result like this:

Configuration option 'external rest endpoint enabled' changed from 0 to 1. Run the RECONFIGURE statement to install.

Then, confirm its active by running this query:

EXEC sp_configure 'external rest endpoint enabled';

You should get the following results if it is running:

nameminimummaximumconfig_valuerun_value
external rest endpoint enabled0111

Calling sp_invoke_external_rest_endpoint

The procedure accepts the following arguments:

EXECUTE @returnValue = sp_invoke_external_rest_endpoint 
[ @url = ] N'url' 
[ , [ @payload = ] N'request_payload' ] 
[ , [ @headers = ] N'http_headers_as_json_array' ] 
[ , [ @method = ] 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' ] 
[ , [ @timeout = ] seconds ]
[ , [ @credential = ] credential ] 
[ , @response OUTPUT ] 
[ , [ @retry_count = ] 
# of retries if there are errors ]

Shipping Stored Procedure

This article uses the shiplogic api. We will create a SQL Server stored procedure that will send a request to the API and store the response as the OUTPUT variable.

First, you must create a database Master Key and then a database scoped credential to store your API key or any secret:

USE SalesDB;
GO
 
CREATE MASTER KEY ENCRYPTION BY PASSWORD = 'YourStrongPassword!2025';
GO 

And for the scoped credential:

CREATE DATABASE SCOPED CREDENTIAL ShipLogicAuth
WITH IDENTITY = 'HTTPEndpointHeaders',
SECRET = '{"Authorization":"Bearer <YourKey>"}';

Then, create the procedure to get the shipping rates for your application. As you can see in the code a bunch of items are hard coded but you can extend this further to pass more values to the stored procedure and then to the API.

USE [SalesDB]
GO 
 
CREATE OR ALTER PROCEDURE [dbo].[GetShipLogicRate] ( 
   @ToPostal NVARCHAR(20), 
   @WeightKg DECIMAL(10,2), 
   @ResponseJson NVARCHAR(MAX) OUTPUT ) 
AS 
BEGIN 
SET NOCOUNT ON;
 
-- Warehouse info
DECLARE @FromPostal NVARCHAR(20) = (
    SELECT TOP 1 PostalCode
    FROM dbo.WarehouseConfig
    WHERE IsDefault = 1
);
DECLARE @url NVARCHAR(4000) = 'https://api.shiplogic.com/v2/rates';
-- Headers
-- please make sure that there is not spaces on your headers
DECLARE @headers NVARCHAR(MAX) = N'{"Content-Type":"application/json","User-Agent":"SQLServer"}';
-- Payload
DECLARE @Payload NVARCHAR(MAX) = 
N'{
    "collection_address": {
        "type": "business",
        "company": "Shiplogic",
        "street_address": "194 Bancor Avenue",
        "local_area": "Menlyn",
        "city": "Pretoria",
        "zone": "Gauteng",
        "country": "ZA",
        "code": "' + @FromPostal + '"
    },
    "delivery_address": {
        "type": "residential",
        "company": "",
        "street_address": "10 Midas Avenue",
        "local_area": "",
        "city": "",
        "zone": "",
        "country": "ZA",
        "code": "' + @ToPostal + '"
    },
    "parcels": [
        {
            "submitted_length_cm": 40,
            "submitted_width_cm": 30,
            "submitted_height_cm": 10,
            "submitted_weight_kg": ' + CAST(@WeightKg AS NVARCHAR(10)) + '
        }
    ],
    "declared_value": 1500,
    "collection_min_date": "' + CONVERT(NVARCHAR(10), GETDATE(), 23) + '",
    "delivery_min_date": "' + CONVERT(NVARCHAR(10), GETDATE(), 23) + '"
}';

BEGIN TRY
        EXEC sp_invoke_external_rest_endpoint
            @url = @url,
            @method = 'POST',
            @headers = @headers,
            @payload = @payload,
            @credential = ShipLogicAuth,
            @response = @ResponseJson OUTPUT;
END TRY
BEGIN CATCH
        SELECT 
            ERROR_MESSAGE() AS ErrorMessage,
            ERROR_NUMBER() AS ErrorNumber,
            ERROR_LINE() AS ErrorLine,
            ERROR_PROCEDURE() AS ErrorProc;
END CATCH;
END;

Then test this stored procedure by running this code:

USE SalesDB;
GO
 
DECLARE @response NVARCHAR(MAX);
 
EXEC dbo.GetShipLogicRate @ToPostal = '2196', @WeightKg = 3.0, @ResponseJson = @response OUTPUT;
 
SELECT @response AS RawResponseJson;
 
SELECT TOP 1 CAST(
   JSON_VALUE(rate.value, '$.rate') AS DECIMAL(10,2)) AS CheapestRate, 
   JSON_VALUE(rate.value, '$.service_level.code') AS ServiceCode,  
   JSON_VALUE(rate.value, '$.service_level.name') AS ServiceName,  
   JSON_VALUE(rate.value, '$.service_level.description') AS ServiceDescription  
FROM OPENJSON(@response, '$.result.rates') AS rate 
ORDER BY CAST(JSON_VALUE(rate.value, '$.rate') AS DECIMAL(10,2));

Sales Order Stored Procedure to Store Rates Automatically

After you have successfully ran the procedure above, you can now use it to automate the process of getting the shipping rates and saving them in your database table using the following SQL Server stored procedure:

-- MSSQLTIPS CODE
USE SalesDB; 
GO
CREATE OR ALTER PROCEDURE [dbo].[CreateSalesOrder] ( 
   @CustomerID INT , 
   @ToPostal NVARCHAR(20) , 
   @WeightKg DECIMAL(10,2) ) 
AS 
BEGIN 
 
SET NOCOUNT ON;
 
DECLARE @json NVARCHAR(MAX);
 
-- Get default warehouse origin postal
DECLARE @FromPostal NVARCHAR(20) = (
    SELECT TOP 1 PostalCode 
    FROM dbo.WarehouseConfig 
    WHERE IsDefault = 1
);
 
-- Call ShipLogic Rate Procedure
EXEC dbo.GetShipLogicRate  
      @ToPostal = @ToPostal,
      @WeightKg = @WeightKg,
      @ResponseJson = @json OUTPUT; 
 
;WITH RateOptions AS (
    SELECT
        JSON_VALUE(value, '$.service_level.name') AS ServiceName,
        CAST(JSON_VALUE(value, '$.rate') AS DECIMAL(10,2)) AS Price,
        -- ETA calculation using delivery_date_to datetime
        DATEDIFF(DAY, CAST(GETDATE() AS DATE), 
        TRY_CONVERT(DATE, JSON_VALUE(value, '$.service_level.delivery_date_to')) ) AS ETA
    FROM OPENJSON(@json, '$.result.rates')
)
SELECT TOP 1 *
INTO #BestRate
FROM RateOptions
WHERE Price IS NOT NULL  -- avoid nulls breaking ordering
ORDER BY Price ASC;
-- Insert into SalesOrder
INSERT INTO dbo.SalesOrder (CustomerID, FromPostalCode, ToPostalCode, WeightKg, ChosenService, ShippingRate, ETA_Days)
SELECT
    @CustomerID,
    @FromPostal,
    @ToPostal,
    @WeightKg,
    ServiceName,
    Price,
    ETA
FROM #BestRate;
SELECT 
    SCOPE_IDENTITY() AS OrderID,
    ServiceName,
    Price,
    ETA
FROM #BestRate;
END;	 

Run an example call with the following script:

EXEC dbo.CreateSalesOrder 
      @CustomerID = 1,
      @ToPostal = '2196',
      @WeightKg = 2.5;

After running this, you should see a reply like this:

CreateSalesOrder Procedure results

Next Steps

2 Comments

  1. Just make a generic Customer table, I guess? FOREIGN KEY (CustomerID) REFERENCES dbo.Customer(CustomerID)

    • CREATE TABLE dbo.Customer (
      CustomerID INT PRIMARY KEY,
      FirstName VARCHAR(50),
      LastName VARCHAR(50),
      Email VARCHAR(100),
      Phone VARCHAR(20),
      Address VARCHAR(50)
      — Foreign key constraint would be added here
      );

Leave a Reply

Your email address will not be published. Required fields are marked *