API call Demo - Python
A Python API call demo is provided as a hands-on example of how to use the REST APIs of SwiftFederation solutions.
For reference, download the API call demo source code.
""" API Call Demo is used to demonstrate how to use the Open APIs of SwiftFederation for the access to the SwiftFederation solutions.
You could run this python code to make the API Call to get the customer information of "Customer_Demo".
> python api_call_demo.py
The customer information will be shown as below after a successful response is received:
{
'partnership': None,
'name': 'Customer_Demo',
'ingestDomain': '',
'industry': 'Cloud Service Provider',
'id': 35394,
'type': 2,
'region': 'SG',
'parentId': 2,
'onboardingType': 1
}
For a sole demo purpose, SwiftFederation platform had provided a demo customer (Customer ID: 35394) with a live Access Key to
illustrate making the API Call.
Please don't use this Access Key and demo customer information in any other cases with any improper intents.
For details on a specific REST API, please refer to the documentation at https://docs.swiftfederation.com
Copyright@2025 Conversant Solutions Pte Ltd.
"""
#coding=utf-8
import hashlib
import hmac
import json
import traceback
from datetime import datetime
from datetime import timezone
from random import randint
import requests
def debug(message):
"""Print out for debugging
Args:
message (string): message to be printed out
"""
if True:
print("[ " + datetime.now().strftime('%Y%m%d %H:%M:%S') + " ]: ====================>\n", message)
def print_request(request):
"""Print out the request.
Args:
request (requests.Request): requst object
"""
print("================== Request =====================")
print(request.method + " " + request.path_url + " HTTP/1.1")
print("\r\n".join(f"{key}: {value}" for key, value in request.headers.items()))
print("")
print(request.body)
def print_response(response):
"""Print out the response
Args:
response (requests.Response): response object
"""
print("==================== Response ===================")
print("HTTP/1.1 {} {}".format(response.status_code, response.reason))
print("\r\n".join(f"{key}: {value}" for key, value in response.headers.items()))
print("")
print(response.text)
def generate_signature(access_key_id, access_key_secret, signature_version, method, host, request_uri, custom_headers, request_body):
"""Sign the canonical string to generate the signature with the Access key.
Args:
access_key_id (string): Access Key ID
access_key_secret (string): Access Key Secret
signature_version (int): version of the signature, 1 or 2
method (string): HTTP method of the reuqest, "GET", "POST", "DELETE", "HEAD", "PUT" and "PATCH"
request_uri (string): the uri to identify the API with Query string in the format of absolute URI or absolute path
custom_headers (string): custom header as Header Parameters. The header names starting with "X-SFD-" will be used to generate signature
request_body (string): the Request Body in JSON format
Returns:
signature (string): signature in HEX format
canonical_headers (dict): canonical headers used for signature
"""
# Take the signature version as 2 except an explicit indication of version 1
if signature_version != 1:
signature_version = 2
# Get the Request-URI in the format of absolute path
path_url = request_uri if request_uri != None else "/"
if ( not path_url.lower().startswith("/") ):
path_url = '/' + path_url.split('/', 3)[3]
# generate the canonical headers
canonical_headers = {}
# add the Common Headers with header names starting with "X-SFD-"
canonical_headers["X-SFD-Date"] = datetime.now(timezone.utc).strftime('%Y%m%dT%H%M%SZ')
canonical_headers["X-SFD-Nonce"] = str(randint(10000, 99999))
if (signature_version == 2):
# For signature v2, canonical headers include Host, X-SFD-Date, X-SFD-Nonce, X-SFD-Signature-Version, custom headers starting with "X-SFD-"
canonical_headers["Host"] = host
canonical_headers["X-SFD-Signature-Version"] = str(signature_version)
# add the Custom headers with header names starting with "X-SFD-"
if (custom_headers != None):
for header_name in custom_headers:
if (header_name.lower().startswith("x-sfd-")):
canonical_headers[header_name] = custom_headers[header_name]
# Concatenate all the canonical_headers after sorting by header names
canonical_headers_str = ""
for header_name in sorted(canonical_headers.keys()):
canonical_headers_str += "{}:{}\n".format(header_name.lower(), canonical_headers[header_name])
# Concatenate to generate the canonical string to be signed
if (method == None):
method = "GET"
# Signature will sign with: method, abs_path, canonical headers (v1 or v2) in order, Access Key ID, Request Body
canonical_str_to_be_signed = "{}\n{}\n{}{}\n{}".format(method.upper(), path_url, canonical_headers_str, access_key_id, request_body)
# Convert to HEX format after signing the Canonical str with the algorithm of HMAC-SHA256
signature = hmac.new(access_key_secret.encode('utf-8'), canonical_str_to_be_signed.encode('utf-8'), digestmod=hashlib.sha256).hexdigest()
return signature, canonical_headers
def fire_api_call(access_key_id, access_key_secret, signature_version, method, host, request_uri, custom_headers, request_body):
"""Fire an API Call with the parameters required to the API Gateway.
Args:
access_key_id (string): Access Key ID
access_key_secret (string): Access Key Secret
signature_version (int): version of the signature, 1 or 2
method (string): HTTP method of the reuqest, "GET", "POST", "DELETE", "HEAD", "PUT" and "PATCH"
request_uri (string): the uri to identify the API with Query string in the format of absolute URI or absolute path
custom_headers (string): custom header as Header Parameters. The header names starting with "X-SFD-" will be used to generate signature
request_body (string): the Request Body in JSON format
Returns:
status_code: HTTP status code
data: data in JSON format, including error code and error message
"""
# Generate the signature
signature, canonical_headers = generate_signature(access_key_id, access_key_secret, signature_version, method, host, request_uri, custom_headers, request_body)
# Construct the HTTP headers
request_headers = {}
request_headers["Host"] = host
request_headers["Content-Type"] = "application/json; charset=utf-8"
request_headers["Authorization"] = "HMAC-SHA256 {}:{}".format(access_key_id, signature)
request_headers.update(custom_headers)
request_headers.update(canonical_headers)
try:
# print out the request
print_request(requests.Request(method, request_uri, data=request_body, headers=request_headers).prepare())
rs = None
match method.upper():
case "POST":
rs = requests.post(request_uri, data=request_body, headers=request_headers)
case "DELETE":
rs = requests.delete(request_uri, data=request_body, headers=request_headers)
case "PUT":
rs = requests.put(request_uri, data=request_body, headers=request_headers)
case "HEAD":
rs = requests.head(request_uri, data=request_body, headers=request_headers)
case "PATCH":
rs = requests.patch(request_uri, data=request_body, headers=request_headers)
case _:
rs = requests.get(request_uri, data=request_body, headers=request_headers)
# print out the response
print_response(rs)
# Get status code
status_code = rs.status_code
# Get the Trace ID
trace_id = rs.headers["x-sfd-trace-id"]
# get error code and error message
data = json.loads(rs.text)
return status_code, trace_id, data
except requests.RequestException as e:
raise e
# Settings for API Call Demo
# API gateway
api_gateway = "open-api.swiftfederation.com"
# Path-URL to get the customer information
path_url = "/v1.1/customer/{customer_id}"
# Acces key is used as the credentials to sign the API request for authentication.
# Please log into SwiftFederation Portal with your account and password to obtain your Access Key.
# For a demo purpose, the key pair of a live demo account from SwiftFederation Portal will be used.
# Please don't use this Access Key and demo customer information in any other cases with any improper intents.
access_key_id = "O80ybSq26xUE383u"
access_key_secret = "q738531SV3s0yFC2I3p7QJ49og37yIat"
# Fire the API Call
try:
# Prepare the Path parameters
customer_id = "35394"
path_url = path_url.format(customer_id=customer_id)
# Prepare the absolute Request-URI
request_uri = "https://" + api_gateway + path_url
# Assume we need to pass a custom header: X-SFD-FZone: SG
custom_headers = {"X-SFD-FZone" : "SG"}
# Fire the API Call
status_code, trace_id, customer_info = fire_api_call(access_key_id, access_key_secret, 2, "GET", api_gateway, request_uri, custom_headers, "")
if status_code == 200:
debug(customer_info)
else:
debug("Error Code: {}".format(customer_info["code"]))
debug("Error message: {}".format((customer_info["message"])))
except Exception as e:
debug(e)