Automating News Feed Integration with Sitecore XM Cloud Using Azure Functions and GraphQL

Santosh Kumar
6 min read2 days ago

--

Integrating external news feeds into Sitecore XM Cloud can be a powerful way to automate content publishing. This guide walks through the complete implementation of fetching news from an API and pushing it into Sitecore XM Cloud using Azure Functions, C#, GraphQL, and Azure Key Vault.

By the end of this article, you will have a fully functional solution that:

✅ Fetches news data from an external API (NewsAPI).
✅ Authenticates and interacts with Sitecore using the Management API.
✅ Uses GraphQL mutations to create and update Sitecore content.
✅ Secures API keys with Azure Key Vault.
✅ Runs on Azure Functions to automate execution at regular intervals.

1. Fetching News from an API

Use an external news feed API (e.g., NewsAPI) to fetch articles. Below is a sample C# method using HttpClient to retrieve data from News API and push into Sitecore XM Cloud:

 private static async Task FetchAndPushNewsAsync()
{
using var httpClient = new HttpClient();

try
{
var request = new HttpRequestMessage(HttpMethod.Get, NewsApiUrl);
request.Headers.Add("User-Agent", "news-feed-dotnet-app");

var response = await httpClient.SendAsync(request);

if (response.IsSuccessStatusCode)
{
_logger.LogInformation("News items fetched successfully.");
var jsonResponse = await response.Content.ReadAsStringAsync();
var newsData = JsonSerializer.Deserialize<Dictionary<string, object>>(jsonResponse);
var articles = newsData?["articles"] as JsonElement?;

var tasks = new List<Task>();

foreach (var article in articles?.EnumerateArray() ?? new JsonElement.ArrayEnumerator())
{
var newsItem = new Dictionary<string, string>
{
{ "title", article.GetProperty("title").GetString() ?? "Untitled" },
{ "author", article.GetProperty("author").GetString() ?? "author" },
{ "description", article.GetProperty("description").GetString() ?? "" },
{ "url", article.GetProperty("url").GetString() ?? "" },
{ "name", article.GetProperty("source").GetProperty("name").GetString() ?? $"news-{DateTime.Now:ddMMyyHHmmss}" }
};

tasks.Add(PushNewsToSitecore(newsItem));
}

await Task.WhenAll(tasks);
}
else
{
_logger.LogError("Failed to fetch news: {Error}", await response.Content.ReadAsStringAsync());
}
}
catch (Exception ex)
{
_logger.LogError("FetchAndPushNews Error: {Exception}", ex);
throw;
}
}

2. Authenticating with Sitecore XM Cloud

Getting an Access Token

Before interacting with Sitecore’s Management API, obtain an authentication token:

 private static async Task<string> GetSitecoreTokenAsync()
{
if (!string.IsNullOrEmpty(_sitecoreAccessToken) && DateTime.UtcNow < _tokenExpiryTime)
return _sitecoreAccessToken;

using var httpClient = new HttpClient();

var requestContent = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("grant_type", "client_credentials"),
new KeyValuePair<string, string>("client_id", SitecoreClientId),
new KeyValuePair<string, string>("client_secret", SitecoreClientSecret),
new KeyValuePair<string, string>("audience", "https://api.sitecorecloud.io"),
});

try
{
HttpResponseMessage response = await httpClient.PostAsync("https://auth.sitecorecloud.io/oauth/token", requestContent);

if (response.IsSuccessStatusCode)
{
var responseBody = await response.Content.ReadAsStringAsync();
var tokenResponse = JsonSerializer.Deserialize<Dictionary<string, object>>(responseBody);
_sitecoreAccessToken = tokenResponse?["access_token"]?.ToString() ?? string.Empty;
int expiresIn = Convert.ToInt32(tokenResponse?["expires_in"]?.ToString());
_tokenExpiryTime = DateTime.UtcNow.AddSeconds(expiresIn - 60);
}
else
{
_logger.LogError("Failed to acquire access token: {Error}", await response.Content.ReadAsStringAsync());
}

return _sitecoreAccessToken;
}
catch (Exception ex)
{
_logger.LogError("GetAccessToken Error: {Exception}", ex);
throw;
}
}

3. GraphQL Mutation to Push News into Sitecore

Use Sitecore’s GraphQL Management API to create a new content item:

private static async Task PushNewsToSitecore(Dictionary<string, string> newsItem)
{
const string templateId = "{YOUR-TEMPLETE-ID}";
const string parentId = "{YOUR-PARENT-ID}";
const string language = "en";

var mutationQuery = new
{
query = $@"
mutation {{
createItem(
input: {{
name: ""{newsItem["name"]}""
templateId: ""{templateId}""
parent: ""{parentId}""
language: ""{language}""
fields: [
{{ name: ""Title"", value: ""{newsItem["title"]}"" }}
{{ name: ""Author"", value: ""{newsItem["author"]}"" }}
{{ name: ""Description"", value: ""{newsItem["description"]}"" }}
{{ name: ""Url"", value: ""{newsItem["url"]}"" }}
]
}}
) {{
item {{
itemId
name
path
}}
}}
}}"
};

using var httpClient = new HttpClient();
var requestContent = new StringContent(JsonSerializer.Serialize(mutationQuery), Encoding.UTF8, "application/json");
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _sitecoreAccessToken);

try
{
var response = await httpClient.PostAsync(SitecoreApiUrl, requestContent);

if (response.IsSuccessStatusCode)
{
_logger.LogInformation("News item '{Title}' added to Sitecore.", newsItem["title"]);
}
else
{
_logger.LogError("Failed to add news item '{Title}': {Error}", newsItem["title"], await response.Content.ReadAsStringAsync());
}
}
catch (Exception ex)
{
_logger.LogError("PushNewsToSitecore Error: {Exception}", ex);
throw;
}
}

Main Function:

// Class Level Private Variables:
private static ILogger _logger;
private static string _sitecoreAccessToken;
private static DateTime _tokenExpiryTime = DateTime.MinValue;
private static string SitecoreClientId;
private static string SitecoreClientSecret;
private static string SitecoreApiUrl;
private static string NewsApiKey;

[Function("NewsfeedFunction")]
public async Task Run([TimerTrigger("0 */15 * * * *")] TimerInfo myTimer)
{
_logger.LogInformation("Newsfeed Timer trigger function executed at: {Time}", DateTime.Now);

try
{
if (!string.IsNullOrEmpty(await GetSitecoreTokenAsync()))
{
await FetchAndPushNewsAsync();
}

if (myTimer.ScheduleStatus is not null)
{
_logger.LogInformation("Next timer schedule at: {NextSchedule}", myTimer.ScheduleStatus.Next);
}
}
catch (Exception ex)
{
_logger.LogError("Run Error: {Exception}", ex);
}
}

4. Deploying to Azure Functions

Creating an Azure Function

  1. Open Visual Studio and create a new Azure Function project.
  2. Choose the Timer Trigger function type.
  3. Install required packages
  4. Use environment variables to store API URLs and Client Secret, which can also be managed securely using Azure Key Vault.
  5. Deploy the function using Azure CLI.
dotnet add package Microsoft.Extensions.Configuration
dotnet add package Azure.Identity
az functionapp publish <your-function-app-name>

5. Securing API Keys with Azure Key Vault

Storing Secrets — Steps to Use Azure Key Vault:

Save API keys and Client Secret etc. securely in Azure Key Vault instead of hardcoding them:

Create an Azure Key Vault and store:

  • SitecoreClientId
  • SitecoreClientSecret
  • NewsApiUrl
  • SitecoreApiUrl

Enable Managed Identity for the Azure Function

  • In Azure Portal, navigate to your Azure Function App.
  • Go to Identity (under Settings).
  • Turn on System Assigned Identity → Click Save.
  • Copy the Object ID.
  • Assign Key Vault access using:
az keyvault set-policy --name MyKeyVault --object-id <function-object-id> --secret-permissions get list
  • Retrieve Secrets from Azure Key Vault in the Function
  • Use the secrets securely in the API requests

Store Secrets in Azure Key Vault

az keyvault create --name MyKeyVault --resource-group MyResourceGroup --location eastus

az keyvault secret set --vault-name MyKeyVault --name "SitecoreClientId" --value "<your-client-id>"
az keyvault secret set --vault-name MyKeyVault --name "SitecoreClientSecret" --value "<your-client-secret>"
az keyvault secret set --vault-name MyKeyVault --name "NewsApiUrl" --value "<your-news-api-url-with-key>"
az keyvault secret set --vault-name MyKeyVault --name "SitecoreApiUrl" --value "<your-sitecore-api-url>"

Retrieve Secrets from Key Vault

private static async Task LoadSecrets()
{
string keyVaultUrl = Environment.GetEnvironmentVariable("AZURE_KEY_VAULT_URL");
var keyVaultClient = new SecretClient(new Uri(keyVaultUrl), new DefaultAzureCredential());

SitecoreClientId = (await keyVaultClient.GetSecretAsync("SitecoreClientId")).Value.Value;
SitecoreClientSecret = (await keyVaultClient.GetSecretAsync("SitecoreClientSecret")).Value.Value;
SitecoreApiUrl = (await keyVaultClient.GetSecretAsync("SitecoreApiUrl")).Value.Value;
NewsApiUrl = (await keyVaultClient.GetSecretAsync("NewsApiUrl")).Value.Value;
}

6. Handling Common Errors

🔹 Expired Token Handling

Refresh the Sitecore token only if expired:

if (DateTime.UtcNow >= tokenExpiryTime)
{
await RefreshSitecoreToken();
}

🔹 Debugging API Errors

Log API responses for better debugging:

string responseBody = await response.Content.ReadAsStringAsync();
Console.WriteLine($"Response: {responseBody}");

🔹 Schema is not configured for mutations

Set Sitecore_GraphQL_ExposePlayground with a true value in XM Cloud and restart your environment.

Test in your GraphQL Playground; mutationType will appear as 'Mutation' instead of null once everything is set up correctly.

If Application Insights is enabled for your Azure Function, you can view the logs as shown below:

Additionally, the same news should be pushed to your XM Cloud site in the specified folder.

Conclusion

By following this guide, you have successfully built an automated pipeline to fetch news articles and push them into Sitecore XM Cloud using Azure Functions, C#, GraphQL, and Azure Key Vault.

🚀 Key Takeaways

✅ Fetching and processing news articles dynamically.
✅ Authenticating with Sitecore and refreshing tokens when expired.
✅ Using GraphQL mutations to create Sitecore content.
✅ Deploying an automated solution via Azure Functions.
✅ Enhancing security with Azure Key Vault.

This approach provides a scalable, serverless, and secure way to manage dynamic content in Sitecore XM Cloud. 🎯🔥

You can find my other blogs here.

References:

Create an automation client for the XM Cloud organization.
Query examples for authoring operations.
How to store function apps keys in a key vault.

For a similar implementation using Logic Apps to automate news feeds, you can refer to this article.

Thank you!

--

--

Santosh Kumar
Santosh Kumar

Written by Santosh Kumar

Software Architect | Sitecore MVP | Expert in Sitecore XM Cloud, Content Hub, .NET Core, Azure, AWS, Gen AI & MACH Architecture.

No responses yet