Introduction
Recently I encountered the following situation in Business Central with Web Service that uses Pagination with Continuation Tokens:
–> When you get data from Web Service and it returns a lot of results, the data is paginated.
–> One method to handle pagination is using Continuation Tokens.
–> For example, look at the below response, it contains a data array with customer elements and after that, information about the Continuation Token:

–> This means that you first need to create a request to the API Endpoint and then check if there is a Continuation Token or not. If there is one, you need to make the same request again but include the Continuation Token in the query string in order to get the rest of the data.. And so on, until the last response will not contain a Continuation Token anymore.
–> Example of request URL with Continuation Token:
https://apitest.azure-api.net/int/customer?&continuationtoken=eyJ0b2tlbiI6IitSSUQ6fjBKNXhBTkQxdedewfrwfrwfreresibWluIjoiIiwibWF4IjoiRkYifX0=
Coding Pagination with Continuation Tokens in Business Central
–> I think it is obvious that Recursion is the solution for implementing the Pagination with Continuation Tokens. Let’s see my idea about how to accomplish this in D365 Business Central AL language.
–> You can see below the whole logic of calling recursively the function to retrieve Customer data until the last page of the Web Service response:
procedure GetCustomer()
begin
GetCustomerRecursive('', '');
end;
local procedure GetCustomerRecursive(ContinuationToken: Text; PreviousContinuationToken: Text)
var
Token: Text;
HttpClient: HttpClient;
ReqHeaders: HttpHeaders;
RequestHeader: HttpHeaders;
Content: HttpContent;
ResponseMessage: HttpResponseMessage;
JObject: JsonObject;
JToken: JsonToken;
JArray: JsonArray;
RString: Text;
i: Integer;
TestCustStag: Record "Test Customer Staging";
EntryNo: Integer;
LineNo: Integer;
DateTimeDotNetUTC: DotNet DateTime;
ContinuationTokenMod: Text;
GetUrl: Text;
begin
Token := GetOauthToken();
GetAPISetup();
TestAPISetup.TestField("Customer - INT URL");
TestAPISetup.TestField("Ocp-Apim-Subscription-Key Cust");
TestAPISetup.TestField("If-Modified-Since Cust");
PreviousContinuationToken := ContinuationToken;
IF ContinuationToken = '' THEN begin
HttpClient.SetBaseAddress(TestAPISetup."Customer - INT URL");
GetUrl := TestAPISetup."Customer - INT URL";
end Else begin
ContinuationTokenMod := ReplaceString(ContinuationToken, '<originalUrl>', '');
GetUrl := TestAPISetup."Customer - INT URL" + '?' + ContinuationTokenMod;
end;
ReqHeaders := HttpClient.DefaultRequestHeaders;
ReqHeaders.Add('Authorization', Token);
ReqHeaders.Add('Ocp-Apim-Subscription-Key', TestAPISetup."Ocp-Apim-Subscription-Key Cust");
ReqHeaders.Add('If-Modified-Since', TestAPISetup."If-Modified-Since Cust");
DateTimeDotNetUTC := DateTImeDotNetUTC.UtcNow;
HttpClient.Get(GetUrl, ResponseMessage);
If not ResponseMessage.IsSuccessStatusCode() then
Error('Web service returned error:\\' +
'Status code: %1\' +
'Description: %2',
ResponseMessage.HttpStatusCode(),
ResponseMessage.ReasonPhrase());
ResponseMessage.Content().ReadAs(RString);
JToken.ReadFrom(RString);
JObject := JToken.AsObject();
ContinuationToken := SelectJsonToken(JObject, '$.links.next'); // !!!
JObject.SelectToken('data', JToken);
JArray := JToken.AsArray();
EntryNo := GetTestCustStagingNextEntryNo;
For i := 0 to JArray.Count - 1 do begin
LineNo += 1000;
TestCustStag.Init();
TestCustStag."Entry No" := EntryNo;
TestCustStag."Line No." := LineNo;
JArray.Get(i, JToken);
HandleArrayCustomer(JToken, TestCustStag); //get data from each array element
TestCustStag."Import Date Time" := CurrentDateTime;
TestCustStag.Insert(TRUE);
end;
IF (ContinuationToken <> '') AND (ContinuationToken <> PreviousContinuationToken) then
GetCustomerRecursive(ContinuationToken, PreviousContinuationToken); //for pagination...
//do if no recursion occured OR only for the first call in case recursion occured!
IF ((ContinuationToken = '') AND (PreviousContinuationToken = '')) OR ((ContinuationToken <> '') AND (PreviousContinuationToken = '')) THEN BEGIN
UpdateIfModifiedSinceCustTestSetup(DateTimeDotNetUTC);
END;
end;
–> The Web Service used in this example returns JSON data. I leave here also the function “HandleArrayCustomer” which reads different data from a complicated JSON Response Array element.. I think this is a good example of how to handle any type of Json File in D365 Business Central:
local procedure HandleArrayCustomer(JsonToken: JsonToken; var TestCustStag: Record "Test Customer Staging")
var
JsonObject: JsonObject;
JsonObjectCustomer: JsonObject;
JTokenCustomer: JsonToken;
JTokenAffiliationArr: JsonToken;
Txt: Text;
JArrayAffiliation: JsonArray;
JTokenAffiliationElement: JsonToken;
i: Integer;
j: integer;
JObjectAffiliationElement: JsonObject;
AffiliationType: text;
JTokenEntityInfo: JsonToken;
JArrayEntityInfo: JsonArray;
JObjectEntityInfo: JsonObject;
PartyType: Text;
JTokenOrganization: JsonToken;
JArrayOrganization: JsonArray;
JObjectOrganization: JsonObject;
LegalEntity: Text;
JTokenCustExtension: JsonToken;
JObjectCustExtension: JsonObject;
JTokenPaymentTerms: JsonToken;
JTokenPaymentMethod: JsonToken;
begin
JsonObject := JsonToken.AsObject();
IF Evaluate(TestCustStag."Response Data Id", SelectJsonToken(JsonObject, '$.id')) THEN;
JsonObject.SelectToken('$.Customers.Customer[0]', JTokenCustomer);
JsonObjectCustomer := JTokenCustomer.AsObject();
JsonObjectCustomer.SelectToken('$.Affiliation', JTokenAffiliationArr);
JArrayAffiliation := JTokenAffiliationArr.AsArray();
For i := 0 to JArrayAffiliation.Count - 1 do begin
JArrayAffiliation.Get(i, JTokenAffiliationElement);
JObjectAffiliationElement := JTokenAffiliationElement.AsObject();
AffiliationType := SelectJsonToken(JObjectAffiliationElement, '$.AffiliationType[0]');
IF AffiliationType = 'CustomerNumber' then
TestCustStag.CustomerNumber_PartyID := SelectJsonToken(JObjectAffiliationElement, '$.PartyID');
IF AffiliationType = 'PayingCustomer' then
TestCustStag.PayingCustomer_PartyID := SelectJsonToken(JObjectAffiliationElement, '$.PartyID');
end;
JsonObjectCustomer.SelectToken('$.EntityInformation', JTokenEntityInfo);
JArrayEntityInfo := JTokenEntityInfo.AsArray();
For i := 0 to JArrayEntityInfo.Count - 1 Do begin
JArrayEntityInfo.Get(i, JTokenEntityInfo);
JObjectEntityInfo := JTokenEntityInfo.AsObject();
PartyType := SelectJsonToken(JObjectEntityInfo, '$.PartyType');
IF PartyType = 'Organization' THEN begin
JObjectEntityInfo.SelectToken('$.Organization', JTokenOrganization);
JArrayOrganization := JTokenOrganization.AsArray();
for j := 0 to JArrayOrganization.Count - 1 Do begin
JArrayOrganization.Get(j, JTokenOrganization);
JObjectOrganization := JTokenOrganization.AsObject();
LegalEntity := SelectJsonToken(JObjectOrganization, '$.Classification.LegalEntity');
If LegalEntity <> 'Juridisk Eier' Then begin
TestCustStag."Customer Name" := SelectJsonToken(JObjectOrganization, '$.Name[0]');
TestCustStag.TaxPayerID := SelectJsonToken(JObjectOrganization, '$.TaxpayerID');
TestCustStag.LegalEntity := LegalEntity;
end;
IF LegalEntity = 'Juridisk Eier' then
TestCustStag."Customer Name 2" := SelectJsonToken(JObjectOrganization, '$.Name[0]');
end;
end;
end;
JsonObjectCustomer.SelectToken('$.NGARTSCustomerCustomerExtension[0]', JTokenCustExtension);
JObjectCustExtension := JTokenCustExtension.AsObject();
JObjectCustExtension.SelectToken('$.PaymentTerm', JTokenPaymentTerms);
TestCustStag."Payment Term Code" := SelectJsonToken(JTokenPaymentTerms.AsObject(), '$.Code');
TestCustStag."Payment Term Name" := SelectJsonToken(JTokenPaymentTerms.AsObject(), '$.Name');
JObjectCustExtension.SelectToken('$.PaymentMethod', JTokenPaymentMethod);
TestCustStag."Payment Method Code" := SelectJsonToken(JTokenPaymentMethod.AsObject(), '$.Code');
TestCustStag."Payment Method Name" := SelectJsonToken(JTokenPaymentMethod.AsObject(), '$.Name');
IF Evaluate(TestCustStag.CreditLimit, SelectJsonToken(JObjectCustExtension, '$.CreditLimit')) THEN;
end;
local procedure UpdateIfModifiedSinceCustTestSetup(var DotNetUTCTime: DotNet DateTime)
var
begin
TestAPISetup."If-Modified-Since Cust" := DotNetUTCTime.ToString('r');
TestAPISetup.Modify();
end;
–> And Function “SelectToken”:
local procedure SelectJsonToken(JsonObject: JsonObject; Path: text): Text;
var
JsonToken: JsonToken;
begin
if not JsonObject.SelectToken(Path, JsonToken) then
exit('');
if JsonToken.AsValue.IsNull then
exit('');
exit(jsontoken.asvalue.astext);
end;
–> This is, I think, almost all the “arsenal” you need to know in order to work with Json Data in Business Central.
I hope this helps.





