The standard way to get data into an SAP SAC Model is to set up the data extraction to pull the data from outside sources. If you want to control the data injection to push the data into an SAC Model, then you can use SAC as an endpoint. To achieve this you will need to do some settings in SAC and then use the API created by SAC to post your data. When the prerequisites were set up, you can use any language to post the data into SAC. Here we will use Python.

Helpful Links

OAuth2.0 and creating OAuth-Client

The SAC API needs OAuth2.0 authentication to allow any data download or upload/modification. For that, we need to create an OAuth client in SAC. In the left menu, click the “System” button and choose the “Administration” option. In the new menu, choose “App-Integration” to open the relevant window.

Under “Configured Clients” click on “Add new OAuth-Client” to create a new OAuth-Client.

Give it an appropriate name and choose “API-Access” as the purpose. “Interactive Usage” cannot be used in this case, as it requires active user intervention to allow authentication.

Under “Access” at least these 5 are required for our purposes (if other features are implemented others could be needed):  Data Import Service, Data Export Service, Resource permissions, Modelling, key values.

Under the “Safety” submenu, for “Authorisation” choose Client Login Data, and give the token an appropriate lifetime.

Now we have created an OAuth-Client in SAC. The window will show an OAuth-Client-ID and a Secret Key. Both are needed, please save them, as losing them means needing to create a new OAuth-Client.

Authentication and CSRF Token

With the OAuth-Client, we can authenticate our access to the API.

Authentication here works like this:

  1. Create a basic authentication with the OAuth-Client-ID as Username and the secret key as Password
  2. Make a GET request to the Token URL with this Authentication to receive an Access Token, which will authenticate all following requests to the normal Tenant URL
  3. Make a GET request to the Tenant URL to fetch “x-csrf-token” and save cookies

In the “App-Integration” tab from before you can find several links. We need the Token-URL.

In our code we save the OAuth-Client-ID, Secret Key and Token-URL (also save the Tenant URL for later).

Here we use the Python requests library, you can install it with „pip install“ requests.

Create a Session like in line 10 to automatically save cookies, we need the cookies of each request to authenticate the following requests. Then create a HTTPBasicAuth object (from requests.auth import HTTPBasicAuth) with OAuth-Client-ID as username and secret key as password. Finally, make the get request but do not use the session object yet, as the cookies from the Token URL are not relevant. If grant type is not specified you will get the error „b'{„error“:“invalid_request“,“error_description“:“Missing grant type“}'“, see this documentation.

This request will return a response with the Access Token. We read this Access Token from the resulting json and save it. Then we need to edit three headers in the previously created Session:

“Authorization” needs to be set to: “Bearer “ +  <access_token>
(Do not forget the space after “Bearer”)

“x-sap-sac-custom-auth” is set to true

“x-csrf-token” is set to “fetch”

We then make a GET request to the normal Tenant URL with these headers set. Any request will do, as this request is only to fetch the x-csrf-token. Don’t forget to now use the Session for our requests, as the cookies are needed for further authentication!

The CSRF Token is saved and has to be manually added into the Session headers, as headers are not automatically saved. The other headers are not changed, they remain the same!

Now we have authenticated our program and determined the CSRF Token. We can now make any CRUD Request.

Read Model Data

With the previous preparations we can read model data. The documentation on this page has more information on details, here is only one example.

To read data from a model the model ID is needed, which can be determined from the URL of the Model in SAC. You could also make a request to get all models and find your model there. It should look like this: …/m/model/[Model ID].

Now you can make the GET request with the model ID and get any info about the model. Here we get the metadata of the model (columns, data types, etc.)

Write(Upload) Fact Data

With the CSRF Token we have access to writing data to a model. For that we use the import interface.

For our example we created a model with 5 columns: Version, Date, CompanyCode, Country and QuantitySold. The data looks like this in a csv-file:

We read the csv-file and update the Session headers to include content-type with the value text-csv.

With the Session we make a POST request where we include the data from the csv-file.

Now the data is uploaded to the model.

The model data will be refreshed

Common Errors

If the error “Temporary storage does not contain valid records. Every record sent is incorrect. Please review all records and retry. [1011]” occurs, at least one column has wrong data in every row.

Possible errors:

  • Wrong data type for a column
  • Value of dimension outside of normal range (e.g. Date in Model only defined from 2024 to 2025, but input date starts at 2023)

Python Code

import requests
from requests.auth import HTTPBasicAuth
import json as js

secret_key = "[add key]"
oauthclientid = "[add id]"
token_url = "https://[URL]/oauth/token?grant_type=client_credentials"
tenant_url = "https://[URL]/"

s = requests.Session()
basicAuth = HTTPBasicAuth(oauthclientid, secret_key)
response = requests.get(token_url, auth=basicAuth)

access_token = response.json()["access_token"]
print(access_token)
s.headers.update({'Authorization': 'Bearer ' + access_token})
s.headers.update({'x-sap-sac-custom-auth': 'true'})
s.headers.update({'x-csrf-token': 'fetch'})

response = s.get(tenant_url + "api/v1/dataimport/models")
csrf_token = response.headers.get("x-csrf-token")
s.headers.update({'x-csrf-token': csrf_token})
print(csrf_token)

#response = s.get(tenant_url + "api/v1/dataimport/models")

modelID = "C4lkdv035jj15efjhr8fh60or7d"
response = s.get(tenant_url + "api/v1/dataimport/models/"+modelID + "/metadata")

csvFile = open("test.csv","r")
csvContent = csvFile.read()
csvFile.close()
s.headers.update({'content-type':'text/csv'})

response = s.post(tenant_url + "api/v1/dataimport/import/"+modelID+"/factData", data=csvContent)
print(response.text)

Download Data:

https://dimitrirybak.de/wp-content/uploads/2024/10/Code_SAC_POST.zip

Special Thanks for creating this knowledge goes to Cüneyd Tas